【问题标题】:How to rotate a image without losing the edges如何在不丢失边缘的情况下旋转图像
【发布时间】:2012-08-14 20:19:41
【问题描述】:

我想在 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(组织图片大小为1428x928px):

[ 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 ]

最终

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;

}

【问题讨论】:

    标签: math opencv rotation javacv


    【解决方案1】:

    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,这是包含每个点的最小尺寸。您需要的步骤如下

    1. 获取图像所有 4 个角的坐标。
    2. 通过仿射变换变换每个坐标。
    3. 将这些点传递给 cv::boundingRect 以获得边界矩形。

    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);
    

    此外,平移矩阵中的变量需要是图像1的中心和图像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);
    

    我想大部分屏幕都是黑色的,因为您正在将整个图像从屏幕上移开。

    编辑3

    我只是自己编写并测试了它。这是我的代码。没有从 Matx 到 pt 和返回的所有转换,可能有一种更优雅的方式来做到这一点,但这对我有用。它使用的是 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);
    

    编辑4

    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 度。

    【讨论】:

    • 好的,现在我有了 dst.image 的大小。但它也被裁剪了。因为图像没有放在 dst.image 的中心。有办法吗?
    • 但是如果 (0,0)-(1,1) 为零,那么 alpha 和 beta 就丢失了,不是吗?
    • @destiny 是的,你完全正确,我在编写翻译矩阵时犯了一个错误。查看修改后的矩阵。
    • 嘿,谢谢。现在我得到了 0 度和 360 度(:-X)的结果,但所有其他的......我在某个地方犯了一个错误,但我找不到它:/
    • @destiny 如果你的代码和我的代码一样,它会起作用:) 查看更新的答案。
    【解决方案2】:

    Hammer 的回答对我有用。虽然它给出了一个错误。只需要稍微改变一下语法。这是对我有用的功能。

    Mat rotateImage(Mat im, double angle){
    cv::Matx23d rot = getRotationMatrix2D(cv::Point2f(im.cols/2,im.rows/2),angle,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 < 2; i++)
        for(int j=0; j<3; j++)
            rot33(i,j) = rot(i,j);
    
    rot33(2,0) = 0;
    rot33(2,1) = 0;
    rot33(2,2) = 1;
    cv::Matx33d combined = tran*rot33;
    cv::Matx23d finall;
    for(int i = 0; i < 2; i++)
        for(int j=0; j<3; j++)
            finall(i,j) = combined(i,j);
    
    cv::Size im_size(bounds.width,bounds.height);
    Mat drawing_image;
    cv::warpAffine(im, drawing_image,finall, im_size);
    
    return drawing_image;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-03
      • 1970-01-01
      • 2011-04-14
      • 1970-01-01
      • 1970-01-01
      • 2022-10-23
      • 1970-01-01
      相关资源
      最近更新 更多