OpenCV:从图像中移除周围环境 我有一张图片,中间有一个文件,周围有地毯,桌子的桌子等。
我想删除所有周围的,只有一个文件。 我尝试了阈值和自适应阈值,但没有取得多大成就 所以我想采集周围环境的样本,并在此基础上移除所有周围环境 这在OpenCV中可能吗?GerGeneral infos 当前场景中的背景可以解释为噪声 整体图像照明不均匀左侧比右侧亮。如果我们想使用阈值,这会使事情复杂化。 文档具有非常明显的灰色/白色 这份文件大体上是同质的 文档的阴影可能会导致问题 如果场景/背景可以改变,那么这个问题就更难解决了 一般提示 轮廓大小可以是其长度,只有在以下情况下才有效 cv::轮廓近似模式::链约无 正在使用或其所在区域 只需绘制填充轮廓并计算绘制的像素-这是提取区域信息最可靠的方法 解决问题 有多种方法可以解决此特定问题: 阈值,应用形态学,轮廓搜索,选择最大轮廓。除了初始参数化,不需要包含背景的样本图像 噪声检测滤波图像和未滤波图像之间的简单差异,标准偏差的每像素邻域分析等-定义最大噪声阈值,然后应用cv::threshold 对于背景分类,然后对前景进行分类,应用形态学,执行轮廓搜索,选择最大轮廓。 背景的样本图像可用于确定初始参数猜测的阈值 边缘检测Sobel/Scharr/等-定义最大边缘强度的阈值,然后应用cv::阈值进行背景分类,然后对前景进行分类,应用形态学,执行轮廓搜索,选择最大轮廓。 背景的样本图像可用于确定初始参数猜测的阈值 背景棕色或前景灰色/白色的颜色检测/分类,应用形态学,执行轮廓搜索,选择最大轮廓。 颜色检测应在HSV或实验室颜色空间中进行。 为了有一个稳健的方法,颜色检测应该忽略亮度的差异,否则文档的阴影可能无法识别为背景 同时,应该将太暗和太亮的像素完全排除在颜色分类之外。 背景的样本图像可用于确定典型的背景颜色,但由于背景中有灰色色调,且对象本身为灰色/白色 人们可能应该手动提取颜色 要普遍解决这个问题相当困难。 例如,如果使用颜色检测查找灰色/白色文档,但将场景更改为灰色且亮度低于文档的背景,则颜色 检测和分类将失败,因为我们将使用灰色来检测背景和前景。 在这种情况下,阈值方法将工作得更好 因此,一个完全自动化的过程将很难实现。 我认为您必须使背景分类策略可互换,以便您可以轻松地更改它,使其适合场景。 当然,可以实现某种启发式,例如,可以分析背景中的样本图像的噪声或标准偏差等。 如果存在大量噪声,则噪声检测/分类方法是可行的。 如果噪声几乎为零,但亮度或颜色与文档不同,则阈值或颜色检测/分类方法最适合 下面是第一种方法简单阈值的一些代码,它可以完全提取文档:OpenCV:从图像中移除周围环境 我有一张图片,中间有一个文件,周围有地毯,桌子的桌子等。,opencv,Opencv,我想删除所有周围的,只有一个文件。 我尝试了阈值和自适应阈值,但没有取得多大成就 所以我想采集周围环境的样本,并在此基础上移除所有周围环境 这在OpenCV中可能吗?GerGeneral infos 当前场景中的背景可以解释为噪声 整体图像照明不均匀左侧比右侧亮。如果我们想使用阈值,这会使事情复杂化。 文档具有非常明显的灰色/白色 这份文件大体上是同质的 文档的阴影可能会导致问题 如果场景/背景可以改变,那么这个问题就更难解决了 一般提示 轮廓大小可以是其长度,只有在以下情况下才有效 cv::轮
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