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或曝光时间,或者在曝光时间窗口内选择时变光源