使用 openCV 对齐图像
Aligning images using openCV
我有 collection 大约 250 张图像。所有这些都是从一本书中扫描出来的,因此它们都相对于彼此移动或旋转了一点。现在我想对这些图像进行一些数据提取,但为了自动执行此操作,所有图像中的所有位置都应该相同。这就是为什么我需要以某种方式对齐这些图像,以便所有图像中的所有位置都相互对应。实现这一目标的最佳方法是什么?我认为 openCV 是执行此操作的最佳方法,但我不确定应该如何开始。
下面是扫描图像的示例:
虚线可能是一个很好的锚点。
- 使用Morphological Transformation将虚线变成实线。
- 使用 HoughLines
识别行
- 在面具上画线
您可以使用蒙版来对齐单独的图像。扫描蒙版的边缘以获得坐标,并使用它们来旋转和移动图像。我的意思是循环遍历掩码顶行的值。第一个白色像素给出顶部中心坐标。面罩的另一面类似。您可以比较不同图像上的这些值以确定移动和旋转。要应用这些转换,请阅读 here。但这需要相当多的工作。也许有一个更简单的选择:
我可能错了,但您似乎想要对齐页面,因此您可以使用硬编码值提取图表。另一种更简单的方法是使用 findContours 创建 'tiles' 的子图像。然后可以进一步处理这些。这是在下面的代码中实现的。
分离的子图像:
代码:
import cv2
import numpy as np
# load image
img_large=cv2.imread("BAgla.jpg")
# resize for ease of use
img_ori = cv2.resize(img_large, None, fx=0.2, fy=0.2, interpolation= cv2.INTER_CUBIC)
# create grayscale
img = cv2.cvtColor(img_ori, cv2.COLOR_BGR2GRAY)
# create mask for image size
mask = np.zeros((img.shape[:2]),dtype=np.uint8)
# do a morphologic close to merge dotted line
kernel = np.ones((8,8))
res = cv2.morphologyEx(img,cv2.MORPH_OPEN, kernel)
# detect edges for houglines
edges = cv2.Canny(res, 50,50)
# detect lines
lines = cv2.HoughLines(edges,1,np.pi/180,200)
# draw detected lines
for line in lines:
rho,theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(mask,(x1,y1),(x2,y2),(255),2)
cv2.line(img,(x1,y1),(x2,y2),(127),2)
# invert the mask for use with findcontours
mask_inv = cv2.bitwise_not(mask)
# use findcontours to get the boundingboxes of the tiles
contours, hier = cv2.findContours(mask_inv,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
boundingrects = []
for cnt in contours:
boundingrects.append(cv2.boundingRect(cnt))
# findContours has no garanteed order, so sort array
boundingrects.sort()
# titles for window names / save file names
title = ['Kaart', None, 'Smaakprofiel', 'Basiswaarden','Gelijkaardige bieren','Chemisch Profiel']
# create images for top and bottom tiles
for index in [0,2,3,5]:
x,y,w,h = boundingrects[index]
subimg = img_ori[y:y+h,x:x+w]
cv2.imshow(title[index], subimg )
# combine middle tiles
x1,y1,w1,h1 = boundingrects[1]
x2,y2,w2,h2 = boundingrects[4]
subimg = img_ori[y1:y2+h2,x1:x2+w2]
cv2.imshow(title[4], subimg )
# display result
cv2.imshow("Result", img )
cv2.imshow("Mask", mask )
cv2.waitKey(0)
cv2.destroyAllWindows()
请注意,我使用了缩小版的图像,因此在处理图像时要考虑到这一点。
我有 collection 大约 250 张图像。所有这些都是从一本书中扫描出来的,因此它们都相对于彼此移动或旋转了一点。现在我想对这些图像进行一些数据提取,但为了自动执行此操作,所有图像中的所有位置都应该相同。这就是为什么我需要以某种方式对齐这些图像,以便所有图像中的所有位置都相互对应。实现这一目标的最佳方法是什么?我认为 openCV 是执行此操作的最佳方法,但我不确定应该如何开始。
下面是扫描图像的示例:
虚线可能是一个很好的锚点。
- 使用Morphological Transformation将虚线变成实线。
- 使用 HoughLines 识别行
- 在面具上画线
您可以使用蒙版来对齐单独的图像。扫描蒙版的边缘以获得坐标,并使用它们来旋转和移动图像。我的意思是循环遍历掩码顶行的值。第一个白色像素给出顶部中心坐标。面罩的另一面类似。您可以比较不同图像上的这些值以确定移动和旋转。要应用这些转换,请阅读 here。但这需要相当多的工作。也许有一个更简单的选择:
我可能错了,但您似乎想要对齐页面,因此您可以使用硬编码值提取图表。另一种更简单的方法是使用 findContours 创建 'tiles' 的子图像。然后可以进一步处理这些。这是在下面的代码中实现的。
分离的子图像:
代码:
import cv2
import numpy as np
# load image
img_large=cv2.imread("BAgla.jpg")
# resize for ease of use
img_ori = cv2.resize(img_large, None, fx=0.2, fy=0.2, interpolation= cv2.INTER_CUBIC)
# create grayscale
img = cv2.cvtColor(img_ori, cv2.COLOR_BGR2GRAY)
# create mask for image size
mask = np.zeros((img.shape[:2]),dtype=np.uint8)
# do a morphologic close to merge dotted line
kernel = np.ones((8,8))
res = cv2.morphologyEx(img,cv2.MORPH_OPEN, kernel)
# detect edges for houglines
edges = cv2.Canny(res, 50,50)
# detect lines
lines = cv2.HoughLines(edges,1,np.pi/180,200)
# draw detected lines
for line in lines:
rho,theta = line[0]
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
cv2.line(mask,(x1,y1),(x2,y2),(255),2)
cv2.line(img,(x1,y1),(x2,y2),(127),2)
# invert the mask for use with findcontours
mask_inv = cv2.bitwise_not(mask)
# use findcontours to get the boundingboxes of the tiles
contours, hier = cv2.findContours(mask_inv,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
boundingrects = []
for cnt in contours:
boundingrects.append(cv2.boundingRect(cnt))
# findContours has no garanteed order, so sort array
boundingrects.sort()
# titles for window names / save file names
title = ['Kaart', None, 'Smaakprofiel', 'Basiswaarden','Gelijkaardige bieren','Chemisch Profiel']
# create images for top and bottom tiles
for index in [0,2,3,5]:
x,y,w,h = boundingrects[index]
subimg = img_ori[y:y+h,x:x+w]
cv2.imshow(title[index], subimg )
# combine middle tiles
x1,y1,w1,h1 = boundingrects[1]
x2,y2,w2,h2 = boundingrects[4]
subimg = img_ori[y1:y2+h2,x1:x2+w2]
cv2.imshow(title[4], subimg )
# display result
cv2.imshow("Result", img )
cv2.imshow("Mask", mask )
cv2.waitKey(0)
cv2.destroyAllWindows()
请注意,我使用了缩小版的图像,因此在处理图像时要考虑到这一点。