首页 > 解决方案 > 使用 OpenCV 的视角和光照条件略有不同的两张图像之间的差异

问题描述

使用CV - 提取两个图像之间的差异中解释的方法,我们可以识别两个对齐图像之间的差异。

当摄像机角度(视点)和光照条件略有不同时,如何使用 OpenCV 做到这一点?

How to match and align two images using SURF features (Python OpenCV) 中的代码?有助于旋转/对齐两个图像,但由于透视变换(“单应性”)的结果并不完美,“差异”算法在这里无法正常工作。

例如,如何从这 2 张照片中仅获取绿色贴纸(= 差异)?

在此处输入图像描述 在此处输入图像描述

标签: opencvimage-processingdifferenceimage-recognitionhomography

解决方案


对于两个图像的对齐,您可以使用仿射变换。为此,您需要两个图像中的三个点对。为了获得这些点,我将使用对象角。这是我为获得角落而遵循的步骤。

  1. 高斯混合模型的背景减法(或对象提取)
  2. 第一步输出去噪
  3. 使用轮廓获取角点

我将为所有这些功能使用 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)

这是我的最终结果。 提取纸


推荐阅读