我正在尝试从相机拍摄的照片中提取单个对象。存在没有对象的摄像机视图的图片。相机的焦点不会改变,但是当物体在视野中时,照明会发生变化。物体是坚固的,但不是恒定的灰色阴影。 Here是右侧参考图像的图片,左侧是另一个图像。图片是单色的。
所有处理都在图像采集和计算时间不是大问题之后进行。算法的精度更重要。
我在这里发现的大多数问题都是关于两张图片是否相似的问题,但是我有兴趣找到对象在图像中占据的区域以供后续测量。
到目前为止,我已尝试使用阈值减去然后将模糊图像二值化的不同组合,但这不是光照不变的。预先将参考图像乘以相对光照差异也不会产生令人满意的结果。如果使用更好的模拟不同曝光的方法,它可能会起作用。
我也试过减去LoG过滤后的图像和一些粗略的邻域像素比较而没有成功。
感觉这是一个非常直观的任务,某种描述符比较应该能够处理,但我很难找到一个好的解决方案。有没有人知道我错过了一个好方法?
提前致谢。
感谢Franco Callari的回答,我偶然发现了histogram matching,令我惊讶的是现成的OpenCV函数没有涵盖。由于这似乎是一个常见的问题,我不妨发布我的低效hack-up,以防有人想要使用它。
// 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;
}
}
}
调整光照有助于更好地对物体进行初步猜测,但这并不足以获得精确的轮廓,尤其是当背景与物体差异不大或功能丰富时。这就像我一样到了那一刻。
答案 0 :(得分:2)
如果相机未移动且背景未发生变化(从样本照片中显示),全局照明的差异很可能是由于(大部分)两个因素中的任何一个因素 - 具有自动曝光功能的相机当拍摄对象在场景中时,f / stop或曝光时间的不同选择,或曝光时间窗口内的时变光源(例如灯泡中的60Hz线嗡嗡声)。如果发光器是一个闪光灯,则排除后者(并且闪光灯之间有足够的时间进行再充电)。
我上面说“大部分”是因为当拍摄对象占据画面的很大一部分时,从它反射的光线也会影响全局照明,但在你的情况下,这可能是二阶效应。< / p>
您最好的方法可能是更好地控制拍摄 - 至少禁用相机中的自动曝光,使用有碴灯(如果它不是闪光灯)。
如果你不能(或另外),你应该从全局直方图对齐开始,而不是均衡。其他海报建议的全局直方图均衡可能会受到伤害,因为主体的像素值将是直方图的一部分,而不仅仅是背景。但是,如果未移动相机,则可以在图像框架中预先识别已知为背景的区域,并仅在“仅背景”和“与主体”图像中对它们进行直方图采样。然后,您可以在动态范围的顶部和底部5%处找到值,并只应用全局缩放比例。
答案 1 :(得分: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
);
答案 2 :(得分:0)
如果照明是时变的,您可以在HSV或HSI空间中利用色调信息,色调可能被证明对照明是强不变的。如果您的图像是灰度图像,那么您可以尝试一些直方图均衡
答案 3 :(得分:0)
我会从:
开始(i)直方图均衡化 (ii)减去两个图像(如果参考物体确实没有改变它的位置 (iii)开口(侵蚀,膨胀),尝试不同的面罩尺寸(3x3,5x5)
然后看一下结果图像。剩下多少“垃圾”。如果您感兴趣的区域外有太多连接的像素,则可能需要增加开口的遮罩(或者仅用于侵蚀)。您可以将图像二值化并将二进制文件作为掩码来过滤掉原始图像中的感兴趣区域。
答案 4 :(得分:0)
另一种选择是使用背景减法算法。虽然主要用于视频序列,但应该没问题。我建议你来自opencv的Code Book Method
希望这有帮助