从图像中删除 Boxes/rectangles
Remove Boxes/rectangles from image
我有下图。
this image
我想去掉数字周围的橙色 boxes/rectangle 并保持原始图像干净,没有任何橙色 grid/rectangle。
下面是我当前的代码,但没有删除它。
Mat mask = new Mat();
Mat src = new Mat();
src = Imgcodecs.imread("enveloppe.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
Scalar lowerThreshold = new Scalar(0, 50, 50);
Scalar upperThreshold = new Scalar(25, 255, 255);
Mat mask = new Mat();
Core.inRange(hsvMat, lowerThreshold, upperThreshold, mask);
//src.setTo(new scalar(255,255,255),mask);
what to do next ?
如何从原始图像中删除橙色 boxes/rectangle?
更新:
有关信息,掩码完全包含我要删除的所有 boxes/rectangle。我不知道如何使用此掩码从源 (src) 图像中删除 boxes/rectangle,就好像它们不存在一样。
这就是我解决问题的方法。我用 C++ 解决了这个问题,我使用了 OpenCV。
第 1 部分:查找候选框
首先,我想隔离特定于红色通道的信号。我将图像分成三个通道。然后我从蓝色通道中减去红色通道,从绿色通道中减去红色通道。之后,我将之前的两个减法结果相互减去。最终减法结果如下图所示
using namespace cv;
using namespace std;
Mat src_rgb = imread("image.jpg");
std::vector<Mat> channels;
split(src_rgb, channels);
Mat diff_rb, diff_rg;
subtract(channels[2], channels[0], diff_rb);
subtract(channels[2], channels[1], diff_rg);
Mat diff;
subtract(diff_rb, diff_rg, diff);
我的下一个目标是将获得的图像的各个部分分成单独的 "groups"。为此,我使用高斯滤波器对图像进行了一些平滑处理。然后我应用了一个阈值来获得二值图像;最后我在该图像中寻找外部轮廓。
GaussianBlur(diff, diff, cv::Size(11, 11), 2.0, 2.0);
threshold(diff, diff, 5, 255, THRESH_BINARY);
vector<vector<Point>> contours;
findContours(diff, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
Click to see subtraction result, Gaussian blurred image, thresholded image and detected contours.
第 2 部分:检查候选框
在那之后,我必须估计每个轮廓的内部是否包含数字或其他内容。我假设数字总是用黑色墨水打印,并且它们会有锋利的边缘。因此,我拍了一张蓝色通道图像,我只应用了一点高斯平滑,并用拉普拉斯算子对其进行了卷积。
Mat blurred_ch2;
GaussianBlur(channels[2], blurred_ch2, cv::Size(7, 7), 1, 1);
Mat laplace_result;
Laplacian(blurred_ch2, laplace_result, -1, 1);
然后我拍摄了生成的图像,并对每个轮廓分别应用了以下过程。我计算了轮廓内部像素值的标准差。围绕数字的等高线内的标准偏差很高;它在围绕着狗头的两个轮廓和邮票顶部的字母内。
这就是我可以应用标准偏差阈值的原因。标准偏差约为。包含数字的等高线大两倍,因此这是仅 select 包含数字的等高线的简单方法。然后我画了轮廓内部面具。我使用腐蚀和减法来获得 "box edge mask".
最后一步相当简单。我计算了图像每个通道上框附近的平均像素值的估计值。然后我将 "box edge mask" 下的所有像素值更改为每个通道上的那些值。在对每个框轮廓重复该过程后,我将所有三个通道合并为一个。
Mat mask(src_rgb.size(), CV_8UC1);
for (int i = 0; i < contours.size(); ++i)
{
mask.setTo(0);
drawContours(mask, contours, i, cv::Scalar(200), -1);
Scalar mean, stdev;
meanStdDev(laplace_result, mean, stdev, mask);
if (stdev.val[0] < 10.0) continue;
Mat eroded;
erode(mask, eroded, cv::Mat(), cv::Point(-1, -1), 6);
subtract(mask, eroded, mask);
for (int c = 0; c < src_rgb.channels(); ++c)
{
erode(mask, eroded, cv::Mat());
subtract(mask, eroded, eroded);
Scalar mean, stdev;
meanStdDev(channels[c], mean, stdev, eroded);
channels[c].setTo(mean, mask);
}
}
Mat final_result;
merge(channels, final_result);
imshow("Final Result", final_result);
Click to see red channel of the image, the result of convolution with Laplacian operator, drawn mask of the box edges and the final result.
请注意
这段代码远非最佳,尤其是最后一个循环做了很多不必要的工作。但我认为在这种情况下可读性更重要(而且问题的作者也没有要求优化的解决方案)。
寻找更通用的解决方案
在我发布初始回复后,问题的作者指出数字可以是任何颜色并且它们的边缘不一定是尖锐的。这意味着上述过程可能由于各种原因而失败。 I altered the input image so that it contains different kinds of numbers (click to see the image) 你可以 运行 我的算法在此输入上分析哪里出了问题。
在我看来,需要这些方法中的一种(或者可能是两者的混合)来获得更 "general" 的解决方案:
- 只关注矩形形状和颜色(确认候选框确实是橙色框,不管里面是什么,都将其删除)
- 只关注数字(运行每个候选框内部的适当数字检测算法;如果它包含单个数字,则删除框)
我将给出第一种方法的一个简单示例。如果您可以假设橙色框的大小始终相同,只需检查框的大小而不是算法最后一个循环中信号的标准差:
Rect rect = boundingRect(contours[i]);
float area = rect.area();
if (area < 1000 || area > 1200) continue;
警告:矩形的实际面积约为 600Px^2,但我考虑了高斯模糊,导致轮廓扩大。另请注意,如果您使用此方法,则不再需要对蓝色通道图像执行模糊或拉普拉斯操作。
您还可以为该条件添加其他简单的约束;宽度和高度之间的比例是我想到的第一个。几何属性也是一个不错的选择(直角、直边、凸度……)。
我有下图。 this image
我想去掉数字周围的橙色 boxes/rectangle 并保持原始图像干净,没有任何橙色 grid/rectangle。
下面是我当前的代码,但没有删除它。
Mat mask = new Mat();
Mat src = new Mat();
src = Imgcodecs.imread("enveloppe.jpg",Imgcodecs.CV_LOAD_IMAGE_COLOR);
Imgproc.cvtColor(src, hsvMat, Imgproc.COLOR_BGR2HSV);
Scalar lowerThreshold = new Scalar(0, 50, 50);
Scalar upperThreshold = new Scalar(25, 255, 255);
Mat mask = new Mat();
Core.inRange(hsvMat, lowerThreshold, upperThreshold, mask);
//src.setTo(new scalar(255,255,255),mask);
what to do next ?
如何从原始图像中删除橙色 boxes/rectangle?
更新: 有关信息,掩码完全包含我要删除的所有 boxes/rectangle。我不知道如何使用此掩码从源 (src) 图像中删除 boxes/rectangle,就好像它们不存在一样。
这就是我解决问题的方法。我用 C++ 解决了这个问题,我使用了 OpenCV。
第 1 部分:查找候选框
首先,我想隔离特定于红色通道的信号。我将图像分成三个通道。然后我从蓝色通道中减去红色通道,从绿色通道中减去红色通道。之后,我将之前的两个减法结果相互减去。最终减法结果如下图所示
using namespace cv;
using namespace std;
Mat src_rgb = imread("image.jpg");
std::vector<Mat> channels;
split(src_rgb, channels);
Mat diff_rb, diff_rg;
subtract(channels[2], channels[0], diff_rb);
subtract(channels[2], channels[1], diff_rg);
Mat diff;
subtract(diff_rb, diff_rg, diff);
我的下一个目标是将获得的图像的各个部分分成单独的 "groups"。为此,我使用高斯滤波器对图像进行了一些平滑处理。然后我应用了一个阈值来获得二值图像;最后我在该图像中寻找外部轮廓。
GaussianBlur(diff, diff, cv::Size(11, 11), 2.0, 2.0);
threshold(diff, diff, 5, 255, THRESH_BINARY);
vector<vector<Point>> contours;
findContours(diff, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);
Click to see subtraction result, Gaussian blurred image, thresholded image and detected contours.
第 2 部分:检查候选框
在那之后,我必须估计每个轮廓的内部是否包含数字或其他内容。我假设数字总是用黑色墨水打印,并且它们会有锋利的边缘。因此,我拍了一张蓝色通道图像,我只应用了一点高斯平滑,并用拉普拉斯算子对其进行了卷积。
Mat blurred_ch2;
GaussianBlur(channels[2], blurred_ch2, cv::Size(7, 7), 1, 1);
Mat laplace_result;
Laplacian(blurred_ch2, laplace_result, -1, 1);
然后我拍摄了生成的图像,并对每个轮廓分别应用了以下过程。我计算了轮廓内部像素值的标准差。围绕数字的等高线内的标准偏差很高;它在围绕着狗头的两个轮廓和邮票顶部的字母内。
这就是我可以应用标准偏差阈值的原因。标准偏差约为。包含数字的等高线大两倍,因此这是仅 select 包含数字的等高线的简单方法。然后我画了轮廓内部面具。我使用腐蚀和减法来获得 "box edge mask".
最后一步相当简单。我计算了图像每个通道上框附近的平均像素值的估计值。然后我将 "box edge mask" 下的所有像素值更改为每个通道上的那些值。在对每个框轮廓重复该过程后,我将所有三个通道合并为一个。
Mat mask(src_rgb.size(), CV_8UC1);
for (int i = 0; i < contours.size(); ++i)
{
mask.setTo(0);
drawContours(mask, contours, i, cv::Scalar(200), -1);
Scalar mean, stdev;
meanStdDev(laplace_result, mean, stdev, mask);
if (stdev.val[0] < 10.0) continue;
Mat eroded;
erode(mask, eroded, cv::Mat(), cv::Point(-1, -1), 6);
subtract(mask, eroded, mask);
for (int c = 0; c < src_rgb.channels(); ++c)
{
erode(mask, eroded, cv::Mat());
subtract(mask, eroded, eroded);
Scalar mean, stdev;
meanStdDev(channels[c], mean, stdev, eroded);
channels[c].setTo(mean, mask);
}
}
Mat final_result;
merge(channels, final_result);
imshow("Final Result", final_result);
Click to see red channel of the image, the result of convolution with Laplacian operator, drawn mask of the box edges and the final result.
请注意
这段代码远非最佳,尤其是最后一个循环做了很多不必要的工作。但我认为在这种情况下可读性更重要(而且问题的作者也没有要求优化的解决方案)。
寻找更通用的解决方案
在我发布初始回复后,问题的作者指出数字可以是任何颜色并且它们的边缘不一定是尖锐的。这意味着上述过程可能由于各种原因而失败。 I altered the input image so that it contains different kinds of numbers (click to see the image) 你可以 运行 我的算法在此输入上分析哪里出了问题。
在我看来,需要这些方法中的一种(或者可能是两者的混合)来获得更 "general" 的解决方案:
- 只关注矩形形状和颜色(确认候选框确实是橙色框,不管里面是什么,都将其删除)
- 只关注数字(运行每个候选框内部的适当数字检测算法;如果它包含单个数字,则删除框)
我将给出第一种方法的一个简单示例。如果您可以假设橙色框的大小始终相同,只需检查框的大小而不是算法最后一个循环中信号的标准差:
Rect rect = boundingRect(contours[i]);
float area = rect.area();
if (area < 1000 || area > 1200) continue;
警告:矩形的实际面积约为 600Px^2,但我考虑了高斯模糊,导致轮廓扩大。另请注意,如果您使用此方法,则不再需要对蓝色通道图像执行模糊或拉普拉斯操作。
您还可以为该条件添加其他简单的约束;宽度和高度之间的比例是我想到的第一个。几何属性也是一个不错的选择(直角、直边、凸度……)。