使用opencv-python的薄板样条插值(tps)实现图像和坐标变换
自己用opencv-python做基于薄板样条图像配准时踩了不少坑。网上现有关于thinplatetransformer的内容太少,这里跟大家分享下错误经验。
opencv安装
需要注意opencv-python、opencv-python-contrib、numpy版本一定要匹配,不然会出现查看不了注释的情况,详见: Pycharm配置opencv与numpy
我是用的版本是如下:
python-3.8.0
numpy-1.22.4+kml
opencv&opencv-contrib-4.5.5 (.62)
tps模块使用方法
类说明文档: ThinPlateSplineShapeTransformer OpenCV Documentation
我自己写的例程如下:
def TPSTransformWithPoints(pts1, pts2, img=None, ptm=None):
"""
Editor: 五月的白杨 CSDN
Perform thin plate spline transform
inputs:
pts1---marker points on moving image, with size (m,2), ndarray, dtpye numeric, float or int
pts2---marker points on fixed image, with size (m,2), ndarray, dtpye numeric, float or int
img ---moving image, an cv2 image
ptm ---other points needed to be transformed on moving image,
outputs:
result registrated moving image
p_iter registrated points of moving image
"""
m = pts1.shape[0]
# Reshape pts1 & pts2
pts1 = pts1.reshape(1, -1, 2)
pts2 = pts2.reshape(1, -1, 2)
matches = []
# Create Matches
for i in range(m):
matches.append(cv2.DMatch(i, i, 0))
# Estimate Transformation
tps_img = cv2.createThinPlateSplineShapeTransformer()
tps_pts = cv2.createThinPlateSplineShapeTransformer()
tps_img.estimateTransformation(pts2, pts1, matches)
tps_pts.estimateTransformation(pts1, pts2, matches)
# Outputs
p = ptm.reshape(1, -1, 2)
if ptm is not None:
p_iter_tps = tps_pts.applyTransformation(p)
else:
p_iter_tps = None
if img is not None:
result_tps = tps_img.warpImage(img)
else:
result_tps = None
return result_tps, p_iter_tps
注意事项:
1. 创建对象一般用 cv2.createThinPlateSplineShapeTransformer(),用到的函数为estimateTransformation(),但我这里对浮动图像(img)做变换和对浮动图像上的点(pts)做变换,使用了两个transformer对象: tps_img 和 tps_pts 分别算了一次。
原因是opencv封装的ThinPlateSpline模块从2017年就有一个没修的bug,estimatetransformation的说明文档的输入顺序如下:source_points,target_points,matches. 但开发者搞反了输入顺序,如果对图像变换,source 和 target 点应该互换,但对浮动图像选的点做变换又是正确的(崩溃)参考这个opencv的issue: 点此查看 https://github.com/opencv/opencv/issues/7084
简而言之
# 对图像变换用这个
tps_img = cv2.createThinPlateSplineShapeTransformer()
tps_img.estimateTransformation(target_points, source_points, matches)
pts = tps_img.warpImage(img)
# 对点变换用这个
tps_pts = cv2.createThinPlateSplineShapeTransformer()
tps_pts.estimateTransformation(source_points, target_points, matches)
pts = tps_pts.applyTransformation(p)
"""两个target和source的顺序是相反的"""
2. 如果输入的pts1 pts2为(m,2)的ndarray,下面两句是必要的,因为opencv的ThinPlateSpline模块estimateTransformation只接受输入为(1,m,2)的tuple貌似,不然就会报错
pts1 = pts1.reshape(1, -1, 2)
pts2 = pts2.reshape(1, -1, 2)
3. 最后补充一个注意事项,输入的坐标点必须转为np.float32 np.float32 np.float32 重要的事情说三遍,不然真的被坑惨了。。。