Я пытаюсь обнаружить самую большую/большую прямоугольную форму и нарисовать ограничительную рамку для обнаруженной области. В моем случае использования очень часто (и не всегда) объект, представляющий форму прямоугольника, имеет цвет белый, а фон также имеет цвет, очень похожий на белый.OpenCV точить края (края без отверстий)
Прежде чем обнаруживать контуры, я предварительно обработал изображение, чтобы обнаружить идеальный край. Моя проблема заключается в том, что я не могу точно определить края, и у меня есть много шума даже после размытия и использования «адаптивного порога» или «порога».
The original image i have used for contours detection
Я попытался другой способ обнаружить идеальный край в различных условиях освещения, не увенчались успехом.
Как обработать изображение, чтобы обнаружить идеальный край (края без отверстий) для определения контура?
Ниже приведен код, я использую
public static Mat findRectangleX(Mat original) {
Mat src = original.clone();
Mat gray = new Mat();
Mat binary = new Mat();
MatOfPoint2f approxCurve;
List<MatOfPoint> contours = new ArrayList<MatOfPoint>();
if (original.type() != CvType.CV_8U) {
Imgproc.cvtColor(original, gray, Imgproc.COLOR_BGR2GRAY);
} else {
original.copyTo(gray);
}
Imgproc.GaussianBlur(gray, gray, new Size(5,5),0);
Imgproc.adaptiveThreshold(gray, binary, 255,Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C,Imgproc.THRESH_BINARY_INV,11, 1);
//Imgproc.threshold(gray, binary,0,255,Imgproc.THRESH_BINARY_INV | Imgproc.THRESH_OTSU);
double maxArea = 0;
Imgproc.findContours(binary, contours, new Mat(),Imgproc.RETR_LIST, Imgproc.CHAIN_APPROX_SIMPLE);
for (int i = 0; i<contours.size();i++) {
MatOfPoint contour = contours.get(i);
MatOfPoint2f temp = new MatOfPoint2f(contour.toArray());
double area = Imgproc.contourArea(contour);
approxCurve = new MatOfPoint2f();
Imgproc.approxPolyDP(temp, approxCurve, Imgproc.arcLength(temp, true) * 0.03, true);
if (approxCurve.total() == 4) {
Rect rect = Imgproc.boundingRect(contours.get(i));
Imgproc.rectangle(src, rect.tl(), rect.br(), new Scalar(255, 0, 0, .8), 4);
if(maxArea < area)
maxArea = area;
}
}
Log.v(TAG, "Total contours found : " + contours.size());
Log.v(TAG, "Max area :" + maxArea);
return src;
}
Я искать подобные проблемы на StackOverflow и попробовать образец кода, но ни один из них работал для меня. Трудность, я думаю, это белый объект на белом фоне.
Как обработать изображение, чтобы обострить края для определения контура?
Как определить максимальную или большую прямоугольную форму и нарисовать прямоугольную линию для обнаруженной формы?
// Обновлено: 20/02/2017
я попробовал решение, предложенное @Nejc в следующей почте. Сегментация лучше, но у меня все еще есть отверстия в контуре, и findcontours не удается обнаружить более крупный контур. Ниже приведен код, предоставленный @Nejc и переведенный в java.
public static Mat process(Mat original){
Mat src = original.clone();
Mat hsvMat = new Mat();
Mat saturation = new Mat();
Mat sobx = new Mat();
Mat soby = new Mat();
Mat grad_abs_val_approx = new Mat();
Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
List<Mat> hsv_channels = new ArrayList<Mat>(3);
Core.split(hsvMat, hsv_channels);
Mat hue = hsv_channels.get(0);
Mat sat = hsv_channels.get(1);
Mat val = hsv_channels.get(2);
Imgproc.GaussianBlur(sat, saturation, new Size(9, 9), 2, 2);
Mat imf = new Mat();
saturation.convertTo(imf, CV_32FC1, 0.5f, 0.5f);
Imgproc.Sobel(imf, sobx, -1, 1, 0);
Imgproc.Sobel(imf, soby, -1, 0, 1);
sobx = sobx.mul(sobx);
soby = soby.mul(soby);
Mat abs_x = new Mat();
Core.convertScaleAbs(sobx,abs_x);
Mat abs_y = new Mat();
Core.convertScaleAbs(soby,abs_y);
Core.addWeighted(abs_x, 1, abs_y, 1, 0, grad_abs_val_approx);
sobx.release();
soby.release();
Mat filtered = new Mat();
Imgproc.GaussianBlur(grad_abs_val_approx, filtered, new Size(9, 9), 2, 2);
final MatOfDouble mean = new MatOfDouble();
final MatOfDouble stdev = new MatOfDouble();
Core.meanStdDev(filtered, mean, stdev);
Mat thresholded = new Mat();
Imgproc.threshold(filtered, thresholded, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_TOZERO);
/*
Mat thresholded_bin = new Mat();
Imgproc.threshold(filtered, thresholded_bin, mean.toArray()[0] + stdev.toArray()[0], 1.0, Imgproc.THRESH_BINARY);
Mat converted = new Mat();
thresholded_bin.convertTo(converted, CV_8UC1);
*/
return thresholded;
}
Вот изображение, которое я получил после выполнения кода выше
Image after using @Nejc solution
1) Почему мой транслируемый код не выводит то же изображение, как @Nejc? Тот же код, примененный к тому же изображению, должен выдавать тот же результат?
2) я что-то пропустил при переводе?
3) Для моего понимания, почему мы умножили изображение самостоятельно в этой инструкции sobx = sobx.mul (sobx); ?
Что вы подразумеваете под "идеальным краем"? Совершенно прямо? – Rethunk
затачивайте края изображения, чтобы лучше определить контуры. –