1. 介绍
我们通过摄像头拍摄时,除非是俯视图拍摄,否则都会出现变形。离摄像头进的地方大,离摄像头远的地方小。
因为空间感,就和我们人眼看物体一样,近大远小。
例如下图所示:
在相机中,真实世界中的标准矩形,变成了梯形。我们如果要获取其中某个坐标点的位置,也会因为这个偏移而发生错误。
而针对这种情况下,我们要计算相机中的坐标,并转换为真实坐标。有两种方法,一种是实现透视变化,一种是计算相机坐标和世界坐标的转换。
- 透视变化:只需要标注4个对应点,不用摄像机或者其他参数。(简单)
- 相机坐标转换:需要知道相机内参信息,相机的俯仰角度等,需要的前置参数较多。(复杂)可以参考:https://www.guyuehome.com/36095
2. 透视变换
实现方法简单,不需要知道摄像机参数或者平面位置的任何信息。只需要标注四个对应点为。和转换后的四个对应点位。
就能直接进行线性方程运算,将图片进行拉伸。透视变换则是在三维空间中视角的变化。
通过Imgproc.getPerspectiveTransform
得到变形矩阵数据,然后在通过Imgproc.warpPerspective
将效果绘制而成就可以了。
Imgproc.getPerspectiveTransform(Mat src, Mat dst, int solveMethod)
- Mat src: 输入图形的四边形顶点坐标
- Mat dst:输出图形的四边形顶点坐标
- int solveMethod:可选项,默认值为
Core.DECOMP_LU
,变换的计算方法,cv::solve 会需要该计算值。
上面的方法就能得到一个透视矩阵的变换函数,Mat对象。这个矩阵是一个3*3的变形矩阵
然后我们再通过Imgproc.warpPerspective
将要透视变换的值,扔进去进行透视变换。可以将坐标扔进去进行变换,也可以将图片扔进行做透视变换。
Imgproc.warpPerspective(Mat src, Mat dst, Mat M, Size dsize, int flags, int borderMode, Scalar borderValue);
- Mat src:输入对象,需要变换的坐标或者图片
- Mat dst:输出对象,变换结束后的效果
- Mat M:3*3尺寸大小的转换矩阵,openCV将会按照这个转换矩阵将输入src转为输出dst。
- Size dsize:输出图像的大小。
- int flags:可选参数,插值方法的组合。一般是使用
Imgproc.INTER_NEAREST
最近邻插值,和Imgproc.INTER_LINEAR
线性插值, - int borderMode:可选参数像,素外推方法 (
Core.BORDER_CONSTANT
指定常数填充 或者Core.BORDER_REPLICATE
复制边缘像素填充). - Scalar borderValue:可选参数,固定边缘情况下使用的值,默认值是0 也就是黑色。
下面结合示例来看看效果吧。
2.1 示例
第一个需求,我想将手机拍摄的梯形,矫正为矩形。效果就是上面示例图的效果:
第一步,就是将获取坐标点,可以通过OpenCV的轮廓识别获取坐标点(精度准确),也可以手动触摸提取坐标点(精度偏移较大)
我这里就简单点了,直接提取触摸点的方法来实现了。获取ImageView控件和Bitmap的矩阵偏移值。
matrix = new Matrix();
binding.image.getImageMatrix().invert(matrix);
matrix.postTranslate(binding.image.getScrollX(), binding.image.getScrollY());
因为我的图片并不是完整填充ImageView,所以我们需要先从ImageView中得到ImageView对象和图片实际之间的偏差。
binding.image.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
//执行坐标转换
final int index = e.getActionIndex();
final float[] coords = new float[]{e.getX(index), e.getY(index)};
matrix.mapPoints(coords);
float x = coords[0];
float y = coords[1];
}
return false;
}
});
如果我们点击图片,就能够得到实际图片的坐标值了。其中的关键方法:matrix.mapPoins()
。进行的转换。
当我们获取了坐标值之后,进行透视变换的矩形数据生成。
中间的获取相机,再将相机的imageProxy转Mat这里就不做介绍,步骤简单。
将得到的Mat 先执行getPerspectiveTransform:
MatOfPoint2f srcPoint = new MatOfPoint2f();
srcPoint.fromArray(point1, point2, point3, point4);
MatOfPoint2f desPoit = new MatOfPoint2f();
desPoit.fromArray(new Point(0, 0), new Point(640,0), new Point(0,480), new Point(640,480));
//得到换算结果
Mat m = Imgproc.getPerspectiveTransform(srcPoint, desPoit);
参数不对就会错误哦。
到这里,只是得到了转换关系的矩阵对象。下面要对图片进行转换操作了。
先介绍一下参数:
- mat:是我们相机拍摄得到的ImageProxy转换的Mat对象,
- dss:是转换后,我们要显示的Mat对象。
- m:是上一步getPerspectiveTransform之后得到的Mat对象。
- size: 是我们dss对象的尺寸大小。
Mat dss = new Mat();
Imgproc.warpPerspective(mat, dss, m, new Size(640, 480));
然后将dss对象转为Bitmap 并进行显示就可以了。
你将会得到:
将原图mat中标注的坐标srcPoint区域的图片,进行截取。并拉伸平铺到desPoit尺寸的区域内进行显示。然后这个尺寸区域将会绘制在dss的Mat中,该mat的值为设置的new Size(640,480)。
大家实际操作一遍就能明白代码逻辑了。能够将摄像机拍摄倾斜的区域,矫正为真实世界上的俯视图效果。
3 错误
3.1 getPerspectiveTransform 坐标错误
在调用getPerspectiveTransform 方法的时候出现崩溃异常: 说坐标点需要时CV_32F。
E/cv::error(): OpenCV(4.6.0) Error: Assertion failed (src.checkVector(2, CV_32F) == 4 && dst.checkVector(2, CV_32F) == 4) in getPerspectiveTransform, file /home/ci/opencv/modules/imgproc/src/imgwarp.cpp, line 3392
原先的写法为:
MatOfPoint srcPoint = new MatOfPoint2f();
srcPoint.fromArray(point1, point2, point3, point4);
MatOfPoint desPoit = new MatOfPoint();
desPoit.fromArray(new Point(0, 0), new Point(640,0), new Point(0,480), new Point(640,480));
//得到换算结果
Mat m = Imgproc.getPerspectiveTransform(srcPoint, desPoit);
修改为:
MatOfPoint2f srcPoint = new MatOfPoint2f();
srcPoint.fromArray(point1, point2, point3, point4);
MatOfPoint2f desPoit = new MatOfPoint2f();
desPoit.fromArray(new Point(0, 0), new Point(640,0), new Point(0,480), new Point(640,480));
//得到换算结果
Mat m = Imgproc.getPerspectiveTransform(srcPoint, desPoit);
也就是修改坐标点的类型,从MatOfPoint
改为MatOfPoint2f
类型就可以了。
参考资料:
https://blog.csdn.net/liuweiyuxiang/article/details/86510191
https://www.guyuehome.com/36095
https://zhuanlan.zhihu.com/p/64025334
https://blog.csdn.net/baidu_36669549/article/details/97825291
评论区