背景颜色图像分割 - OpenCV Android

6

我正在尝试将名片分段并按背景颜色拆分,以将它们视为不同的感兴趣区域。

例如,这样的一张卡片: Sample business card

应该能够被拆分成两个图像,因为有两种背景颜色。有什么建议来解决这个问题吗?我尝试了一些轮廓分析,但效果不太好。

其他示例卡: enter image description here

这张卡应该会给出3个分割,因为有三个部分,即使只有2种颜色(虽然2种颜色也可以)。

enter image description here

以上卡片应该只给出一个分割,因为它只有一个背景颜色。

我还没有考虑渐变背景。


SalGad 或者有人能帮我解决这个问题吗,我真的需要做这个。https://stackoverflow.com/questions/61876457/image-segmentation-using-segment-seeds-watershed-in-android - Tecnologia da Net
2个回答

9
这取决于其他卡片的外观,但如果所有图像都是如此出色,那就不应该太难。在您发布的示例中,您可以收集边框像素(最左列,最右列,第一行,最后一行)的颜色,并将找到的内容视为可能的背景颜色。也许检查是否有足够数量的大致相同颜色的像素。您需要某种距离测量方法,在RGB颜色空间中使用欧几里得距离只是一个简单的解决方案。更通用的解决方案是查找整个图像颜色直方图中的聚类,并将每个颜色(再次带有容差)视为具有超过x%总像素量的背景颜色。但是,您定义的背景取决于您想要实现的内容以及您的图像外观。如果您需要进一步的建议,可以发布更多图像并标记您希望检测为背景颜色的图像部分以及不需要的部分。

-

编辑:您的两张新图片也显示了相同的模式。背景颜色占据了图像的大部分,没有噪点,也没有颜色渐变。因此,一个简单的方法可能如下所示: 如果您有不适用于此方法的示例,请发布它们。

我已经添加了几张更多的示例卡片。如果可能的话,你能否将你的答案更多地与OpenCV相关联? - SalGad
第一张图片的背景似乎有轻微的渐变,并且在柱状图中出现了几个峰值... 对此有什么建议吗? - SalGad
好的。我一开始没有看到这个。我只看了第二张图的直方图,我猜是那种情况下我的方法是无用的。你已经尝试过像这个教程中的常规轮廓查找吗?http://docs.opencv.org/doc/tutorials/imgproc/shapedescriptors/find_contours/find_contours.html 你只需要添加一个参数来保留你想要保留的轮廓的最小大小。你可以使用cv::contourArea(http://docs.opencv.org/modules/imgproc/doc/structural_analysis_and_shape_descriptors.html#contourarea)获取面积,并将其除以图像中的像素数。 - Tobias Hermann
我会标记您的答案并将赏金传递给您,因为到目前为止您真的很有帮助。 - SalGad
@TobiasHermann 或者有人能帮我解决这个问题吗?我真的需要做这个。https://stackoverflow.com/questions/61876457/image-segmentation-using-segment-seeds-watershed-in-android - Tecnologia da Net
显示剩余5条评论

0
作为一种寻找带有颜色渐变背景的方法,可以使用Canny算法。以下代码(是的,不是Android,但如果您移植它,结果应该是相同的)可以很好地处理到目前为止您发布的三个示例图像。如果您有其他无法使用此代码的图像,请告诉我。
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

Mat src;
Mat src_gray;
int canny_thresh = 100;
int max_canny_thresh = 255;
int size_per_mill = 120;
int max_size_per_mill = 1000;
RNG rng(12345);

bool cmp_contour_area_less(const vector<Point>& lhs, const vector<Point>& rhs)
{
    return contourArea(lhs) < contourArea(rhs);
}

void Segment()
{
    Mat canny_output;
    vector<vector<Point> > contours;
    vector<Vec4i> hierarchy;

    Canny(src_gray, canny_output, canny_thresh, canny_thresh*2, 3);

    // Draw rectangle around canny image to also get regions touching the edges.
    rectangle(canny_output, Point(1, 1), Point(src.cols-2, src.rows-2), Scalar(255));
    namedWindow("Canny", CV_WINDOW_AUTOSIZE);
    imshow("Canny", canny_output);

    // Find the contours.
    findContours(canny_output, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));

    // Remove largest Contour, because it represents always the whole image.
    sort(contours.begin(), contours.end(), cmp_contour_area_less);
    contours.resize(contours.size()-1);
    reverse(contours.begin(), contours.end());

    // Maximum contour size.
    int image_pixels(src.cols * src.rows);
    cout << "image_pixels: " << image_pixels << "\n";

    // Filter the contours, leaving just large enough ones.
    vector<vector<Point> > background_contours;
    for(size_t i(0); i < contours.size(); ++i)
    {
        double area(contourArea(contours[i]));
        double min_size((size_per_mill / 1000.0) * image_pixels);
        if (area >= min_size)
        {
            cout << "Background contour " << i << ") area: " << area << "\n";
            background_contours.push_back(contours[i]);
        }
    }

    // Draw large contours.
    Mat drawing = Mat::zeros(canny_output.size(), CV_8UC3);
    for(size_t i(0); i < background_contours.size(); ++i)
    {
        Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0,255), rng.uniform(0,255));
        drawContours(drawing, background_contours, i, color, 1, 8, hierarchy, 0, Point());
    }

    namedWindow("Contours", CV_WINDOW_AUTOSIZE);
    imshow("Contours", drawing);
}

void size_callback(int, void*)
{
    Segment();
}

void thresh_callback(int, void*)
{
    Segment();   
}

int main(int argc, char* argv[])
{
    if (argc != 2)
    {
        cout << "Please provide an image file.\n";
        return -1;
    }

    src = imread(argv[1]);

    cvtColor(src, src_gray, CV_BGR2GRAY);
    blur(src_gray, src_gray, Size(3,3));

    namedWindow("Source", CV_WINDOW_AUTOSIZE);
    imshow("Source", src);

    if (!src.data)
    {
        cout << "Unable to load " << argv[1] << ".\n";
        return -2;
    }

    createTrackbar("Canny thresh:", "Source", &canny_thresh, max_canny_thresh, thresh_callback);
    createTrackbar("Size thresh:", "Source", &size_per_mill, max_size_per_mill, thresh_callback);

    Segment();
    waitKey(0);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接