我想在JavaCV / OpenCV中以可变角度(如22°)旋转一些图像。
目前我使用cvWarpAffine()
。
我的问题是,我在旋转后失去了图像的边缘,所以我必须使dst.image更大并移动中心点。在this page我发现了一些AS-Code来计算图像的新大小。但我不知道如何用JavaCV / OpenCV实现它
我及时拥有以下代码:
public CvMat rotateImage(int angle) {
CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
input.rows() / 2.0F);
CvMat rotMat = cvCreateMat(2, 3, CV_32F);
cv2DRotationMatrix(center, angle, 1, rotMat);
CvMat dst = cvCreateMat(input.rows(), input.cols(), input.type());
cvWarpAffine(input, dst, rotMat);
return dst;
}
有人有想法吗?
问候
//更新
我不知道......出了什么问题。 如果我计算了旋转图像,我的结果有正确的尺寸,但它大多是黑色(0和360°工作)... 这是代码:
public CvMat rotateImage(float angle) {
CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
input.rows() / 2.0F);
CvBox2D box = new CvBox2D(center, cvSize2D32f(input.cols() - 1,
input.rows() - 1), angle);
CvPoint2D32f points = new CvPoint2D32f(4);
cvBoxPoints(box, points);
CvMat pointMat = cvCreateMat(1, 4, CV_32FC2);
pointMat.put(0, 0, 0, points.position(0).x());
pointMat.put(0, 0, 1, points.position(0).y());
pointMat.put(0, 1, 0, points.position(1).x());
pointMat.put(0, 1, 1, points.position(1).y());
pointMat.put(0, 2, 0, points.position(2).x());
pointMat.put(0, 2, 1, points.position(2).y());
pointMat.put(0, 3, 0, points.position(3).x());
pointMat.put(0, 3, 1, points.position(3).y());
CvRect boundingRect = cvBoundingRect(pointMat, 0);
CvMat dst = cvCreateMat(boundingRect.height(), boundingRect.width(),
input.type());
CvPoint2D32f centerDst = new CvPoint2D32f(center.x()
+ (dst.cols() / 2.0F), center.y() + (dst.rows() / 2.0F));
CvMat rotMat = cvCreateMat(2, 3, CV_32F);
cv2DRotationMatrix(centerDst, angle, 1, rotMat);
CvMat trans = cvCreateMat(3, 3, CV_32F);
cvZero(trans);
trans.put(0, 2, dst.cols() / 2.0F);
trans.put(1, 2, dst.rows() / 2.0F);
trans.put(0, 0, 1);
trans.put(1, 1, 1);
trans.put(2, 2, 1);
CvMat newRot = cvCreateMat(3, 3, CV_32F);
cvZero(newRot);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
newRot.put(i, j, rotMat.get(i, j));
}
}
newRot.put(2, 2, 1);
cvMul(trans, newRot, newRot, 1);
cvWarpPerspective(input, dst, newRot);
// cvWarpAffine(input, dst, dstRotMat);
return dst;
}
rotMat
看起来像:
[ 0.9396926, 0.34202015, -311.1334
-0.34202015, 0.9396926, 601.47485 ]
trans
(组织图片的大小为1428x928像素):
[ 1.0, 0.0, 836.0
0.0, 1.0, 699.0
0.0, 0.0, 1.0 ]
和newRot
[ 0.9396926, 0.0, -260107.52
-0.0, 0.9396926, 420430.94
0.0, 0.0, 1.0 ]
我找不到错误
//更新2
public CvMat rotateImage(float angle) {
CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
input.rows() / 2.0F);
CvBox2D box = new CvBox2D(center, cvSize2D32f(input.cols() - 1,
input.rows() - 1), angle);
CvPoint2D32f points = new CvPoint2D32f(4);
cvBoxPoints(box, points);
CvMat pointMat = cvCreateMat(1, 4, CV_32FC2);
pointMat.put(0, 0, 0, points.position(0).x());
pointMat.put(0, 0, 1, points.position(0).y());
pointMat.put(0, 1, 0, points.position(1).x());
pointMat.put(0, 1, 1, points.position(1).y());
pointMat.put(0, 2, 0, points.position(2).x());
pointMat.put(0, 2, 1, points.position(2).y());
pointMat.put(0, 3, 0, points.position(3).x());
pointMat.put(0, 3, 1, points.position(3).y());
CvRect boundingRect = cvBoundingRect(pointMat, 0);
CvMat dst = cvCreateMat(boundingRect.height(), boundingRect.width(),
input.type());
// CvPoint2D32f centerDst = new CvPoint2D32f(((dst.cols()-input.cols()
// )/ 2.0F),(( dst.rows()-input.rows()) / 2.0F));
CvMat rotMat = cvCreateMat(2, 3, CV_32F);
cv2DRotationMatrix(center, angle, 1, rotMat);
CvMat trans = cvCreateMat(3, 3, CV_32F);
cvZero(trans);
trans.put(0, 2, (dst.cols() - input.cols()) / 2.0F);
trans.put(1, 2, (dst.rows() - input.rows()) / 2.0F);
trans.put(0, 0, 1);
trans.put(1, 1, 1);
trans.put(2, 2, 1);
CvMat newRot = cvCreateMat(3, 3, CV_32F);
cvZero(newRot);
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
newRot.put(i, j, rotMat.get(i, j));
}
}
newRot.put(2, 2, 1);
cvMul(trans, newRot, newRot, 1);
cvWarpPerspective(input, dst, newRot);
// cvWarpAffine(input, dst, rotMat);
System.out.println(rotMat);
System.out.println(trans);
System.out.println(newRot);
return dst;
在1度时,矩阵看起来像:
rotMat
:
[ 0.9998477, 0.017452406, -8.338219
-0.017452406, 0.9998477, 12.534734 ]
trans
[ 1.0, 0.0, -8.0
0.0, 1.0, -12.0
0.0, 0.0, 1.0 ]
newRot
[ 0.9998477, 0.0, 66.70575
-0.0, 0.9998477, -150.41681
0.0, 0.0, 1.0 ]
FINAL
使用Hammer的指令编写JavaCode:
public CvMat rotateImage(float angle) {
CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,
input.rows() / 2.0F);
CvBox2D box = new CvBox2D(center, cvSize2D32f(input.cols() - 1,
input.rows() - 1), angle);
CvPoint2D32f points = new CvPoint2D32f(4);
cvBoxPoints(box, points);
CvMat pointMat = cvCreateMat(1, 4, CV_32FC2);
pointMat.put(0, 0, 0, points.position(0).x());
pointMat.put(0, 0, 1, points.position(0).y());
pointMat.put(0, 1, 0, points.position(1).x());
pointMat.put(0, 1, 1, points.position(1).y());
pointMat.put(0, 2, 0, points.position(2).x());
pointMat.put(0, 2, 1, points.position(2).y());
pointMat.put(0, 3, 0, points.position(3).x());
pointMat.put(0, 3, 1, points.position(3).y());
CvRect boundingRect = cvBoundingRect(pointMat, 0);
CvMat dst = cvCreateMat(boundingRect.height(), boundingRect.width(),
input.type());
CvMat rotMat = cvCreateMat(2, 3, CV_32FC1);
cv2DRotationMatrix(center, angle, 1, rotMat);
double y_1 = ((boundingRect.width() - input.cols()) / 2.0F)
+ rotMat.get(0, 2);
double y_2 = ((boundingRect.height() - input.rows()) / 2.0F + rotMat
.get(1, 2));
rotMat.put(0, 2, y_1);
rotMat.put(1, 2, y_2);
cvWarpAffine(input, dst, rotMat);
return dst;
}
答案 0 :(得分:6)
opencv有一个函数已经用于查找边界矩形,这是你链接到的代码所做的事情。以下是使用它的示例
std::vector<cv::Point2f> points;
points.push_back(cv::Point2f(0,0));
points.push_back(cv::Point2f(13,0));
points.push_back(cv::Point2f(0,11));
points.push_back(cv::Point2f(10,10));
cv::Rect rectangle = cv::boundingRect(points);
返回的矩形的左上角为(0,0),宽度为14,高度为12,最小尺寸将包含每个点。您需要的步骤如下
boundingRect返回的矩形的尺寸是目标图像所需的尺寸。不要考虑边界矩形的位置,只考虑其尺寸。
修改强>
第二个问题是将旧图像的中心与新图像的中心对齐。如果矩形的尺寸改变dw和dh,那么
centerNew.x = centerOld.x+dw/2;
centerNew.y = centerOld.y+dh/2;
所以你需要整个旧图像向右移动dw / 2和向下(正y)dh / 2。这种运动可以包含在你的仿射变换中。您希望每个像素都被变换扭曲,然后通过矩阵
进行平移tran_mat = [1,0,dw/2,
0,1,dh/2]
要将两者结合起来,需要将它们相乘
NewWarp = tran_mat * old_warp;
不幸的是,你不能只是将它们相乘,因为它们的尺寸不匹配。在这种情况下,通过将[0,0,1]添加为新的第三行,将它们转换为3x3矩阵。然后它们可以相乘。然后,您可以将该矩阵传递给cv :: warpPerspective,或者通过删除底行(仍应为[0,0,1]来将其转换回仿射变换。
编辑2 我认为你有两个问题。第一
cv2DRotationMatrix(centerDst, angle, 1, rotMat);
centerDst应该代表输入图像的中心,应该是
CvPoint2D32f center = new CvPoint2D32f(input.cols() / 2.0F,input.rows() / 2.0F);
所以将您的电话改为
cv2DRotationMatrix(center, angle, 1, rotMat);
此外,放置在平移矩阵中的变量需要是image1中心与图像2中心之间的距离。有几种方法可以计算但是
trans.put(0, 2, dst.cols() / 2.0F);
trans.put(1, 2, dst.rows() / 2.0F);
不是其中之一。这将为您提供中心的移动
trans.put(0, 2, (dst.cols()-input.cols()) / 2.0F);
trans.put(1, 2, (dst.rows()-input.rows()) / 2.0F);
我想大部分屏幕都是黑色的,因为你正在将整个图像从屏幕上翻译过来。
<强> EDIT3 强>
我自己写了它并测试了它。这是我的代码。如果没有从Matx到pt和back的所有转换,可能有一种更优雅的方式,但这对我有用。它使用的是c ++,但它仍然清楚地显示了这个过程。找出你偏离这个的地方
cv::Mat im; //your image
cv::Matx23d rot = getRotationMatrix2D(cv::Point2f(im.cols/2,im.rows/2),45,1);
cv::Matx31d tl(0,0,1);
cv::Matx31d tr(im.cols,0,1);
cv::Matx31d bl(0,im.rows,1);
cv::Matx31d br(im.cols,im.rows,1);
std::vector<cv::Point2f> pts;
cv::Matx21d tl2 = rot*tl;
cv::Matx21d tr2 = rot*tr;
cv::Matx21d bl2 = rot*bl;
cv::Matx21d br2 = rot*br;
pts.push_back(cv::Point2f(tl2(0),tl2(1)));
pts.push_back(cv::Point2f(tr2(0),tr2(1)));
pts.push_back(cv::Point2f(bl2(0),bl2(1)));
pts.push_back(cv::Point2f(br2(0),br2(1)));
cv::Rect bounds = cv::boundingRect(pts);
cv::Matx33d tran(1,0,(bounds.width-im.cols)/2,
0,1,(bounds.height-im.rows)/2,
0,0,1);
cv::Matx33d rot33;
for(int i = 0; i < 6; i++)
rot33(i) = rot(i);
rot33(2,0) = 0;
rot33(2,1) = 0;
rot33(2,2) = 1;
cv::Matx33d combined = tran*rot33;
cv::Matx23d final;
for(int i = 0; i < 6; i++)
final(i) = combined(i);
cv::Size im_size(bounds.width,bounds.height);
cv::warpAffine(im, drawing_image,final, im_size);
<强> Edit4 强>
rotation = 45 deg;
width = 300 height = 287
rotation matrix
[0.7071067811865476, 0.7071067811865475, -57.18228688765842;
-0.7071067811865475, 0.7071067811865476, 147.9497474683058]
translation matrix
[1, 0, 58;
0, 1, 64;
0, 0, 1]
combined matrix
[0.7071067811865476, 0.7071067811865475, 0.8177131123415791;
-0.7071067811865475, 0.7071067811865476, 211.9497474683058]
这是原始图片
然后使用我发布的代码将其旋转45度。
答案 1 :(得分:0)
哈默的答案为我做了诀窍。但它给出了一个错误。只需稍微改变语法。这是对我有用的功能。
lastsolution = cpx.solution.get_values()
cpx.start.set_start(col_status=[], row_status=[],
col_primal=lastsolution, row_primal=[],
col_dual=[], row_dual=[])