python - 基于特色的图像对齐问题

标签 python opencv

我正在尝试学习 OpenCV,以改进我为比较工程图而编写的脚本。我正在使用 this 上的代码(见下文)教程,但我的成功率为零。在本教程中,作者使用空白表格作为引用图像的示例,并使用已完成表格的照片作为要对齐的图像。我的情况非常相似,因为我试图使用空白绘图标题 block 作为引用图像,并将绘图的扫描图像作为我的图像进行对齐。

我的目标是使用 OpenCV 清理扫描的工程图,以便它们正确对齐,但无论我在 MAX_FEATURES 和 GOOD_MATCH_PERCENT 参数中尝试什么,我都会得到一个看起来像黑白星爆的图像。此外,当我查看脚本生成的“matches.jpg”文件时,似乎没有正确的匹配项。我尝试了多张图纸,得到了相同的结果。

任何人都可以看到为什么这个脚本不能以我尝试使用它的方式工作的原因吗?

from __future__ import print_function
import cv2
import numpy as np


MAX_FEATURES = 500
GOOD_MATCH_PERCENT = 0.15


def alignImages(im1, im2):

  # Convert images to grayscale
  im1Gray = cv2.cvtColor(im1, cv2.COLOR_BGR2GRAY)
  im2Gray = cv2.cvtColor(im2, cv2.COLOR_BGR2GRAY)

  # Detect ORB features and compute descriptors.
  orb = cv2.ORB_create(MAX_FEATURES)
  keypoints1, descriptors1 = orb.detectAndCompute(im1Gray, None)
  keypoints2, descriptors2 = orb.detectAndCompute(im2Gray, None)

  # Match features.
  matcher = cv2.DescriptorMatcher_create(cv2.DESCRIPTOR_MATCHER_BRUTEFORCE_HAMMING)
  matches = matcher.match(descriptors1, descriptors2, None)

  # Sort matches by score
  matches.sort(key=lambda x: x.distance, reverse=False)

  # Remove not so good matches
  numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
  matches = matches[:numGoodMatches]

  # Draw top matches
  imMatches = cv2.drawMatches(im1, keypoints1, im2, keypoints2, matches, None)
  cv2.imwrite("matches.jpg", imMatches)

  # Extract location of good matches
  points1 = np.zeros((len(matches), 2), dtype=np.float32)
  points2 = np.zeros((len(matches), 2), dtype=np.float32)

  for i, match in enumerate(matches):
    points1[i, :] = keypoints1[match.queryIdx].pt
    points2[i, :] = keypoints2[match.trainIdx].pt

  # Find homography
  h, mask = cv2.findHomography(points1, points2, cv2.RANSAC)

  # Use homography
  height, width, channels = im2.shape
  im1Reg = cv2.warpPerspective(im1, h, (width, height))

  return im1Reg, h


if __name__ == '__main__':

  # Read reference image
  refFilename = "form.jpg"
  print("Reading reference image : ", refFilename)
  imReference = cv2.imread(refFilename, cv2.IMREAD_COLOR)

  # Read image to be aligned
  imFilename = "scanned-form.jpg"
  print("Reading image to align : ", imFilename);  
  im = cv2.imread(imFilename, cv2.IMREAD_COLOR)

  print("Aligning images ...")
  # Registered image will be resotred in imReg. 
  # The estimated homography will be stored in h. 
  imReg, h = alignImages(im, imReference)

  # Write aligned image to disk. 
  outFilename = "aligned.jpg"
  print("Saving aligned image : ", outFilename); 
  cv2.imwrite(outFilename, imReg)

  # Print estimated homography
  print("Estimated homography : \n",  h)

模板图片:
template

要对齐的图像:
scanned drawing

预期输出图像:
enter image description here

最佳答案

这是 Python/OpenCV 中使用刚性仿射变换的一种方法 (仅缩放、旋转和平移 - 没有倾斜或透视)扭曲一个图像以匹配另一个图像。它使用 findTransformECC()(增强的相关系数最大化)来获取旋转矩阵,然后使用 warpAffine 进行刚性翘曲。

模板:

enter image description here

要变形的图像:

enter image description here

import cv2
import numpy as np
import math
import sys

# Get the image files from the command line arguments
# These are full paths to the images
# image2 will be warped to match image1
# argv[0] is name of script
image1 = sys.argv[1]
image2 = sys.argv[2]
outfile = sys.argv[3]

# Read the images to be aligned
# im2 is to be warped to match im1
im1 =  cv2.imread(image1);
im2 =  cv2.imread(image2);

# Convert images to grayscale for computing the rotation via ECC method
im1_gray = cv2.cvtColor(im1,cv2.COLOR_BGR2GRAY)
im2_gray = cv2.cvtColor(im2,cv2.COLOR_BGR2GRAY)

# Find size of image1
sz = im1.shape

# Define the motion model - euclidean is rigid (SRT)
warp_mode = cv2.MOTION_EUCLIDEAN

# Define 2x3 matrix and initialize the matrix to identity matrix I (eye)
warp_matrix = np.eye(2, 3, dtype=np.float32)

# Specify the number of iterations.
number_of_iterations = 5000;

# Specify the threshold of the increment
# in the correlation coefficient between two iterations
termination_eps = 1e-3;

# Define termination criteria
criteria = (cv2.TERM_CRITERIA_EPS | cv2.TERM_CRITERIA_COUNT, number_of_iterations,  termination_eps)

# Run the ECC algorithm. The results are stored in warp_matrix.
(cc, warp_matrix) = cv2.findTransformECC (im1_gray, im2_gray, warp_matrix, warp_mode, criteria, None, 1)

# Warp im2 using affine
im2_aligned = cv2.warpAffine(im2, warp_matrix, (sz[1],sz[0]), flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP);

# write output
cv2.imwrite(outfile, im2_aligned)

# Print rotation angle
row1_col0 = warp_matrix[0,1]
angle = math.degrees(math.asin(row1_col0))
print(angle)

结果:

enter image description here

产生的旋转角度(度):
-0.3102187026194794

请注意,如果需要,您可以将 affineWarp 中的背景颜色更改为白色。

还要使终止 epsilon 小一到两个数量级,以提高准确性,但处理时间更长。

我之前在评论中提到的另一种刚性仿射方法是 使用ORB特征匹配,过滤关键点,然后使用estimateAffinePartial2D()得到刚性仿射矩阵。然后用它来扭曲图像。对于大角度,这在我看来比 ECC 方法更可靠。但是 ECC 方法对于小旋转似乎更准确。
import cv2
import numpy as np
import math
import sys


MAX_FEATURES = 10000
GOOD_MATCH_PERCENT = 0.15
DIFFY_THRESH = 2

# Get the image files from the command line arguments
# These are full paths to the images
# image[2] will be warped to match image[1]
# argv[0] is name of script
file1 = sys.argv[1]
file2 = sys.argv[2]
outFile = sys.argv[3]

# Read image1
image1 = cv2.imread(file1, cv2.IMREAD_COLOR)

# Read image2 to be warped to match image1
image2 = cv2.imread(file2, cv2.IMREAD_COLOR)

# Convert images to grayscale
image1Gray = cv2.cvtColor(image1, cv2.COLOR_BGR2GRAY)
image2Gray = cv2.cvtColor(image2, cv2.COLOR_BGR2GRAY)

# Detect ORB features and compute descriptors.
orb = cv2.ORB_create(MAX_FEATURES)
keypoints1, descriptors1 = orb.detectAndCompute(image1Gray, None)
keypoints2, descriptors2 = orb.detectAndCompute(image2Gray, None)

# Match features.
matcher = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True)
matches = matcher.match(descriptors1, descriptors2, None)

# Sort matches by score
matches.sort(key=lambda x: x.distance, reverse=False)

# Remove not so good matches
numGoodMatches = int(len(matches) * GOOD_MATCH_PERCENT)
matches = matches[:numGoodMatches]
#print('numgood',numGoodMatches)

# Extract location of good matches and filter by diffy if rotation is small
points1 = np.zeros((len(matches), 2), dtype=np.float32)
points2 = np.zeros((len(matches), 2), dtype=np.float32)

for i, match in enumerate(matches):
    points1[i, :] = keypoints1[match.queryIdx].pt
    points2[i, :] = keypoints2[match.trainIdx].pt

# initialize empty arrays for newpoints1 and newpoints2 and mask
newpoints1 = np.empty(shape=[0, 2], dtype=np.float32)
newpoints2 = np.empty(shape=[0, 2], dtype=np.float32)
matches_Mask = [0] * len(matches)

count=0
for i in range(len(matches)):
    pt1 = points1[i]
    pt2 = points2[i]
    pt1x, pt1y = zip(*[pt1])
    pt2x, pt2y = zip(*[pt2])
    diffy = np.float32( np.float32(pt2y) - np.float32(pt1y) )
    if abs(diffy) < DIFFY_THRESH:
        newpoints1 = np.append(newpoints1, [pt1], axis=0).astype(np.uint8)
        newpoints2 = np.append(newpoints2, [pt2], axis=0).astype(np.uint8)
        matches_Mask[i]=1
        count += 1

# Find Affine Transformation
# note swap of order of newpoints here so that image2 is warped to match image1
m, inliers = cv2.estimateAffinePartial2D(newpoints2,newpoints1)

# Use affine transform to warp im2 to match im1
height, width, channels = image1.shape
image2Reg = cv2.warpAffine(image2, m, (width, height))

# Write aligned image to disk. 
cv2.imwrite(outFile, image2Reg)

# Print angle
row1_col0 = m[1,0]
print('row1_col0:',row1_col0)
angle = math.degrees(math.asin(row1_col0))
print('angle', angle)

结果图像:

enter image description here

结果旋转角度:
-0.6123936361765413

关于python - 基于特色的图像对齐问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59654471/

相关文章:

python - 如何抑制pip升级警告?

python - 在 Python 3.5.1 中酸洗 ParseResult 时出错

python - PyCharm - 尾部类型提示和线宽限制

python pandas 将文本中的数字提取到新列

c++ - 唯一标识 OpenCV 相机

c++ - 我们可以使用 pthread 库进行 opencv C++ 编程吗?

Python从配置创建字典的字典

iphone - iphone 应用程序图像中的颜色替换

python - 如何在灰色图像上覆盖红色的热图 numpy 数组?

c++ - opencv:如何用距中心的距离填充椭圆形状