回転a1296x968画像を90度OpenCVのC++ APIを使用していて、いくつかの問題に直面しています。
入力:
回転:
ご覧のとおり、回転した画像にはいくつかの問題があります。最初に、元のサイズを反転した宛先Mat
を具体的に作成しましたが、元のサイズと同じです。その結果、宛先イメージがトリミングされます。
これは、warpAffine()
を呼び出して、宛先Mat
のサイズではなく、元のMat
のサイズを渡しているために発生していると思います。 私は この答えをフォローしたので、これを行っていますが、今は答えが間違っているのではないかと疑っています。これが私の最初の疑問/問題です。
2つ目は、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;
}
私は解決策を見つけましたそれは関与しない warpAffine()
です。
しかし、その前に、私の疑惑が正しかったことを(将来の参考のために)述べる必要があります。 warpAffine()
を呼び出すときに、宛先のサイズを渡す必要がありました。
_warpAffine(image, rotated_img, rot_matrix, rotated_img.size());
_
私の知る限り、この関数によって描かれた黒い境界線(オフセットでの書き込みが原因)は、標準の動作のようです。 MacとLinuxで動作するOpenCVのCインターフェースとCインターフェースで、バージョン2.3.1aと2.3.0を使用してこれに気づきました。
私が最終的に使用したソリューションははるかに簡単これよりもワープなものです。 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>
多くの人がオフセットなどのために画像または画像チャンクの回転に問題を抱えています。そこで、画像の領域(または全体)を回転させて別の画像に貼り付けたり、機能を使用できるようにするソリューションを投稿しますすべてがぴったり合う画像を計算します。
// 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)*p.x + R.at<double>(0,1)*p.y + R.at<double>(0,2));
rp.y = (float)(R.at<double>(1,0)*p.x + R.at<double>(1,1)*p.y + 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);
}
他のより速い解決策を見つけたと思います(90度の回転は非常に速く、warpAffineのすべての機構は必要ありません)。しかし、これに遭遇した他の人のために黒い境界の問題に対処したいと思います。
WarpAffineは他に何ができますか?宛先の画像は高さよりも幅が広く指定され、アフィン変換は拡大縮小ではなく、指定された回転(画像の中心の周り)のみを指定しました。それはまさにそれがしたことです。 warpAffineにこれらの黒い境界線に何を描画する必要があるかを伝える情報はどこにもないため、黒いままにしておきました。
直接の物理実験:テーブルの上にシートを置きます。周囲に輪郭を描きます(これは、結果をオリジナルと同じ形状/サイズにしたいことを指定したときに行った操作です)。次に、そのシートをその中心を中心に90度回転させます。テーブルのアウトラインで囲まれた領域を見てください。黒いテーブルの場合は、結果とまったく同じになります。
多分これは誰かを助けることができます。
変数は
img:元の画像
角度:度
規模
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
私が見つけた1つの問題は、warpAffine
の宛先イメージサイズが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
...でバグの匂いがし始めています。