使用OpenCV的视角和光照条件略有不同的两幅图像之间的差异
使用CV - 提取两个图像之间的差异中解释的方法,我们可以识别两个对齐图像之间的差异。
当摄像机角度(视角)和光照条件略有不同时,如何使用 OpenCV 做到这一点?
从代码如何搭配和使用对齐功能,SURF(Python的OpenCV的)两个图像?有助于旋转/对齐两个图像,但由于透视变换(“单应性”)的结果并不完美,“差异”算法在这里不能很好地工作。
例如,如何从这 2 张照片中仅获得绿色贴纸(= 差异)?
回答
对于两个图像的对齐,您可以使用仿射变换。为此,您需要来自两个图像的三个点对。为了获得这些点,我将使用对象角。以下是我为获得拐角而遵循的步骤。
- 高斯混合模型的背景减法(或对象提取)
- 第一步输出的噪声去除
- 使用轮廓获取角点
我将使用 opencv 库来实现所有这些功能。
import cv2
from sklearn.mixture import GaussianMixture as GMM
import matplotlib.pyplot as plt
import numpy as np
import math
def extract_object(img):
img2 = img.reshape((-1,3))
n_components = 2
#covariance choices: full, tied, diag, spherical
gmm = GMM(n_components=n_components, covariance_type='tied')
gmm.fit(img2)
gmm_prediction = gmm.predict(img2)
#Put numbers back to original shape so we can reconstruct segmented image
original_shape = img.shape
segmented_img = gmm_prediction.reshape(original_shape[0], original_shape[1])
# set background always to 0
if segmented_img[0,0] != 0:
segmented_img = cv2.bitwise_not(segmented_img)
return segmented_img
def remove_noise(img):
img_no_noise = np.zeros_like(img)
labels,stats= cv2.connectedComponentsWithStats(img.astype(np.uint8),connectivity=4)[1:3]
largest_area_label = np.argmax(stats[1:, cv2.CC_STAT_AREA]) +1
img_no_noise[labels==largest_area_label] = 1
return img_no_noise
def get_box_points(img):
contours, _ = cv2.findContours(img.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
cnt = contours[0]
rect = cv2.minAreaRect(cnt)
box_points = cv2.boxPoints(rect)
box_points = np.int0(box_points)
return box_points
img = cv2.imread('choco.jpg',1)
img_paper = cv2.imread('choco_with_paper.jpg',1)
# remove background
img_bg_removed = extract_object(img)
img_paper_bg_removed = extract_object(img_paper)
img_no_noise = remove_noise(img_bg_removed)
img_paper_no_noise = remove_noise(img_paper_bg_removed)
img_box_points = get_box_points(img_no_noise)
img_paper_box_points = get_box_points(img_paper_no_noise)
图像的四角略微偏离,但对于这项任务来说已经足够了。我确信有更好的方法来检测角落,但这对我来说是最快的解决方案:)
接下来,我将应用仿射变换将原始图像与纸上的图像配准/对齐。
# Affine transformation matrix
M = cv2.getAffineTransform(img_box_points[0:3].astype(np.float32), img_paper_box_points[0:3].astype(np.float32))
# apply M to the original binary image
img_registered = cv2.warpAffine(img_no_noise.astype(np.float32), M, dsize=(img_paper_no_noise.shape[1],img_paper_no_noise.shape[0]))
# get the difference
dif = img_registered-img_paper_no_noise
# remove minus values
dif[dif<1]=0
这是纸张图像和注册原始图像之间的区别。
我所要做的就是在这些区域中获得最大的组件(即这张纸),并应用一个凸包来覆盖这张纸的大部分。
dif = remove_noise(dif) # get the largest component
contours, _ = cv2.findContours(dif.astype(np.uint8), cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
drawing = dif.copy().astype(np.uint8)
hull = [cv2.convexHull(contours[0])]
cv2.drawContours(drawing, hull, 0, 255,-1)
img_paper_extracted = cv2.bitwise_and(img_paper,img_paper,mask=drawing)
这是我的最终结果。