提取旋轉矩形區域圖像
OpenCV中有一個很有用的數據結構是RotatedRect,也就是旋轉的矩形。最近手上有一個需求是提取圖像中旋轉矩形的區域作爲一張單獨的圖像保存起來,但是庫裏面沒有現成的API,網上查了一下,大致是這麼一種方法:先根據矩形的角度把圖像整體旋轉到水平方向,再計算出旋轉矩形的四個點在旋轉之後的位置,也就是一個水平的矩形,可以形成一個bbox從而提取出想要的圖像。
思路沒有問題,但是網上的一些源碼在處理一些特殊情況時依舊會報錯,也就是,當矩形的長邊大於原圖像的短邊,並且矩形角度大於45度時,矩形就會進行一個大反轉,這時四個點經過旋轉變換後,會出現負座標,解決方法就是增加一個判定,在任何情況下都不讓它出現大反轉就行了。
說多了沒用,上代碼,代碼是opencvsharp版本的:
public void CutLabel(Mat input, RotatedRect rect, out Bitmap label)
{
float angle = rect.Angle;
int height = Convert.ToInt16(rect.Size.Height);
int width = Convert.ToInt16(rect.Size.Width);
if (Math.Abs(angle) > 45)
{
angle = angle + 90;
}
Point2f[] rect_points = rect.Points();
//把原圖旋轉到水平方向
Mat rotate_mat = Cv2.GetRotationMatrix2D(new OpenCvSharp.Point(input.Width / 2, input.Height / 2), angle, 1);
int heightNew = Convert.ToInt16(input.Width * Math.Abs(Math.Sin(Math.PI * (angle / 180))) +
input.Height * Math.Abs(Math.Cos(Math.PI * (angle / 180))));
int widthNew = Convert.ToInt16(input.Height * Math.Abs(Math.Sin(Math.PI * (angle / 180))) +
input.Width * Math.Abs(Math.Cos(Math.PI * (angle / 180))));
rotate_mat.Set<int>(0, 2, (widthNew - input.Width) / 2 + rotate_mat.At<int>(0, 2));
rotate_mat.Set<int>(1, 2, (heightNew - input.Height) / 2 + rotate_mat.At<int>(1, 2));
Mat img_rotate = new Mat();
Cv2.WarpAffine(input, img_rotate, rotate_mat, new OpenCvSharp.Size(widthNew, heightNew));
List<Point2f> rotatedRectPoints = new List<Point2f>();
//旋轉矩形的四個頂點
Cv2.Transform(InputArray.Create(rect_points), OutputArray.Create(rotatedRectPoints), rotate_mat);
//提取矩形區域
Rect bbox = Cv2.BoundingRect(rotatedRectPoints);
Mat ret = new Mat();
img_rotate[bbox].CopyTo(ret);
if (ret.Width < ret.Height)
{
Mat ret_trans = new Mat();
Mat ret_flip = new Mat();
Cv2.Transpose(ret, ret_trans);
Cv2.Flip(ret_trans, ret_flip, FlipMode.X);
label = BitmapConverter.ToBitmap(ret_flip);
}
else
label = BitmapConverter.ToBitmap(ret);
}
這裏再解釋一下RotatedRect中angle的含義,查資料的過程中看了一篇博文講得很好,它是將x軸按逆時針旋轉,碰到的第一根矩形的邊的角度,所以angle的範圍是[-90,0],並不是想象中的長邊與水平方向的夾角。這裏我其實是有點懵逼的,因爲在後面的計算旋轉矩陣時,角度爲正表示的是逆時針旋轉,所以正負號不要亂加。。。。
判斷指定點是否在給定的矩形區域內
opencv有一個函數叫PointPolygonTest()可以用來判斷一個點是否在contour內。但是這裏我們需要的是判斷是否在RotatedRect內,那麼就需要把這個矩形轉換爲contour。怎麼轉換呢?c++裏面可以參考OpenCV中判斷點在矩形中的方法。那opencvsharp裏面怎麼搞??我想的是把矩形區域繪製出來,然後重新計算一遍contour....這裏的數據轉換可把我整懵了.....具體實現方法如下:
RotatedRect rect = Cv2.MinAreaRect(contours[i]);
Mat rect_img = new Mat(img.Size(), MatType.CV_8UC1, 0);
OpenCvSharp.Point[] pts = new OpenCvSharp.Point[]
{
new OpenCvSharp.Point(rect.Points()[0].X,rect.Points()[0].Y),
new OpenCvSharp.Point(rect.Points()[1].X,rect.Points()[1].Y),
new OpenCvSharp.Point(rect.Points()[2].X,rect.Points()[2].Y),
new OpenCvSharp.Point(rect.Points()[3].X,rect.Points()[3].Y),
};
OpenCvSharp.Point[][] pts1 = new OpenCvSharp.Point[][] { pts};
Cv2.FillPoly(rect_img, pts1, 255);
OpenCvSharp.Point[][] contours_rect;
HierarchyIndex[] hierarchy;
Cv2.FindContours(rect_img, out contours_rect, out hierarchy, RetrievalModes.External, ContourApproximationModes.ApproxNone);
double score = Cv2.PointPolygonTest(contours_rect[0], index, true);
如果有大佬知道更簡單的轉換方法,求告知呀~~~~