OpenCV:从图像中移除周围环境 我有一张图片,中间有一个文件,周围有地毯,桌子的桌子等。

OpenCV:从图像中移除周围环境 我有一张图片,中间有一个文件,周围有地毯,桌子的桌子等。,opencv,Opencv,我想删除所有周围的,只有一个文件。 我尝试了阈值和自适应阈值,但没有取得多大成就 所以我想采集周围环境的样本,并在此基础上移除所有周围环境 这在OpenCV中可能吗?GerGeneral infos 当前场景中的背景可以解释为噪声 整体图像照明不均匀左侧比右侧亮。如果我们想使用阈值,这会使事情复杂化。 文档具有非常明显的灰色/白色 这份文件大体上是同质的 文档的阴影可能会导致问题 如果场景/背景可以改变,那么这个问题就更难解决了 一般提示 轮廓大小可以是其长度,只有在以下情况下才有效 cv::轮

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

所以我想采集周围环境的样本,并在此基础上移除所有周围环境

这在OpenCV中可能吗?

GerGeneral infos 当前场景中的背景可以解释为噪声 整体图像照明不均匀左侧比右侧亮。如果我们想使用阈值,这会使事情复杂化。 文档具有非常明显的灰色/白色 这份文件大体上是同质的 文档的阴影可能会导致问题 如果场景/背景可以改变,那么这个问题就更难解决了 一般提示 轮廓大小可以是其长度,只有在以下情况下才有效 cv::轮廓近似模式::链约无

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

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

阈值,应用形态学,轮廓搜索,选择最大轮廓。除了初始参数化,不需要包含背景的样本图像

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

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

背景棕色或前景灰色/白色的颜色检测/分类,应用形态学,执行轮廓搜索,选择最大轮廓。 颜色检测应在HSV或实验室颜色空间中进行。 为了有一个稳健的方法,颜色检测应该忽略亮度的差异,否则文档的阴影可能无法识别为背景 同时,应该将太暗和太亮的像素完全排除在颜色分类之外。 背景的样本图像可用于确定典型的背景颜色,但由于背景中有灰色色调,且对象本身为灰色/白色 人们可能应该手动提取颜色

要普遍解决这个问题相当困难。 例如,如果使用颜色检测查找灰色/白色文档,但将场景更改为灰色且亮度低于文档的背景,则颜色 检测和分类将失败,因为我们将使用灰色来检测背景和前景。 在这种情况下,阈值方法将工作得更好

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

下面是第一种方法简单阈值的一些代码,它可以完全提取文档:

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);
}
图像 彩色图像 灰度图像 阈值图像 变形图像 面向对象的最大轮廓包围盒 来自面向对象边界框的对象掩码 从原始图像中提取目标

通用信息 当前场景中的背景可以解释为噪声 整体图像照明不均匀左侧比右侧亮。如果我们想使用阈值,这会使事情复杂化。 文档具有非常明显的灰色/白色 这份文件大体上是同质的 文档的阴影可能会导致问题 如果场景/背景可以改变,那么这个问题就更难解决了 一般提示 轮廓大小可以是其长度,只有在以下情况下才有效 cv::轮廓近似模式::链约无

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

解决问题 有骡子 I解决此特定问题的几种方法:

阈值,应用形态学,轮廓搜索,选择最大轮廓。除了初始参数化,不需要包含背景的样本图像

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

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

背景棕色或前景灰色/白色的颜色检测/分类,应用形态学,执行轮廓搜索,选择最大轮廓。 颜色检测应在HSV或实验室颜色空间中进行。 为了有一个稳健的方法,颜色检测应该忽略亮度的差异,否则文档的阴影可能无法识别为背景 同时,应该将太暗和太亮的像素完全排除在颜色分类之外。 背景的样本图像可用于确定典型的背景颜色,但由于背景中有灰色色调,且对象本身为灰色/白色 人们可能应该手动提取颜色

要普遍解决这个问题相当困难。 例如,如果使用颜色检测查找灰色/白色文档,但将场景更改为灰色且亮度低于文档的背景,则颜色 检测和分类将失败,因为我们将使用灰色来检测背景和前景。 在这种情况下,阈值方法将工作得更好

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

下面是第一种方法简单阈值的一些代码,它可以完全提取文档:

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解释得很好+1