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

时间:2017-07-02 11:08:12

标签: opencv

我的图像是中间的文件以及周围的一些(地毯,桌子的桌子等)

enter image description here

我想删除所有周围环境,只提供一份文件。 我尝试了阈值和adaptiveThreshold,但我没有取得多大成就。

所以我想采取周围的样本,并根据所有周围的情况删除。

这在OpenCV中是否可行?

1 个答案:

答案 0 :(得分:6)

Gerneral infos

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

一般提示

  • 轮廓尺寸可以是其长度(仅在
  • 时有效)
  

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);
}

图片

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