相机的自动对焦要求相机根据拍摄环境和场景的变化,通过相机内部的微型驱动马达,自动调节相机镜头和CCD之间的距离,保证像平面正好投影到CCD的成像表面上。这时候物体的成像比较清晰,图像细节信息丰富。

 

相机自动对焦的过程,其实就是对成像清晰度评价的过程,对焦不准确,拍摄出来的图像清晰度低,视觉效果模糊,如果是在工业检测测量领域,对焦不准导致的后果可能是致命的;对焦准确的图像清晰度较高,层次鲜明,对比度高。

 

图像清晰度评价算法有很多种,在空域中,主要思路是考察图像的领域对比度,即相邻像素间的灰度特征的梯度差;在频域中,主要思路是考察图像的频率分量,对焦清晰的图像高频分量较多,对焦模糊的图像低频分量较多。

 

这里实现3种清晰度评价方法,分别是Tenengrad梯度方法、Laplacian梯度方法和方差方法。

 

Tenengrad梯度方法

 

Tenengrad梯度方法利用Sobel算子分别计算水平和垂直方向的梯度,同一场景下梯度值越高,图像越清晰。以下是具体实现,这里衡量的指标是经过Sobel算子处理后的图像的平均灰度值,值越大,代表图像越清晰。

 

 

    1. #include <highgui/highgui.hpp>
    2. #include <imgproc/imgproc.hpp>
    3.  
    4. using namespace std;
    5. using namespace cv;
    6.  
    7. int main()
    8. {
    9. Mat imageSource = imread("2.jpg");
    10. Mat imageGrey;
    11.  
    12. cvtColor(imageSource, imageGrey, CV_RGB2GRAY);
    13. Mat imageSobel;
    14. Sobel(imageGrey, imageSobel, CV_16U, 1, 1);
    15.  
    16. //图像的平均灰度
    17. double meanValue = 0.0;
    18. meanValue = mean(imageSobel)[0];
    19.  
    20. //double to string
    21. stringstream meanValueStream;
    22. string meanValueString;
    23. meanValueStream << meanValue;
    24. meanValueStream >> meanValueString;
    25. meanValueString = "Articulation(Sobel Method): " + meanValueString;
    26. putText(imageSource, meanValueString, Point(20, 50), CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 25), 2);
    27. imshow("Articulation", imageSource);
    28. waitKey();
    29. }

     

    使用三张测试图片模拟不同对焦。第一张最清晰,得分最高,第二三张越来越模糊,得分依次降低。

    javao opencv 让图片字更清晰 opencv图像清晰_Source

     

    javao opencv 让图片字更清晰 opencv图像清晰_ci_02

     

    javao opencv 让图片字更清晰 opencv图像清晰_方差_03

     

     

    Laplacian梯度方法:

     

    Laplacian梯度是另一种求图像梯度的方法,在上例的OpenCV代码中直接替换Sobel算子即可。

     

    1. #include <highgui/highgui.hpp>
    2. #include <imgproc/imgproc.hpp>
    3.  
    4. using namespace std;
    5. using namespace cv;
    6.  
    7. int main()
    8. {
    9. Mat imageSource = imread("1.jpg");
    10. Mat imageGrey;
    11.  
    12. cvtColor(imageSource, imageGrey, CV_RGB2GRAY);
    13. Mat imageSobel;
    14.  
    15. Laplacian(imageGrey, imageSobel, CV_16U);
    16. //Sobel(imageGrey, imageSobel, CV_16U, 1, 1);
    17.  
    18. //图像的平均灰度
    19. double meanValue = 0.0;
    20. meanValue = mean(imageSobel)[0];
    21.  
    22. //double to string
    23. stringstream meanValueStream;
    24. string meanValueString;
    25. meanValueStream << meanValue;
    26. meanValueStream >> meanValueString;
    27. meanValueString = "Articulation(Laplacian Method): " + meanValueString;
    28. putText(imageSource, meanValueString, Point(20, 50), CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 25), 2);
    29. imshow("Articulation", imageSource);
    30. waitKey();
    31. }

    用同样的三张测试图片测试,结果一致,随着对焦模糊得分降低:

    javao opencv 让图片字更清晰 opencv图像清晰_方差_04

     

    javao opencv 让图片字更清晰 opencv图像清晰_Source_05

     

    javao opencv 让图片字更清晰 opencv图像清晰_Source_06

     

     

    方差方法:

     

    方差是概率论中用来考察一组离散数据和其期望(即数据的均值)之间的离散(偏离)成都的度量方法。方差较大,表示这一组数据之间的偏差就较大,组内的数据有的较大,有的较小,分布不均衡;方差较小,表示这一组数据之间的偏差较小,组内的数据之间分布平均,大小相近。

     

    对焦清晰的图像相比对焦模糊的图像,它的数据之间的灰度差异应该更大,即它的方差应该较大,可以通过图像灰度数据的方差来衡量图像的清晰度,方差越大,表示清晰度越好

     

    1. #include <highgui/highgui.hpp>
    2. #include <imgproc/imgproc.hpp>
    3.  
    4. using namespace std;
    5. using namespace cv;
    6.  
    7. int main()
    8. {
    9. Mat imageSource = imread("2.jpg");
    10. Mat imageGrey;
    11.  
    12. cvtColor(imageSource, imageGrey, CV_RGB2GRAY);
    13. Mat meanValueImage;
    14. Mat meanStdValueImage;
    15.  
    16. //求灰度图像的标准差
    17. meanStdDev(imageGrey, meanValueImage, meanStdValueImage);
    18. double meanValue = 0.0;
    19. meanValue = meanStdValueImage.at<double>(0, 0);
    20.  
    21. //double to string
    22. stringstream meanValueStream;
    23. string meanValueString;
    24. meanValueStream << meanValue*meanValue;
    25. meanValueStream >> meanValueString;
    26. meanValueString = "Articulation(Variance Method): " + meanValueString;
    27.  
    28. putText(imageSource, meanValueString, Point(20, 50), CV_FONT_HERSHEY_COMPLEX, 0.8, Scalar(255, 255, 25), 2);
    29. imshow("Articulation", imageSource);
    30. waitKey();
    31. }

    方差数值随着清晰度的降低逐渐降低:

    javao opencv 让图片字更清晰 opencv图像清晰_ci_07

     

    javao opencv 让图片字更清晰 opencv图像清晰_ci_08

     

    javao opencv 让图片字更清晰 opencv图像清晰_方差_09

     

    在工业应用中,最清晰的对焦拍摄出来的图像不一定是最好的,有可能出现摩尔纹(水波纹)现象,一般需要在最清晰对焦位置附件做一个微调。

     

    相关实现源码

    float get_sobel_mean(cv::Mat img) {
        cv::Mat imageSobel;
        Sobel(img, imageSobel, CV_16U, 1, 1);
        double meanValue = 0.0;
        meanValue = mean(imageSobel)[0];
        return meanValue;
    }
    float get_lap_var(cv::Mat img) {
        cv::Mat dst;
        //Laplace(f) = \dfrac{\partial^{2} f}{\partial x^{2}} + \dfrac{\partial^{2} f}{\partial y^{2}}
        int kernel_size = 3;
        int ddepth = CV_16U;
        Laplacian(img, dst, ddepth, kernel_size, BORDER_DEFAULT);
        Mat tmp_m, tmp_sd;
        double m = 0, sd = 0;
        meanStdDev(dst, tmp_m, tmp_sd);
        m = tmp_m.at<double>(0, 0);
        sd = tmp_sd.at<double>(0, 0);
        //cout << "laplacian......... Mean: " << m << " ,laplacian StdDev: " << sd << endl;
        return sd;
    }
    
    float get_img_dft_mean(cv::Mat input)
    {
        cvtColor(input, input, CV_RGB2GRAY);
        int w = getOptimalDFTSize(input.cols);
        int h = getOptimalDFTSize(input.rows);//获取最佳尺寸,快速傅立叶变换要求尺寸为2的n次方
        Mat padded;
        copyMakeBorder(input, padded, 0, h - input.rows, 0, w - input.cols, BORDER_CONSTANT, Scalar::all(0));//填充图像保存到padded中
    
        Mat plane[] = { Mat_<float>(padded), Mat_<float>::zeros(padded.size()) };//创建通道
        Mat complexIm;
        merge(plane, 2, complexIm);//合并通道
    
        dft(complexIm, complexIm);//进行傅立叶变换,结果保存在自身
        int rows = complexIm.rows;
        int cols = complexIm.cols;
        int offsetX = rows / 6;
        int offsetY = cols / 6;
        float mean = 0;
        int count = 0;
        for (size_t i = 0; i < rows; i++)
        {
            float *ptr = complexIm.ptr<float>(i);
            for (size_t j = 0; j < cols * 2; j = j + 2)
            {
                if ((i > offsetX && i < (rows - offsetX)) && (j > 2 * offsetY && j < (2 * cols - 2 * offsetY))) {
                    mean += sqrt(ptr[j] * ptr[j] + ptr[j + 1] * ptr[j + 1]);
                    count++;
                }
            }
        }
        return mean / count;
    }

     

    基于信息熵的评价方法

    javao opencv 让图片字更清晰 opencv图像清晰_Source_10