Java OpenCV鸟瞰视图,无数据丢失
我正在使用OpenCV获取捕获帧的鸟瞰视图。这是通过在平面上提供棋盘图案来实现的,棋盘图案将形成鸟瞰视图 虽然看起来相机在这个平面上已经很漂亮了,但为了确定像素和厘米之间的关系,我需要它是完美的 在下一阶段中,捕获帧将被扭曲。它给出了预期的结果: 但是,通过执行此转换,棋盘模式之外的数据正在丢失。我需要的是旋转图像,而不是扭曲已知的四边形 问题:如何通过相机角度旋转图像,使其自上而下Java OpenCV鸟瞰视图,无数据丢失,java,opencv,image-processing,3d,Java,Opencv,Image Processing,3d,我正在使用OpenCV获取捕获帧的鸟瞰视图。这是通过在平面上提供棋盘图案来实现的,棋盘图案将形成鸟瞰视图 虽然看起来相机在这个平面上已经很漂亮了,但为了确定像素和厘米之间的关系,我需要它是完美的 在下一阶段中,捕获帧将被扭曲。它给出了预期的结果: 但是,通过执行此转换,棋盘模式之外的数据正在丢失。我需要的是旋转图像,而不是扭曲已知的四边形 问题:如何通过相机角度旋转图像,使其自上而下 一些代码说明了我目前正在做的事情: Size chessboardSize = new Size(12,
一些代码说明了我目前正在做的事情:
Size chessboardSize = new Size(12, 8); // Size of the chessboard
Size captureSize = new Size(1920, 1080); // Size of the captured frames
Size viewSize = new Size((chessboardSize.width / chessboardSize.height) * captureSize.height, captureSize.height); // Size of the view
MatOfPoint2f imageCorners; // Contains the imageCorners obtained in a earlier stage
Mat H; // Homography
查找角点的代码:
Mat grayImage = new Mat();
//Imgproc.resize(source, temp, new Size(source.width(), source.height()));
Imgproc.cvtColor(source, grayImage, Imgproc.COLOR_BGR2GRAY);
Imgproc.threshold(grayImage, grayImage, 0.0, 255.0, Imgproc.THRESH_OTSU);
imageCorners = new MatOfPoint2f();
Imgproc.GaussianBlur(grayImage, grayImage, new Size(5, 5), 5);
boolean found = Calib3d.findChessboardCorners(grayImage, chessboardSize, imageCorners, Calib3d.CALIB_CB_NORMALIZE_IMAGE + Calib3d.CALIB_CB_ADAPTIVE_THRESH + Calib3d.CALIB_CB_FILTER_QUADS);
if (found) {
determineHomography();
}
确定单应性的代码:
Point[] data = imageCorners.toArray();
if (data.length < chessboardSize.area()) {
return;
}
Point[] roi = new Point[] {
data[0 * (int)chessboardSize.width - 0], // Top left
data[1 * (int)chessboardSize.width - 1], // Top right
data[((int)chessboardSize.height - 1) * (int)chessboardSize.width - 0], // Bottom left
data[((int)chessboardSize.height - 0) * (int)chessboardSize.width - 1], // Bottom right
};
Point[] roo = new Point[] {
new Point(0, 0),
new Point(viewSize.width, 0),
new Point(0, viewSize.height),
new Point(viewSize.width, viewSize.height)
};
MatOfPoint2f objectPoints = new MatOfPoint2f(), imagePoints = new MatOfPoint2f();
objectPoints.fromArray(roo);
imagePoints.fromArray(roi);
Mat H = Imgproc.getPerspectiveTransform(imagePoints, objectPoints);
[Edit2]更新进度 可能存在更多的旋转,因此我将尝试以下方法:
double pnt[col][row][2];
其中(列,行)
是棋盘索引,[2]
存储(x,y)。您可以使用int
,但double/float
将避免在拟合过程中进行不必要的转换和舍入
可以通过如下方式扫描对角相邻像素来检测角点(除非倾斜/旋转接近45度):
d1=0.5*(pp[2]-pp[0]);
d2=0.5*(pp[3]-pp[1]);
a0=pp[1];
a1=d1;
a2=(3.0*(pp[2]-pp[1]))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-pp[2]+pp[1])); }
coordinate = a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
一条对角线应为一种颜色,另一条对角线应为不同颜色。此模式将检测交叉点周围的点簇,以便找到接近这些点并计算其平均值
如果扫描整个图像,循环轴的上部也将对点列表进行排序,因此无需进一步排序。平均后,对网格拓扑中的点进行排序/排序(例如,按两个最近点之间的方向)
p0
这将确保在这一点上有邻居p
但忽略对角线点(|x/y |->1
+/-正方形比例)。从这一点开始计算第一个基向量,现在将其称为u
p
以与#2相同的方式,但这一次也忽略+/-u方向上的点(|(u.v)|/(|u |.| v |)->1
+/-倾斜/旋转)。从这一点开始计算第二个基向量,现在将其称为v
u
向量指向+x
和v
指向+y
方向。因此,具有较大|x |
值的基向量应该是u
,具有较大|y |
值的基向量应该是v
。因此,如果需要,测试和交换。如果符号错了,就用否定。现在我们有了屏幕中间的基向量(越远,它们可能会改变)p0
点设置为(u=0,v=0)
作为起点。现在遍历所有尚未匹配的点p
。对于每个位置,通过从其位置添加/减去基向量来计算邻居的预测位置。然后找到离该位置最近的点,如果找到该点,它应该是相邻点,因此将其(u,v)
坐标设置为原始点+/-1
。现在更新这些点的基向量并循环整个过程,直到没有找到新的匹配。结果应该是,大多数点都应该计算出它们的(u,v)
坐标,这正是我们所需要的min(u)、min(v)
,并将其移动到(0,0)
,以便在需要时索引不会为负值
pnt[i][j][0]=fx(i,j)
pnt[i][j][1]=fy(i,j)
其中,fx,fy
是多项式函数。您可以尝试任何装配过程。我尝试使用的三次多项式拟合,但结果不如本地的双三次插值(可能是因为测试图像的非均匀失真),因此我切换到双三次插值而不是拟合。这更简单,但使求逆变得非常困难,但可以以牺牲速度为代价来避免。如果需要计算逆矩阵,请参见
d1=0.5*(pp[2]-pp[0]);
d2=0.5*(pp[3]-pp[1]);
a0=pp[1];
a1=d1;
a2=(3.0*(pp[2]-pp[1]))-(2.0*d1)-d2;
a3=d1+d2+(2.0*(-pp[2]+pp[1])); }
coordinate = a0+(a1*t)+(a2*t*t)+(a3*t*t*t);
其中,pp[0..3]
是4个后续已知控制点(我们的网格交叉点),a0..a3
是计算出的多项式系数,坐标
是曲线上带有参数t
的点。这可以扩展到任意数量的维度
这条曲线的性质很简单,它是连续的,从pp[1]
开始,到pp[2]
结束,而t=
。通过所有三次曲线的公共序列,确保与相邻段的连续性i,j
作为浮动值,步长约为像素大小的75%,以避免出现间隙。然后只需简单地循环所有位置(i,j)
计算(x,y)
,并将源图像中(x,y)
处的像素复制到(i*sz,j*sz)+/-offset
,其中sz
是所需的像素网格大小