3

我正在尝试从相机拍摄的照片中提取单个对象。存在没有对象的相机视图的图片。相机的焦点不会改变,但是当物体在视野中​​时,光照会改变。该物体是实心的,但不是恒定的灰色阴影。是右侧参考图像的图片和左侧带有对象的另一张图像。图片是单色的。

所有的处理都是在图像采集和计算时间之后发生的,这不是一个大问题。算法的精度更重要。

我在这里找到的大多数问题都涉及两张图片是否相似的问题,但我有兴趣找到对象在图像中占据的区域以进​​行后续测量。

到目前为止,我已经尝试过用阈值对模糊图像进行减法然后二值化的不同组合,但这并不是光照不变的。预先将参考图像乘以相对照明差异也不会产生令人满意的结果。如果使用更好的方法来模拟不同的曝光,也许它会起作用。

我还尝试减去 LoG 过滤图像和一些粗略的邻域像素比较,但没有成功。

感觉像是一个非常直观的任务,某种描述符比较应该能够处理,但我正在努力寻找一个好的解决方案。有谁知道我一直失踪的好方法?

提前致谢。


感谢 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;
        }
    }
}

调整照明有助于更好地初步猜测物体,但不足以获得精确的轮廓,尤其是在背景与物体差别不大或特征丰富的情况下。就我所知片刻。

4

5 回答 5

3

如果相机没有移动也没有改变背景(从示例照片中显示),全局照明的差异可能是由于(大部分)两个因素之一 - 具有自动曝光的相机做出不同的选择拍摄对象在场景中时的 f/stop 或曝光时间,或曝光时间窗口内的时变光源(例如灯中的 60Hz 线路嗡嗡声)。如果照明器是闪光灯,则后者将被排除在外(并且两次拍摄之间有足够的时间让闪光灯充电)。

我在上面说“大部分”是因为,由于主体占据了画面的大部分,从主体反射的光也会影响全局照明,但在您的情况下,这可能是二阶效应。

您最好的方法可能是更好地控制捕获 - 至少禁用相机中的自动曝光,使用镇流灯(如果它不是闪光灯)。

如果你不能(或另外),你应该从全局直方图对齐开始,而不是均衡化。正如其他海报所建议的那样,全局直方图均衡可能会受到伤害,因为对象的像素值将成为直方图的一部分,而不仅仅是背景。但是,如果相机没有移动,您可以在图像帧中预先识别已知为背景的区域,并在“仅背景”和“有主题”图像中仅从它们中采样直方图。然后,您可以在动态范围的顶部和底部 5% 处找到值,并应用全局缩放来匹配它们。

于 2013-03-28T14:13:34.867 回答
1

1) 使用直方图均衡化(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
);

此功能可用于检查图像中的像素是否在特定指定范围内。所以取一个接近白色的范围(例如 (200, 255))。

4)找到图像中剩余物体的边缘

int cvFindContours(
  IplImage*              img,
  CvMemStorage*          storage,
  CvSeq**                firstContour,
  int                    headerSize  = sizeof(CvContour),
  CvContourRetrievalMode mode        = CV_RETR_LIST,
  CvChainApproxMethod    method      = CV_CHAIN_APPROX_SIMPLE
);
于 2013-03-28T08:21:39.800 回答
0

如果光照是时变的,您可以利用 HSV 或 HSI 空间中的色调信息,色调可能被证明对光照具有很强的不变性。如果您的图像是灰度的,那么您可以尝试一些直方图均衡

于 2013-03-28T06:02:25.580 回答
0

我将从:

(i) 直方图均衡化 (ii) 两幅图像相减(如果参考对象确实没有改变其位置 (iii) 开口(侵蚀、膨胀),尝试不同的蒙版尺寸(3x3、5x5)

然后看看生成的图像。还剩多少“垃圾”。如果在您感兴趣的区域之外有太多连接的像素,您可能需要增加开口的遮罩(或者可能仅用于腐蚀)。您可以对图像进行二值化并将二进制作为掩码过滤掉原始图像中的感兴趣区域。

于 2013-03-28T09:08:21.450 回答
0

另一种选择是使用背景减法算法。虽然主要用于视频序列,但应该没问题。我建议你使用opencv的Code Book Method

  1. 给密码本方法你的输入图像
  2. 您有变量要调整,告诉码本方法它必须以多少帧作为背景,在您的情况下,这将是一个。
  3. 结果是前景图像和背景图像。
  4. 然后,您可以使用平滑来清除一些噪音。
  5. 使用应该包含您的对象的前景图像(假设手指是您想要找到的)
  6. 使用 opencv 方法查找轮廓。

希望这可以帮助

于 2013-04-01T12:15:22.330 回答