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 |