C++ 找出两个嘈杂、光线变化的图像之间的差异(直方图对齐等)
我试图从照相机拍摄的照片中提取一个物体。不存在对象的摄影机视图的图片。摄影机的焦点不会改变,但当对象在视图中时,照明会改变。物体是实心的,但不是恒定的灰色。右侧是参考图像的图片,左侧是另一个带有对象的图像。这些图片是单色的 所有的处理都发生在图像采集之后,计算时间不是什么大问题。算法的精度更为重要 我在这里发现的大多数问题都是关于两张图片是否相似的问题,但我感兴趣的是找到物体在图像中占据的区域,以便进行后续测量 到目前为止,我已经尝试了不同的减法组合,然后用阈值对模糊图像进行二值化,但这不是光照不变的。将参考图像预先乘以相对光照差也不会产生令人满意的结果。如果能用一种更好的方法来模拟不同的曝光,也许它能起作用 我还尝试过减去对数滤波图像和一些粗略的邻域像素比较,但没有成功 这感觉像是一个非常直观的任务,某种描述符比较应该能够处理,但我正在努力找到一个好的解决方案。有谁知道我错过的好方法吗 提前谢谢C++ 找出两个嘈杂、光线变化的图像之间的差异(直方图对齐等),c++,opencv,image-processing,computer-vision,C++,Opencv,Image Processing,Computer Vision,我试图从照相机拍摄的照片中提取一个物体。不存在对象的摄影机视图的图片。摄影机的焦点不会改变,但当对象在视图中时,照明会改变。物体是实心的,但不是恒定的灰色。右侧是参考图像的图片,左侧是另一个带有对象的图像。这些图片是单色的 所有的处理都发生在图像采集之后,计算时间不是什么大问题。算法的精度更为重要 我在这里发现的大多数问题都是关于两张图片是否相似的问题,但我感兴趣的是找到物体在图像中占据的区域,以便进行后续测量 到目前为止,我已经尝试了不同的减法组合,然后用阈值对模糊图像进行二值化,但这不是光照
多亏了Franco Callari的回答,我意外地发现,现成的OpenCV函数并没有涵盖这些函数。由于这似乎是一个常见的问题,我不妨将我的低效破解贴在这里,以防有人想使用它
// Aligns the histogram of the source image to match that of the target image.
// Both are evaluated in a ROI, not across the whole picture.
// The function assumes 8-bit grayscale images.
static void alignHistogram(Mat alignsource, const Mat& aligntarget,
const int rowstart = 500, const int rowend = 700,
const int colstart = 0, const int colend = 1000)
{
// 1) Calculate target and source histogram in region of interest
// 2) Compute the integral of each histogram (cumulative distribution function)
// 3) Set brightness of each pixel in the source image to the brightness of the target where the CDF values are equal
Mat ROIsource = alignsource(Range(rowstart, rowend), Range(colstart, colend));
Mat ROItarget = aligntarget(Range(rowstart, rowend), Range(colstart, colend));
MatND hist_source, hist_target;
int bins = 256, int histSize[] = {bins};
float sranges[] = { 0, 256 }; const float* ranges[] = { sranges };
int channels[] = {0};
calcHist( &ROItarget, 1, channels, Mat(), hist_target, 1, histSize, ranges, true, false );
calcHist( &ROItarget, 1, channels, Mat(), hist_source, 1, histSize, ranges, true, false );
Mat CDF_target_2d, CDF_source_2d;
integral(hist_target, CDF_target_2d);
integral(hist_source, CDF_source_2d);
Mat CDF_target = CDF_target_2d.col(1), CDF_source = CDF_source_2d.col(1);
// Cycle through source image inefficiently and adjust brightness
for (int i = 0; i < alignsource.rows; i++)
{
for (int j = 0; j < alignsource.cols; j++)
{
int source_brightness = alignsource.at<unsigned char>(i, j);
double cdf_source_value = CDF_source.at<double>(source_brightness);
int target_brightness = 0;
while (target_brightness < 256) {
if (CDF_target.at<double>(target_brightness) > cdf_source_value)
break;
target_brightness++;
}
alignsource.at<unsigned char>(i, j) = target_brightness;
}
}
}
//将源图像的直方图与目标图像的直方图对齐。
//两者都是在ROI中评估的,而不是在整个图片中。
//该函数假定为8位灰度图像。
静态空洞对齐柱状图(材质对齐源、常量材质和对齐目标、,
常数int rowstart=500,常数int rowend=700,
常数int colstart=0,常数int colend=1000)
{
//1)计算感兴趣区域的目标和源直方图
//2)计算每个直方图的积分(累积分布函数)
//3)将源图像中每个像素的亮度设置为CDF值相等的目标的亮度
Mat ROIsource=校准源(范围(行开始,行结束),范围(colstart,colend));
Mat ROItarget=aligntarget(范围(行开始,行结束),范围(行开始,行结束));
MatND hist_源、hist_目标;
int bins=256,int histSize[]={bins};
浮点范围[]={0,256};常量浮点*范围[]={sranges};
int通道[]={0};
calcHist(&ROItarget,1,通道,Mat(),hist_目标,1,histSize,ranges,true,false);
calcHist(&ROItarget,1,channels,Mat(),hist_source,1,histSize,ranges,true,false);
Mat CDF_目标_2d,CDF_源_2d;
积分(历史目标、CDF目标、2d);
积分(历史震源、CDF震源、2d);
Mat CDF_target=CDF_target_2d.col(1),CDF_source=CDF_source_2d.col(1);
//无效地循环浏览源图像并调整亮度
对于(int i=0;iCDF_源_值)
打破
目标亮度++;
}
对准光源。at(i,j)=目标亮度;
}
}
}
调整照明有助于更好地对对象进行初步猜测,但是“这还不足以获得精确的轮廓,尤其是当背景与物体差别不大或特征不丰富时。这就是我目前所知道的。如果照明是时变的,你可以利用HSV或HSI空间中的色调信息,色调可能对照明具有很强的不变性。如果您的图像是灰度图像,则可以尝试一些直方图均衡化(使用直方图均衡化(OpenCV中的cvEqualizeHist()函数)。它将帮助您消除照明的差异 2) 您将有两个几乎相同的图像(无对象图像和有对象图像,其他部分必须相同,因为相机是静态的)。从功能上看它们的不同
void cvSub(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask = NULL)
3) 结果必须是:物体所在的位置几乎是白色的,图像的其余部分将不那么明亮。使用
void cvInRangeS(
const CvArr* src,
CvScalar lower,
CvScalar upper,
CvArr* dst
);
此功能可用于检查图像中的像素是否在特定的指定范围内。因此,取一个接近白色的范围(例如(200255))
4) 查找图像中剩余对象的边缘,并使用
int cvFindContours(
IplImage* img,
CvMemStorage* storage,
CvSeq** firstContour,
int headerSize = sizeof(CvContour),
CvContourRetrievalMode mode = CV_RETR_LIST,
CvChainApproxMethod method = CV_CHAIN_APPROX_SIMPLE
);
首先,我想说:
(i) 直方图均衡化
(ii)减去两幅图像(如果参考对象确实没有改变其位置
(iii)打开(腐蚀、膨胀),尝试不同的掩模尺寸(3x3、5x5)
然后看一看结果图像。剩下多少“垃圾”。如果感兴趣的区域外有太多连接的像素,您可能需要增加开口的遮罩(或者可能仅用于腐蚀)。您可以对图像进行二值化,并将二值化作为遮罩,以过滤掉原始图像中的感兴趣区域。如果相机未移动或背景未更改(如示例照片所示),则可能是由于全局照明的差异(大部分情况下)两个因素中的任何一个-当对象在场景中时,具有自动曝光功能的相机可以选择不同的f/stop或曝光时间,或者在曝光时间窗口内选择时变光源