OpenCV:从图像中删除周围环境

Tom*_*rik 1 opencv

我的图像在中间一个文件,周围有一些(地毯,桌子的桌子等)

在此输入图像描述

我想删除所有周围,只有一个文件. 我尝试了阈值和adaptiveThreshold,但我没有取得多大成就.

因此,我想采取周围的样本,并基于删除所有周围.

这在OpenCV中是否可行?

Bai*_*aiz 7

Gerneral的相关信息

  • 当前场景中的背景可以解释为噪声
  • 整个图像没有均匀照明(左侧比右侧更亮).如果我们想要使用阈值,这会使事情变得复杂.
  • 该文档具有非常明显的颜色(灰色/白色)
  • 该文件基本上是同质的
  • 文档的阴影可能会导致问题
  • 如果场景/背景可以改变,这个问题变得更难解决

一般提示

  • 轮廓尺寸可以是它的长度(这只适用于

CV :: ContourApproximationModes :: CHAIN_APPROX_NONE

使用)或其区域(只绘制填充的轮廓并计算绘制的像素 - 这是提取区域信息的最可靠方法).

解决问题

有多种方法可以解决这个特定问题:

  • 阈值,应用形态学,轮廓搜索,选择最大轮廓.不需要包含背景的小图像(除了初始参数化之外).

  • 噪声检测(滤波和未滤波图像之间的简单差异,标准偏差的每像素邻域分析等 - 定义最大噪声阈值,然后应用cv :: threshold())进行背景分类,然后对前景进行分类,应用形态,执行轮廓搜索,选择最大轮廓.背景的样本图像可用于确定初始参数猜测的阈值.

  • 边缘检测(Sobel/Scharr /等 - 定义最大边缘强度的阈值,然后应用cv :: threshold())进行背景分类,然后对前景进行分类,应用形态学,执行轮廓搜索,选择最大轮廓.背景的样本图像可用于确定初始参数猜测的阈值.

  • 背景(褐色)或前景(灰色/白色)的颜色检测/分类,应用形态学,执行轮廓搜索,选择最大轮廓.颜色检测应在HSV或LAB色彩空间中进行.为了获得稳健的方法,颜色检测应该忽略亮度的差异(否则文档的阴影可能不被识别为背景),同时应该从颜色分类中完全排除太暗和太亮的像素.背景的样本图像可以用于确定典型的背景颜色,但由于背景中存在浅灰色调,并且对象本身是灰色/白色,因此应该手动提取颜色.


一般来说解决这个问题是相当困难的.例如,如果您使用颜色检测来查找灰色/白色文档但将场景更改为灰色且不如文档亮的背景,则颜色检测和分类将失败,因为我们将使用灰色来检测背景作为前景.在这种情况下,阈值方法将更好地工作.

因此,完全自动化的过程将很难实现.我认为你必须使背景分类策略可以互换,这样你就可以轻松地改变它以使其适合场景.当然,可以实现某种启发式算法,例如,可以分析来自背景的样本图像的噪声或标准偏差等.如果存在大量噪声,则噪声检测/分类的方法是可行的.如果几乎没有噪声但是与文档不同的原始亮度或颜色,则阈值或颜色检测/分类方法最适合.

以下是完全提取文档的第一种方法(简单阈值)的一些代码:

void drawRotatedRect(cv::Mat& drawing, cv::RotatedRect& rr, cv::Scalar color)
{
    cv::Point2f points[4];
    rr.points(points);
    for (int j = 0; j < 4; j++)
    {
        cv::line(drawing, points[j], points[(j + 1) % 4], color, 1, 8);
    }
}

// Adapted from http://answers.opencv.org/question/14807/fill-an-image-with-the-content-of-rotatedrect/
void drawRotatedRectFilled(cv::Mat& image, cv::RotatedRect rRect, cv::Scalar color) 
{
    cv::Point2f vertices2f[4];
    cv::Point vertices[4];
    rRect.points(vertices2f);
    for (int i = 0; i < 4; ++i) 
    {
        vertices[i] = vertices2f[i];
    }
    cv::fillConvexPoly(image, vertices, 4, color);
}

void testDocumentExtraction()
{
    // Load image
    std::string path = "./Testdata/Stackoverflow 1/";
    std::string filename = "1.jpg";
    std::string fqn = path + filename;
    cv::Mat img = cv::imread(fqn, cv::IMREAD_COLOR);

    auto imageSize = img.size();

    // Convert to gray
    cv::Mat imgGray;
    cv::cvtColor(img, imgGray, CV_BGR2GRAY);

    // Threshold with OTSU
    cv::Mat imgBin;
    int thresholdFlags = cv::ThresholdTypes::THRESH_BINARY + cv::ThresholdTypes::THRESH_OTSU;
    cv::threshold(imgGray, imgBin, 0.0, 255.0, thresholdFlags);

    // Morph
    int erosionSize = 3;
    int erosionType = cv::MORPH_RECT;
    cv::Mat element = cv::getStructuringElement(erosionType,
        cv::Size(2 * erosionSize + 1, 2 * erosionSize + 1),
        cv::Point(erosionSize, erosionSize));

    int nbMorphIterations = 2;
    cv::Mat imgMorphed;
    imgBin.copyTo(imgMorphed);
    for (int i = 0; i < nbMorphIterations; ++i)
    {
        cv::erode(imgMorphed, imgMorphed, element);
    }

    for (int i = 0; i < 4; ++i)
    {
        cv::dilate(imgMorphed, imgMorphed, element);
        cv::erode(imgMorphed, imgMorphed, element);
    }

    // Find contours.
    // TODO: if we find more than one, use the largest one
    std::vector<std::vector<cv::Point>> contours;
    cv::Mat tempMat;
    imgMorphed.copyTo(tempMat);
    cv::findContours(tempMat, contours, cv::RETR_EXTERNAL, cv::ContourApproximationModes::CHAIN_APPROX_SIMPLE);
    // Get contour of our (potential) object
    auto contourOfObject = contours.at(0);
    // Build object oriented bounding box (since the object is rectangular but potentially rotated).
    auto oobb = cv::minAreaRect(contourOfObject);

    // Draw contour or rotated rect filled to get a mask for the object.
    // To have smoother roi borders, use rotated rect
    cv::Mat maskForObject = cv::Mat::zeros(imageSize, CV_8U);
    //cv::drawContours(maskForObject, contours, 0, cv::Scalar(255), cv::FILLED);
    drawRotatedRectFilled(maskForObject, oobb, cv::Scalar(255));

    // Draw the rotated rect in red color
    cv::Mat drawing;
    img.copyTo(drawing);
    drawRotatedRect(drawing, oobb, cv::Scalar(0, 0, 255));

    // Copy only the object from input image
    cv::Mat imgWithOnlyTheObject = cv::Mat::zeros(imageSize, CV_8UC3);
    img.copyTo(imgWithOnlyTheObject, maskForObject);

    // Show all the stuff
    cv::imshow("img", img);
    cv::imshow("maskForObject", maskForObject);
    cv::imshow("imgWithOnlyTheObject", imgWithOnlyTheObject);
    cv::imshow("imgGray", imgGray);
    cv::imshow("imgBin", imgBin);
    cv::imshow("imgMorphed", imgMorphed);
    cv::imshow("drawing", drawing);

    cv::waitKey(0);
}
Run Code Online (Sandbox Code Playgroud)

图片

彩色图像 1 灰色图像 2 阈值图像 3 变形图像 4 面向对象的最大轮廓边界框 五 面向对象边界框的对象蒙版 6 从原始图像中提取的对象 7

  • 非常好解释(+1) (2认同)