Xử lý ảnh trong 1 vùng đa giác nhất định giúp cho bạn hạn chế detect nhầm và tăng tốc độ xử lý. Bài viết này hướng dẫn bạn cách crop (hoặc extract) vùng ROI hình đa giác (polygon).
Bài viết này giả sử rằng vùng ROI là tứ giác lồi, có sự hợp tác của người dùng để cho ra tứ giác bình thường. Tứ giác bình thường là tứ giác lồi, các điểm được sắp xếp tuần tự.
Lý thuyết
Giả sử bạn muốn crop vùng ROI là vùng xe đi vào
Để crop được hình ảnh bạn cần làm 3 bước sau:
Bước 1: crop ảnh theo diện tích hình chữ nhật nhỏ nhất (bouding rect) chứa đa giác.
Bước 2: tạo mask (ảnh binary) và fill màu trắng (255) từ ảnh đã crop.
Bước 3: dùng hàm bitwise_and() để lấy những pixel từ ảnh đã crop nếu trong ảnh mask pixel đó có màu trắng. Vậy là ta được kết quả như hình dưới.
Nếu bạn muốn crop vùng ROI có nền màu trắng thì bạn dùng bitwise_not() để nghịch đảo màu của mask. Sau đó ghép vào ảnh trên là được.
Source code
Bài viết này cung cấp source code cả 3 ngôn ngữ cho bạn
1. Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import numpy as np import cv2 img = cv2.imread("2024-09-08__08-44-01.png") pts = np.array([[55,927],[687,227],[1153,247],[1085,1055]]) ## (1) Crop the bounding rect rect = cv2.boundingRect(pts) x,y,w,h = rect croped = img[y:y+h, x:x+w].copy() ## (2) make mask pts = pts - pts.min(axis=0) mask = np.zeros(croped.shape[:2], np.uint8) cv2.drawContours(mask, [pts], -1, (255, 255, 255), -1, cv2.LINE_AA) ## (3) do bit-op dst = cv2.bitwise_and(croped, croped, mask=mask) ## (4) add the white background bg = np.ones_like(croped, np.uint8)*255 cv2.bitwise_not(bg,bg, mask=mask) dst2 = bg+ dst cv2.imwrite("croped.png", croped) cv2.imwrite("mask.png", mask) cv2.imwrite("dst.png", dst) cv2.imwrite("dst2.png", dst2) |
2. C++
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
cv::Mat CropQuadrilateral(cv::Mat frame, std::vector<cv::Point> points) { // (1) Crop the bounding rect cv::Rect rect = cv::boundingRect(points); cv::Mat cropped = frame(rect).clone(); // (2) Make mask for (auto& point : points) { point -= rect.tl(); // Adjust the points to the cropped region } cv::Mat mask = cv::Mat::zeros(cropped.size(), CV_8UC1); cv::drawContours(mask, std::vector<std::vector<cv::Point>>{points}, -1, cv::Scalar(255, 255, 255), cv::FILLED, cv::LINE_AA); // (3) Do bitwise operation cv::Mat dst; cv::bitwise_and(cropped, cropped, dst, mask); return dst; } |
Cách gọi hàm:
1 2 3 |
std::vector<cv::Point> pts = {cv::Point(55,927), cv::Point(687,227), cv::Point(1153,247), cv::Point(1085,1055)}; cv::Mat img = cv::imread("2024-09-05__08-51-52.jpg"); cv::Mat cropped = CropQuadrilateral(img, pts); |
3. C#
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
using System.Drawing; public static Bitmap CropToPolygon(Bitmap source, List<Point> polygon) { // Create a new empty bitmap with the same size as the source Bitmap croppedBitmap = new Bitmap(source.Width, source.Height); // Create a GraphicsPath from the polygon points using (GraphicsPath path = new GraphicsPath()) { path.AddPolygon(polygon.ToArray()); // Create a Region from the path using (Region region = new Region(path)) { // Create the Graphics object for the new bitmap using (Graphics g = Graphics.FromImage(croppedBitmap)) { // Clear the new bitmap with transparent background g.Clear(Color.Transparent); // Set the clipping region to the polygon g.SetClip(region, CombineMode.Replace); // Draw the source image onto the new bitmap using the clipping region g.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height)); } } } // Crop the resulting bitmap to the bounding rectangle of the polygon Rectangle boundingRect = GetPolygonBoundingBox(polygon); Bitmap finalCroppedBitmap = croppedBitmap.Clone(boundingRect, croppedBitmap.PixelFormat); return finalCroppedBitmap; } private static Rectangle GetPolygonBoundingBox(List<Point> polygon) { int minX = int.MaxValue; int minY = int.MaxValue; int maxX = int.MinValue; int maxY = int.MinValue; foreach (var point in polygon) { if (point.X < minX) minX = point.X; if (point.Y < minY) minY = point.Y; if (point.X > maxX) maxX = point.X; if (point.Y > maxY) maxY = point.Y; } return new Rectangle(minX, minY, maxX - minX, maxY - minY); } |
Cách gọi hàm
1 2 3 4 5 6 7 8 9 10 11 |
Bitmap bmp = new Bitmap("2024-09-08__08-44-01.png"); List<Point> polygon = new List<Point>(); polygon.Add(new Point(55, 927)); polygon.Add(new Point(687, 227)); polygon.Add(new Point(1153, 247)); polygon.Add(new Point(1085, 1055)); Bitmap croped = TGMTimage.CropToPolygon(bmp, polygon); croped.Save("croped.jpg", ImageFormat.Jpeg);<img src="https://thigiacmaytinh.com/wp-content/uploads/2024/09/crop_roi.jpg" alt="" width="1228" height="479" class="alignnone size-full wp-image-6544" /><img src="https://thigiacmaytinh.com/wp-content/uploads/2024/09/crop_roi.jpg" alt="" width="1068" height="478" class="alignnone size-full wp-image-6546" /> |