OpenCV 3.4.6:检测并反转图像负片矩形区域的方法求助
Hey there, let's work through this problem together. Those visible negative rectangles should be detectable with OpenCV—let's fix your approach step by step.
First: Fix the findContours Parameter Mistake
Looking at your code, there's a critical error in the findContours call:
findContours( thresholded, contours, hierarchy, CV_RETR_EXTERNAL, CV_RETR_TREE );
The fifth parameter is supposed to be the contour approximation method, not another retrieval mode. You used CV_RETR_TREE twice—replace that with CV_CHAIN_APPROX_SIMPLE (or CV_CHAIN_APPROX_NONE if you need all contour points). This alone might fix a lot of your detection issues.
Adjust Preprocessing for Negative Regions
Negative rectangles have inverted pixel values compared to their surroundings, so your current thresholding and contrast approach might not be optimal. Let's tweak the pipeline:
1. Start with Grayscale Conversion
Thresholding works best on single-channel images. Skip direct BGR thresholding—convert your original image to grayscale first:
Mat gray; cvtColor(matOriginal, gray, CV_BGR2GRAY);
2. Use Adaptive Thresholding (Instead of Global Threshold)
Global thresholds like 125 can fail if lighting is uneven. Adaptive thresholding adjusts to local pixel conditions, which is perfect for detecting distinct rectangular regions:
Mat thresholded; adaptiveThreshold(gray, thresholded, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 11, 2);
We use THRESH_BINARY_INV here because negative regions are inverted—this will make the negative rectangles white (foreground) against a black background, which is easier for contour detection.
3. Optional: Simplify Contrast Enhancement
Your CLAHE implementation is solid, but sometimes over-enhancing can introduce noise. Try testing without CLAHE first, or adjust the clip limit to a lower value (like 2) if you still need it. If you keep CLAHE, apply it to the grayscale image instead of BGR for better control.
Filter Contours to Find Rectangles
Once you have the thresholded image and corrected findContours call, you need to filter out non-rectangular contours:
std::vector<std::vector<cv::Point>> contours; std::vector<Vec4i> hierarchy; // Fixed parameters: retrieval mode + approximation method findContours(thresholded, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); // Iterate through contours to find rectangles for (size_t i = 0; i < contours.size(); i++) { // Approximate the contour to a polygon std::vector<cv::Point> approx; double epsilon = 0.02 * arcLength(contours[i], true); approxPolyDP(contours[i], approx, epsilon, true); // Check if the approximated polygon is a rectangle (4 vertices) and has enough area if (approx.size() == 4 && contourArea(contours[i]) > 100) { // Convert the polygon to a rectangle Rect rect = boundingRect(approx); // Reverse the negative region: 255 - pixel value Mat roi = matOriginal(rect); roi = 255 - roi; roi.copyTo(matOriginal(rect)); } }
epsiloncontrols how closely the approximation matches the original contour—tweak this value if rectangles are not being detected correctly.- The area filter (
>100) removes small noise contours that might be mistaken for rectangles.
Full Revised Pipeline Example
Here's how all the pieces fit together:
// Step 1: Convert to grayscale Mat gray; cvtColor(matOriginal, gray, CV_BGR2GRAY); // Step 2: Adaptive thresholding for negative regions Mat thresholded; adaptiveThreshold(gray, thresholded, 255, ADAPTIVE_THRESH_GAUSSIAN_C, THRESH_BINARY_INV, 11, 2); // Step 3: Find contours with corrected parameters std::vector<std::vector<cv::Point>> contours; std::vector<Vec4i> hierarchy; findContours(thresholded, contours, hierarchy, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_SIMPLE); // Step 4: Filter rectangles and reverse negative regions for (size_t i = 0; i < contours.size(); i++) { std::vector<cv::Point> approx; double epsilon = 0.02 * arcLength(contours[i], true); approxPolyDP(contours[i], approx, epsilon, true); if (approx.size() == 4 && contourArea(contours[i]) > 100) { Rect rect = boundingRect(approx); Mat roi = matOriginal(rect); roi = 255 - roi; roi.copyTo(matOriginal(rect)); } }
Additional Tips
- If adaptive thresholding isn't working, try
THRESH_OTSUwith global thresholding:threshold(gray, thresholded, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);Otsu's method automatically calculates the optimal threshold value. - For noisy images, add a blur step before thresholding:
GaussianBlur(gray, gray, Size(3,3), 0);this will smooth out small noise points.
内容的提问来源于stack exchange,提问作者NucciDev

