(Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE

LSC

(Lens Shading Correction)镜头阴影矫正

现象:
(Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第1张图片 (Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第2张图片
相机成像会出现中心亮,四周暗的现象;益或在RGB图中出现色彩不均匀的现象,这会为系统引入大量的噪声。
原因分析:这种现象的原因皆是因为镜头成像的光学特性所致。

(Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第3张图片

                    (Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第4张图片

中心亮,四周暗:镜头的物理学模型可以近似看做一个凸透镜,成像时集中在中间区域的能量要大于集中在边缘的能量。
色彩不均匀: 镜头带来的color shading主要是因为不同颜色的光的折射率不同,导致白光经过镜头后达到成像面时不同颜色的光的位置不同导致偏色。
本文采用C++代码演示,针对GigE工业相机读取到的原始图像做LSC处理,最后分析下算法的缺点和改进方向
解决方案:本文采用存储增益矩阵法解决这个问题,就是针对Yshading,分为标定增益矩阵和实际矫正两个步骤。
第一步:标定增益矩阵
//获得相机矫正的增益矩阵
void Get_LscGain(Mat src,double Gain[])
{
       double minval, maxval;
       minMaxLoc(src, &minval, &maxval);
       cout << maxval << endl;
       for (int r = 0; r < src.rows; r++)
       {
              for (int c = 0; c < src.cols; c++)
              {
                      Gain[r * src.cols + c] = maxval / src.at(r, c);
              }
       };
}
        在标定的过程中,用GigE工业相机在均匀分布的光源下,拍摄一副背景为纯色(一般为白色)的图像。下图就是读取的图像,仔细观察其实可以看到LS现象,由于整体亮度偏低,我们对下图做一下直方图均衡化,拉伸对比度,可以更好地看到LS现象。
(Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第5张图片
这个就是我拍到的实际的LS现象,确实对后续的处理影响很大。其中的雪花点多为噪声,我们这里先不考虑滤波的操作。看下代码Get_LscGain,意思就是我们需要获得标定的增益矩阵Gain。
static double* Gain = new double[src.rows * src.cols];
这里的Gain我们用一个静态的一维数组表示,数组中的每个值表示需要在采集图中对应像素需要乘以的系数。
minMaxLoc是OpenCV中一个自带的获取图像像素最大值的函数;
Gain[r * src.cols + c] = maxval / src.at(r, c);通过该语句将每个像素点所取得增益值存储进Gain中。
以上就完成了标定Gain操作。
第二步:实际矫正
void Lsc(Mat src, Mat& des, double Gain[])
{
       des.create(src.rows, src.cols, CV_8UC1);
       for (int r = 0; r < src.rows; r++)
       {
              for (int c = 0; c < src.cols; c++)
              {
                      des.at (r, c) = (src.at(r, c) + 1) * Gain[r *  src.cols + c];
              }
       }
}
src就是待矫正的图像,Gain就是之前标定好的增益矩阵,des就是矫正后图像。矫正就是把增益乘给每个像素点即可。
void StreamRetrieve::threadProc()
{
       //Mat image = imread("gige.jpg");
       Mat gray, fileter_img, lsc_img;
       static double* Gain = new double[gray.rows * gray.cols];
       int frameCount = 0;
       while (m_isLoop)
       {
              // 此frame对象必须为临时变量,对象的生命周期与驱动帧缓存相关。
              // 此对象生命周期结束意味着:驱动可以回收此帧缓存并用于存放后续获取到的帧。
              // 如没有及时释放,获取到的帧与设置的帧缓存相同时,将无法获取到帧(因为驱动已没有可用的帧缓存)。
              // This frame object must be a temporary variable, and its lifecycle is  related to the drive frame cache.
              // The end of this object's life cycle means that the driver can reclaim  this frame cache and use it to store subsequent acquired frames.
              // If it is not released in time, the acquired frame will be the same as  the set frame cache, and the frame will not be acquired (because the driver has no  available frame cache).
              CFrame frame;
              //获取一帧
              //get one image
              if (!m_streamSptr)
              {                      printf("m_streamPtr is NULL.\n");
                      return;
              }
              bool isSuccess = m_streamSptr->getFrame(frame, 1000);
              if (!isSuccess)
              {
                      printf("getFrame  fail.\n");
                      continue;
              }
              //判断帧的有效性
              //Judge the validity of frame
              bool isValid = frame.valid();
              if (!isValid)
              {
                      printf("frame is invalid!\n");
                      continue;
              }
              //显示图像
              Mat image = Mat(frame.getImageHeight(), frame.getImageWidth(), CV_8UC1,  (uchar*)frame.getImage());
              resize(image, image, Size(720, 720));
              if (frameCount==0)
              {
                      //cvtColor(image, gray, COLOR_BGR2GRAY);
                      Get_LscGain(image, Gain);
              }
              Mat image_enhanced;
              Lsc(image, lsc_img,Gain);
              equalizeHist(lsc_img,image_enhanced);
              namedWindow("camera_window");
              namedWindow("image_enhanced");
              imshow("camera_window", image);
              imshow("image_enhanced", image_enhanced);
              waitKey(30);
              if(num++%10==1)
                      printf("get frame %u successfully thread ID :%d\n",  ++frameCount, CThread::getCurrentThreadID());
       }
       delete []Gain;
}

最后我把读图线程的程序给出,这里面调用了上述的两个函数,想看的人仔细看下应该能看懂。
实验效果:
没有经过LSC矫正前:
(Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第6张图片
经过LSC矫正后:
(Lens Shading Correction)镜头阴影矫正,基于面阵工业相机GigE_第7张图片
总结:
本文使用的算法是存储增益矩阵,存在的问题有两个:
1.本文存储的增益矩阵针对每个像素点都有一个比例值,如果相机的像素是1000万甚至更多,存储增益矩阵的开销会大大增加。
  • 解决方法:将图像分成若干网格,针对每一个网格中的所有像素点存储一个增益值。
2.本文的增益矩阵直接来自于一张标定图像的增益矩阵。而单张标定图肯定存在一定的误差。
  • 解决方法:最常见的方法就是多次标定取平均以消除误差。另外,我看有人说增益的衰弱可以用(cosθ)^4拟合,这样的拟合可以更符合相机的衰减规律。

你可能感兴趣的:(算法,c++,图像处理)