ssung_인턴일지

10일차_Image stitching

ssungcohol 2024. 7. 14. 15:32

Image Stitching

이미지 스티칭이란?

  • 간단하게 말하면 '여러 장의 연관된 이미지를 하나의 이미지 또는 파노라마 이미지를 만들기 위한 작업'

어떻게 실행하는가?

  • 이미지 스티칭을 위한 (촬영한) 이미지를 정렬
  • SIFT (Scale-Invariant Feature Transform)을 사용해 각 이미지들의 특징을 출력
    -> 여기서 추출된 특징은 이미지를 자연스럽게 연결하는데 사용!
    -> 기준이 되는 이미지에서 다른 모든 이미지를 기준이 되는 이미지에 맞게 좌표 프레임으로 변환
  • 이 때 중요한 점은 이미지를 연결할 때 생기는 이미지 사이의 이음선 즉, seames

코드

import cv2 as cv
import numpy as np
from skimage.metrics import structural_similarity as ssim
from skimage.metrics import mean_squared_error, peak_signal_noise_ratio
import imutils
from pathlib import Path

class ImageQualityEvaluator : 
	def __init__(self, directory, output_dir, output_name, crop) :
        self.directory = directory
        self.output_dir = output_dir
        self.out_name = out_name
        self.crop = crop

	def preprocess_image(self, image) :
    	# 조명 보정
        lab = cv.cvtColor(image, cv.COLOR_BGR2LAB)
        l, a, b = cv.split(lab)
        clahe = cv.createCLAHE(clipLimit = 3.0, tileGridSize = (8,8))
        cl = clahe.apply(l)
        limg = cv.merge((cl, a, b))
        image = cv.cvtColor(limg, cv.COLOR_LAB2BGR)
        
        # 노이즈 제거 (Gaussian 블러)
        image = cv.GaussanBlur(image, (3, 3), 0)
        
        if min(image.shape[:2]) > 3 :
        	image = cv.GaussianBlur(image, (3, 3), 0)
        else : 
        	print("[INFO] Image is too small for GaussianBlur, skipping noise removal.")
            
        return image
        
    def stitch_images(self, images) :
    	print("[INFO] Stitching images...")
        stitcher = cv.createStitcher() if imutils.is_cv3() else cv.Stitcher_create()
        status, stitched = stitcher.stitch(images)
        return status, stitched
        
    def evaluate_quality(self) :
    	output_dir = self.output_dir + "/" + self.out_name
    	print(f"Output directory : {output_dir}")
        
        print("[INFO] Loding images...")
        image_paths = sorted(list(Path(self.directory).glob('*')))
        images = []
        
        for images_path in image_paths :
        	image = cv.imread(str(image_path))
            image = self.preprocess_image(image)
            images.append(image)
            
        print(f"[INFO] Total {len(images)} images loaded.")
        
        status, stitched = self.stitch_images(images)
        
        if status == 0 :
        	if self.crop == "YES" :
            	print("[INFO] Cropping image...")
                stitched = cv.copyMakeBorder(stitched, 10, 10, 10, 10, cv.BORDER_CONSTANT, (0, 0, 0))
                
                gray = cv.cvtColor(stitched, cv.COLOR_BGR2GRAY)
                thresh = cv.threshold(gray, 0, 255, cv.THRESH_BINATY)[1]
                
                cnts = cv.findContours(thresh.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
                cnts = imutils.grab_contours(cnts)
                c = max(cnts, key = cv.contourArea)
                
                mask = np.zeros(thresh.shape, dtype = "uint8")
                (x, y, w, h) = cv.boundingRect(c)
                cv.rectangle(mask, (x, y), (x + w, y + h), 255, -1)
                min_rect = mask.copy()
                sub = mask.copy()
                
                while cv.countNonZero(sub) > 0 :
                	min_rect = cv.erode(min_rect, None)
                    sub = cv.subtract(min_rect, thresh)
                    
                cnts = cv.findContours(min_rect.copy(), cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
                cnts = imutils.grab_contours(cnts)
                c = max(cnts, key = cv.contourArea)
                (x, y, w, h) = cv.boundingRect(c)
                
                stitched = stitched[y:y + h, x:x + w]
                
            quality_metrics = []
            
            for idx, image in enumerate(images) :
            	# 비교를 위해 원래 이미지를 스티칭 된 이미지 크기와 같게 조정
            	original_resized = cv.resize(image, (stitched.shape[1], stitched.shape[0]))
                
                # SSIM 계산
                ssim_index = ssim(stitched, original_resized, multichannel_True, win_size = 3, channle_axis = 2)
                
                # MSE 계산
                mse_value = mean_squared_error(stitched, original_resized)
                
                psnr_value = peak_signal_noise_ratio(stitched, original_resized)
                
                print(f"Image {idx + 1} : SSIM = {ssim_index:.4f}, MSE = {mse_value:.2f}, PSNR = {psnr_value:.2f}")
                quality_metrics.append((ssim_index, mse_value, psnr_value))
                
            avg_ssim = np.mean([qm[0] for qm in quality_metrics])
            avg_mse = np.mean([qm[1] for qm in quality_metrics])
            avg_psnr = np.mean([qm[2] for qm in quality_metrics])
            
            print(f"Average SSIM: {avg_ssim:.4f}")
            # MSE 값은 이미지가 유사할수록 값이 작아짐 (동일 이미지 = 0)
            print(f"Average MSE: {avg_mse:.2f}")
            print(f"Average PSNR: {avg_psnr:.2f}")
            
            cv.imwrite(output_dir, stitched)
            print(f"Stitched image saved to {output_dir}")
            
        else:
            print(f"[INFO] Image stitching failed (status={status})")

directory = r"C:\Users\sjhj8\Desktop\Golden_Planet\Image_stitching\sample"  # 원본 이미지들이 있는 경로
output_dir = r"C:\Users\sjhj8\Desktop\Golden_Planet\Image_stitching\output"  # 스티칭된 이미지를 저장할 경로
out_name = "output_image1.jpg"  # 저장할 스티칭 이미지의 파일 이름
crop = "YES"  # 또는 "NO", 크롭 여부에 따라 설정

evaluator = ImageQualityEvaluator(directory, output_dir, out_name, crop)
evaluator.evaluate_quality()

 

728x90

'ssung_인턴일지' 카테고리의 다른 글

14일차_pandas 주요 기능 복습  (4) 2024.07.20
11-13일차_EDA 도출  (1) 2024.07.18
9일차_EDA란?  (1) 2024.07.14
8일차_Data Preprocessing(3)  (0) 2024.07.11
7일차_Data Preprocessing(2)  (0) 2024.07.08