栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

opencv图像配准与图像相减python

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

opencv图像配准与图像相减python

1.问题背景:

给一张图片和模板图片(如下图),需要用传统机器视觉的方法来提取他们不一样的特征(也就是划痕部分),并把划痕转为二值化


1.1 才开始使用基于灰度的方法:

因为划痕和旁边背景颜色比较接近,绘制出的灰度直方图变化不大,调了很久阈值也不能分出划痕部分,效果如下:


1.2 后来直接使用图像相减,RGB和Gray分别图像相减结果得到如下:


解决方案:

应该是没有配准而相减有边框的,最终决定配准后,图像相减
查阅资料文献后,大致思路如下:
1. RGB—> 灰度图
2. 去噪滤波,图像增强等一系列预处理操作
3. 提特征点(ORB)并进行图像配准
4. 配准后进行二值化
5. 图像相减操作(ImageChops.difference)

1. RGB-----> Gray
#  1. 转为灰度图
target = cv2.imread("2368_data.png")
gray_target=cv2.cvtColor(target, cv2.COLOR_BGR2GRAY)
template = cv2.imread("2368_model.png")
gray_template=cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)

2. 去噪滤波 预处理操作
# 去噪滤波 预处理
target_gauss = cv2.GaussianBlur(gray_target,(3, 3),1,1)
template_gauss = cv2.GaussianBlur(gray_template,(3, 3),1,1)

# plt转为cv
target_gauss =Image.fromarray(target_gauss) # np array
template_gauss =Image.fromarray(template_gauss) # np array

# 亮度增强
bright=  0.9
bright_target=ImageEnhance.Brightness(target_gauss).enhance(bright)
bright_template=ImageEnhance.Brightness(template_gauss).enhance(bright)

#色度增强
color = 1.2
colored_target = ImageEnhance.Color(bright_target).enhance(color)
colored_template = ImageEnhance.Color(bright_template).enhance(color)

# 对比度增强
contrast =1.1
contrasted_target = ImageEnhance.Contrast(colored_target).enhance(contrast)
contrasted_template = ImageEnhance.Contrast(colored_template).enhance(contrast)

# 锐度增强
sharpness = 18.0
shaped_target= ImageEnhance.Sharpness(contrasted_target).enhance(sharpness)
shaped_template= ImageEnhance.Sharpness(contrasted_template).enhance(sharpness)

plt.subplot(121), plt.imshow(shaped_target, cmap='gray')
plt.title('shaped_target'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(shaped_template, cmap='gray')
plt.title('shaped_template'), plt.xticks([]), plt.yticks([])
plt.show()

1.只做高斯去噪,不做亮度,锐化等预处理后的图片:

2.不进行高斯去噪,只进行预处理的图:
灰度后的图片会产生很多噪点,因此进行去噪滤波还是有一定效果的。
去噪

3.做完高斯去噪以及一系列亮度,锐化预处理后的图片:
(具体参数还没有详细调,只是先按这个值先设置)

3. 提特征点(ORB或SURF)并进行图像配准 3.1 理论部分

参考资料《mage Alignment (Feature Based) using OpenCV (C++/Python)》

核心思想:
在一个图像中检测到一组特征点,并与另一张图像中的特征点相匹配。然后根据这些匹配的特征点计算出一个转换规则,从而将一个图像映射到另一个图像上。
主要应用就是扫描软件,合成全景图片等。

图像对齐技术的核心是一种维数3X3的单应性矩阵(Homography )

图像配准步骤:

  1. 读取图片
  2. 检测特征点:为两张图检测ORB特征点。为了计算单应性矩阵4个就够了,但是一般会检测到成百上千的特征点。可以使用参数MAX_FEATURES来控制检测的特征点的数量。检测特征点并计算描述子的函数是detectAndCompute。
  3. 特征匹配:找到两图中匹配的特征点,并按照匹配度排列,保留最匹配的一小部分。然后把匹配的特征点画出来并保存图片到磁盘。我们使用Hamming来度量两个特征点描述子的相似度。下图中把匹配的特征点用线连起来了。注意,有很多错误匹配的特征点,所以下一步要用一个健壮的算法来计算单应性矩阵。
  4. 计算单应性矩阵:上一步产生的匹配的特征点不是100%正确的,就算只有20~30%的匹配是正确的也不罕见。findHomography函数使用一种被称为随机抽样一致算法(Random Sample Consensus )的技术在大量匹配错误的情况下计算单应性矩阵。
  5. 扭转图片(Warping image):有了精确的单应性矩阵,就可以把一张图片的所有像素映射到另一个图片。warpPerspective函数用来完成这个功能。

如何把两张图像中的特征点匹配起来呢?
SIFT一般的可以使用K近邻(KNN)算法。K近邻算法求取在空间中距离最近的K个数据点,并将这些数据点归为一类。在进行特征点匹配时,一般使用KNN算法找到最近邻的两个数据点,如果最接近和次接近的比值大于一个既定的值,那么我们保留这个最接近的值,认为它和其匹配的点为good match(有Lowe在SIFT论文中提出)


单应性矩阵Homography Matrix

找到特征点并匹配起来后,如何让两张图片对齐呢?
这就用到了单应性矩阵了。Homography这个词由Homo和graphy,Homo意为同一,graphy意为图像,也就是同一个东西产生的图像。不同视角的图像上的点具有如下关系:

其中[x1 y1 1]和[x2 y2 1]分别表示对应像素的齐次坐标。单应性矩阵就是下面这个矩阵:

可以看到,单应性矩阵有八个参数,如果要解这八个参数的话,需要八个方程,由于每一个对应的像素点可以产生2个方程(x一个,y一个),那么总共只需要四个像素点就能解出这个单应性矩阵。

那么应该选择哪四个点来计算Homography matrix呢?
RANSAC算法巧妙的解决了这一问题。

随机抽样一致算法(Random sample consensus:RANSAC)

说道估算模型的算法,很容易想到最小二乘法。最小二乘法在数据误差比较小的情况下是可以的,但是针对噪声很大的数据集的时候,容易出问题,我们可以通过下面的例子来说明

很明显,我们的数据应该是一条由左下到右上的一条线,但是由于离群数据太多(左上和右下区域),如果用最小二乘法的话,就无法准确的找到我们期望的模型(因为最小二乘法无法剔除误差很大的点,一视同仁的将所有的点都用于模型的计算)。RANSAC(Random Sample Consensus)算法的不同之处就在于,它能够有效的去除误差很大的点,并且这些点不计入模型的计算之中。

所以,在得到了众多的匹配点以后,使用RANSAC算法,每次从中筛选四个随机的点,然后求得H矩阵,不断的迭代,直到求得最优的H矩阵为止。所以在前面使用cv2.findHomography方法时,参数cv2.RANSAC表明使用RANSAC算法来筛选关键点

3.2 代码部分
  1. 读取图片并创建ORB
import cv2 as cv
image1 = cv.imread('1.png')
image2 = cv.imread('2.png')
# 初始化ORB
orb = cv.ORB_create()
  1. 寻找关键点和描述子
 # 寻找关键点
 kp1 = orb.detect(img1)
 kp2 = orb.detect(img2)

 # 计算描述符
kp1,des1=orb.detectAndCompute(img1,None)  # 计算哪张图片的用哪张图片的关键点。
kp2,des2=orb.detectAndCompute(img2,None) 

找到了关键点可以画出关键点看一看:

# 画出关键点
outimg1 = cv.drawKeypoints(img1, keypoints=kp1, outImage=None)
outimg2 = cv.drawKeypoints(img2, keypoints=kp2, outImage=None)

# 这里是把两张图片在同一个窗口中显示。
import numpy as np
outimg3 = np.hstack([outimg1, outimg2])
cv.imshow("Key Points", outimg3)
cv.waitKey(0)

  1. 关键点匹配
    有了关键点和描述子,我们就要对这些点进行匹配。这里我们用的是Brute-Force匹配器,它获取第一组中一个特征的描述符,并通过一些距离计算与第二组中的所有其他特征匹配,最接近的一个被返回。
 # 初始化 BFMatcher
 bf = cv.BFMatcher(cv.NORM_HAMMING)

 # 对描述子进行匹配
 matches = bf.match(des1, des2)
  1. 对匹配点进行筛选并画出匹配图
good_match=sorted(mathces,key=lambda x:x.distance) #据距离来排序
result= cv2.drawMatches(gray_target,kp1, gray_template,kp2,good_match,outImg=None) #画出匹配关系
cv2.namedWindow('Match Result',cv2.WINDOW_NORMAL)
cv2.imshow("Match Result", result)
cv2.waitKey(0)

  1. 计算单应性矩阵 (findHomography函数)

首先找出target和template中的特征点,然后找到较好的匹配点对good_match(判断good_match是否大于4,至少4个匹配点才能构建矩阵),最后通过warpPerspective方法对图像target进行投影映射。

 if len(goodMatch) > 4:
       ptsA= np.float32([kp1[m.queryIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
       ptsB = np.float32([kp2[m.trainIdx].pt for m in goodMatch]).reshape(-1, 1, 2)
       ransacReprojThreshold = 4
       H, status =cv2.findHomography(ptsA,ptsB,cv2.RANSAC,ransacReprojThreshold);
       imgOut = cv2.warpPerspective(img2, H, (img1.shape[1],img1.shape[0]),flags=cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)

cv2.warpPerspective(img,H,(img1.shape[1],img1.shape[0]),cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP)

第一个参数为需要投影的图像(img2)
第二个参数为单应性矩阵(H)
第三个参数为所得图像的矩阵大小((img1.shape[1],img1.shape[0]) )
最后的参数cv2.INTER_LINEAR + cv2.WARP_INVERSE_MAP,为插值时使用的插值方法INTER_LINEAR,cv2.WARP_INVERSE_MAP则将M设置为dst—>src的方向变换。


遇到的问题:
kp1求出来的特征点只有两个,构建单应性矩阵最少需要4个,故一直报错


后来查询到ORB产生的特征点在点特征提取算法中最少(详细特征提取算法比对参考Related-8),因此换成了KAZE检测器,产生的特征点很明显增多,匹配显示结果如下:

最终的透视变换结果如下:

emmmm…后来发现自己可能不需要透视变换,只需要简单的平移对齐就好,于是改成仿射变换的矩阵


Tips 1. plt 与opencv相互转换

plt转为opencv

from PIL import Image
import numpy as np
import cv2
img=Image.open(path)
#PIL转ndarray
img=np.array(img)
img=img[:,:,(2,1,0))]
#show
cv2.imshow('img',img)
cv2.waitKey(0)
#write
cv2.imwrite(path,img)

opencv 转为 plt

img=Image.fromarray(cv2.cvtColor(img,cv2.COLOR_BGR2RGB)))
#img=Image.fromarray(img)
plt.imshow(img)
#show
plt.show()
#save、dpi为分辨率
plt.savefig(path,dpi=100)
2.仿射变换和透视变换

简单写一下:
仿射变换:线性变换+平移,二维图像---->二维图像

透射变换:线性变换+平移,二维图像---->投影到三维平面----->转为二维坐标,因此它是空间上的调整,比如图片弯曲

区别是:仿射变换是(2x3)的变换矩阵,透射变换是(3x3)的变换矩阵

Related

1.Image Alignment (ECC) in OpenCV ( C++ / Python )
2.OpenCV快速入门——图像预处理(必看)
3.opencv+python实现图像匹配----模板匹配、特征点匹配
4.Nice!!!Python进行SIFT图像对准
5.图像对齐(图像配准)方法记录
6.OpenCV-Python Feature2D 特征点检测(含SIFT/SURF/ORB/KAZE/FAST/BRISK/AKAZE)
7.OpenCV-Python ORB特征匹配(实践篇)
8.报错等问题:
第6章 特征检测、匹配与搜索 个人笔记
9.基于机器视觉的PCB缺陷检测与分类识别
10.OPENCV特征点提取算法对比

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/982394.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号