旋转cv :: Mat使用cv :: warpAffine偏移目标图像
我正在尝试使用OpenCV的C ++ API将1296×968图像旋转 90度 ,并且遇到了一些问题。
input :
旋转 :
正如你所看到的,旋转的图像有一些问题。 首先,它具有与原始大小相同的尺寸,即使我专门创build了与原始尺寸相反的目标Mat
。 结果,目标图像被裁剪。
我怀疑这是因为我打电话warpAffine()
并传递原始的大小,而不是目的地大小的Mat
。 但是我这样做是因为我遵循了这个答案 ,但现在我怀疑答案可能是错的。 所以这是我第一个怀疑/问题。
第二个是, warpAffine()
以特定的偏移量写入目的地 (可能将旋转的数据复制到图像的中间),并且该操作在图像周围留下可怕的大黑色边框 。
我如何解决这些问题?
我在下面分享源代码:
#include <cv.h> #include <highgui.h> #include <iostream> using namespace cv; using namespace std; void rotate(Mat& image, double angle) { Point2f src_center(image.cols/2.0F, image.rows/2.0F); Mat rot_matrix = getRotationMatrix2D(src_center, angle, 1.0); Mat rotated_img(Size(image.size().height, image.size().width), image.type()); warpAffine(image, rotated_img, rot_matrix, image.size()); imwrite("rotated.jpg", rotated_img); } int main(int argc, char* argv[]) { Mat orig_image = imread(argv[1], 1); if (orig_image.empty()) { cout << "!!! Couldn't load " << argv[1] << endl; return -1; } rotate(orig_image, 90); return 0; }
我find了一个不涉及warpAffine()
的解决scheme 。
但在此之前,我需要陈述(为了将来的参考),我怀疑是正确的,在调用warpAffine()
时需要传递目的地的大小:
warpAffine(image, rotated_img, rot_matrix, rotated_img.size());
据我所知,由这个函数绘制的黑色边框(由写入偏移引起的)似乎是标准的行为。 我已经注意到了这一点与C接口,以及在Mac和Linux上运行的OpenCV的C ++接口,使用版本2.3.1a和2.3.0。
我最终使用的解决scheme比所有这些变形 要简单得多。 您可以使用cv::transpose()
和cv::flip()
将图像旋转90度。 这里是:
Mat src = imread(argv[1], 1); cv::Mat dst; cv::transpose(src, dst); cv::flip(dst, dst, 1); imwrite("rotated90.jpg", dst);
—- I>
很多人都有旋转图像或图像块由于偏移等问题。所以,我发布一个解决scheme,让您旋转图像的区域(或整体),并将其粘贴到另一个图像或具有该function计算一切都适合的图像。
// ROTATE p by R /** * Rotate p according to rotation matrix (from getRotationMatrix2D()) R * @param R Rotation matrix from getRotationMatrix2D() * @param p Point2f to rotate * @return Returns rotated coordinates in a Point2f */ Point2f rotPoint(const Mat &R, const Point2f &p) { Point2f rp; rp.x = (float)(R.at<double>(0,0)*px + R.at<double>(0,1)*py + R.at<double>(0,2)); rp.y = (float)(R.at<double>(1,0)*px + R.at<double>(1,1)*py + R.at<double>(1,2)); return rp; } //COMPUTE THE SIZE NEEDED TO LOSSLESSLY STORE A ROTATED IMAGE /** * Return the size needed to contain bounding box bb when rotated by R * @param R Rotation matrix from getRotationMatrix2D() * @param bb bounding box rectangle to be rotated by R * @return Size of image(width,height) that will compleley contain bb when rotated by R */ Size rotatedImageBB(const Mat &R, const Rect &bb) { //Rotate the rectangle coordinates vector<Point2f> rp; rp.push_back(rotPoint(R,Point2f(bb.x,bb.y))); rp.push_back(rotPoint(R,Point2f(bb.x + bb.width,bb.y))); rp.push_back(rotPoint(R,Point2f(bb.x + bb.width,bb.y+bb.height))); rp.push_back(rotPoint(R,Point2f(bb.x,bb.y+bb.height))); //Find float bounding box r float x = rp[0].x; float y = rp[0].y; float left = x, right = x, up = y, down = y; for(int i = 1; i<4; ++i) { x = rp[i].x; y = rp[i].y; if(left > x) left = x; if(right < x) right = x; if(up > y) up = y; if(down < y) down = y; } int w = (int)(right - left + 0.5); int h = (int)(down - up + 0.5); return Size(w,h); } /** * Rotate region "fromroi" in image "fromI" a total of "angle" degrees and put it in "toI" if toI exists. * If toI doesn't exist, create it such that it will hold the entire rotated region. Return toI, rotated imge * This will put the rotated fromroi piece of fromI into the toI image * * @param fromI Input image to be rotated * @param toI Output image if provided, (else if &toI = 0, it will create a Mat fill it with the rotated image roi, and return it). * @param fromroi roi region in fromI to be rotated. * @param angle Angle in degrees to rotate * @return Rotated image (you can ignore if you passed in toI */ Mat rotateImage(const Mat &fromI, Mat *toI, const Rect &fromroi, double angle) { //CHECK STUFF // you should protect against bad parameters here ... omitted ... //MAKE OR GET THE "toI" MATRIX Point2f cx((float)fromroi.x + (float)fromroi.width/2.0,fromroi.y + (float)fromroi.height/2.0); Mat R = getRotationMatrix2D(cx,angle,1); Mat rotI; if(toI) rotI = *toI; else { Size rs = rotatedImageBB(R, fromroi); rotI.create(rs,fromI.type()); } //ADJUST FOR SHIFTS double wdiff = (double)((cx.x - rotI.cols/2.0)); double hdiff = (double)((cx.y - rotI.rows/2.0)); R.at<double>(0,2) -= wdiff; //Adjust the rotation point to the middle of the dst image R.at<double>(1,2) -= hdiff; //ROTATE warpAffine(fromI, rotI, R, rotI.size(), INTER_CUBIC, BORDER_CONSTANT, Scalar::all(0)); //& OUT return(rotI); }
我意识到你已经find了其他更快的解决scheme(90度旋转应该是非常快的,并不需要warpAffine的所有机器),但我想解决黑色边界问题的任何人谁跑过这个。
还有什么可以warpAffine呢? 目标图像被指定为比较高的宽度,并且仿射变换仅指定旋转(围绕图像的中心),而不是缩放。 这正是它所做的。 没有任何信息可以告诉warpAffine这些黑色边框应该画什么,所以它们留下黑色。
直接的物理实验:把一张纸放在桌子上。 在它周围绘制轮廓(这是您在指定希望结果与原始形状/大小相同时所做的操作)。 现在围绕其中心旋转该表格90度。 看看桌子上的大纲所限定的区域。 如果它是一张黑色的桌子,它看起来就像你的结果。
也许这可以帮助别人。
variables是
img:原始图像
angular度:度
规模
dst:目标图像
int width = img.size().width, height = img.size().height; Mat rot = getRotationMatrix2D(Point2f(0,0), angle, scale)/scale; //scale later double sinv = rot.at<double>(0,1), cosv = rot.at<double>(0,0); rot.at<double>(1,2) = width*sinv; //adjust row offset Size dstSize(width*cosv + height*sinv, width*sinv + height*cosv); Mat dst; warpAffine(img, dst, rot, dstSize); resize(dst, dst, Size(), scale, scale); //scale now
我发现的一个问题是warpAffine
目标图像大小设置为image.size()
而不是image.size()
rotated_img.size()
。 然而,在经纱之后,它仍然被转换得太远,不能在x和y中 …我尝试了完全相同的经纱
[ 6.123031769111886e-17 1 163.9999999999999; -1 6.123031769111886e-17 1132; 0 0 1]
从OpenCV的Matlab中的getRotationMatrix2D,它完美的工作。 我开始嗅到warpAffine
可能的错误…