自动对焦常用方法
核心1:常用的自动对焦方法是基于图像质量评估方法进行的。基本思路是结合运动装置或电控镜头移动拍摄物体对象的同时,对拍摄的图像进行清晰度度量。
核心2:需要使用生产者消费者模式实现快速飞拍自动对焦。
知识扩展: 其它的对焦方案,可以使用液态镜头或者电控镜头/激光测距仪等实现。
c++ 代码如下所示
#include <opencv2/opencv.hpp>
using namespace cv;
class ComputerIqaFunction
{
public:
double Brenner;
double Tenengrad;
double LaplaValue;
double SMD2;
double Energy;
double EAV;
double NRSS;
public:
double ComputerBrenner(Mat&);
double ComputerTenengrad(Mat&);
double ComputerLaplacian(Mat&);
double ComputerSMD2(Mat&);
double ComputerEnergyGradient(Mat&);
double ComputerEAV(Mat&);
double ComputerSSIM(Mat i1, Mat i2);
double ComputerNRSS(Mat&);
void All_Computer(Mat&);
};
#include "ComputerIqaFunction.h"
double ComputerIqaFunction::ComputerBrenner(Mat& image)
{
Mat gray_img;
if (image.channels() == 3) {
cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
double result = 0.;
for (int i = 0; i < gray_img.rows; ++i) {
uchar* data = gray_img.ptr<uchar>(i);
for (int j = 0; j < gray_img.cols - 2; ++j) {
result += pow(data[j + 2] - data[j], 2);
}
}
return result / gray_img.total();
}
double ComputerIqaFunction::ComputerTenengrad(Mat& image)
{
Mat gray_img, sobel_x, sobel_y, G;
if (image.channels() == 3) {
cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
//分别计算x/y方向梯度
Sobel(gray_img, sobel_x, CV_32FC1, 1, 0);
Sobel(gray_img, sobel_y, CV_32FC1, 0, 1);
multiply(sobel_x, sobel_x, sobel_x);
multiply(sobel_y, sobel_y, sobel_y);
Mat sqrt_mat = sobel_x + sobel_y;
sqrt(sqrt_mat, G);
return mean(G)[0];
}
double ComputerIqaFunction::ComputerLaplacian(Mat& image)
{
cv::Mat gray_img, lap_image;
if (image.channels() == 3) {
cv::cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
cv::Laplacian(gray_img, lap_image, CV_32FC1);
lap_image = cv::abs(lap_image);
return cv::mean(lap_image)[0];
}
double ComputerIqaFunction::ComputerSMD2(Mat& image)
{
Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3) {
cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
Mat kernel_x(3, 3, CV_32F, Scalar(0));
kernel_x.at<float>(1, 2) = -1.0;
kernel_x.at<float>(1, 1) = 1.0;
Mat kernel_y(3, 3, CV_32F, Scalar(0));
kernel_y.at<float>(1, 1) = 1.0;
kernel_y.at<float>(2, 1) = -1.0;
filter2D(gray_img, smd_image_x, gray_img.depth(), kernel_x);
filter2D(gray_img, smd_image_y, gray_img.depth(), kernel_y);
smd_image_x = abs(smd_image_x);
smd_image_y = abs(smd_image_y);
multiply(smd_image_x, smd_image_y, G);
return mean(G)[0];
}
double ComputerIqaFunction::ComputerEnergyGradient(Mat& image)
{
Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3) {
cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
Mat kernel_x(3, 3, CV_32F, cv::Scalar(0));
kernel_x.at<float>(1, 2) = -1.0;
kernel_x.at<float>(1, 1) = 1.0;
Mat kernel_y(3, 3, CV_32F, cv::Scalar(0));
kernel_y.at<float>(1, 1) = 1.0;
kernel_y.at<float>(2, 1) = -1.0;
filter2D(gray_img, smd_image_x, gray_img.depth(), kernel_x);
filter2D(gray_img, smd_image_y, gray_img.depth(), kernel_y);
multiply(smd_image_x, smd_image_x, smd_image_x);
multiply(smd_image_y, smd_image_y, smd_image_y);
G = smd_image_x + smd_image_y;
return mean(G)[0];
}
double ComputerIqaFunction::ComputerEAV(Mat& image)
{
Mat gray_img, smd_image_x, smd_image_y, G;
if (image.channels() == 3) {
cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
double result = 0.;
for (int i = 1; i < gray_img.rows - 1; ++i) {
uchar* prev = gray_img.ptr<uchar>(i - 1);
uchar* cur = gray_img.ptr<uchar>(i);
uchar* next = gray_img.ptr<uchar>(i + 1);
for (int j = 0; j < gray_img.cols; ++j) {
result += (abs(prev[j - 1] - cur[i]) * 0.7 + abs(prev[j] - cur[j]) + abs(prev[j + 1] - cur[j]) * 0.7 +
abs(next[j - 1] - cur[j]) * 0.7 + abs(next[j] - cur[j]) + abs(next[j + 1] - cur[j]) * 0.7 +
abs(cur[j - 1] - cur[j]) + abs(cur[j + 1] - cur[j]));
}
}
return result / gray_img.total();
}
double ComputerIqaFunction::ComputerSSIM(Mat i1, Mat i2)
{
const double C1 = 6.5025, C2 = 58.5225;
int d = CV_32F;
cv::Mat I1, I2;
i1.convertTo(I1, d);
i2.convertTo(I2, d);
cv::Mat I1_2 = I1.mul(I1);
cv::Mat I2_2 = I2.mul(I2);
cv::Mat I1_I2 = I1.mul(I2);
cv::Mat mu1, mu2;
GaussianBlur(I1, mu1, cv::Size(11, 11), 1.5);
GaussianBlur(I2, mu2, cv::Size(11, 11), 1.5);
cv::Mat mu1_2 = mu1.mul(mu1);
cv::Mat mu2_2 = mu2.mul(mu2);
cv::Mat mu1_mu2 = mu1.mul(mu2);
cv::Mat sigma1_2, sigam2_2, sigam12;
GaussianBlur(I1_2, sigma1_2, cv::Size(11, 11), 1.5);
sigma1_2 -= mu1_2;
GaussianBlur(I2_2, sigam2_2, cv::Size(11, 11), 1.5);
sigam2_2 -= mu2_2;
GaussianBlur(I1_I2, sigam12, cv::Size(11, 11), 1.5);
sigam12 -= mu1_mu2;
cv::Mat t1, t2, t3;
t1 = 2 * mu1_mu2 + C1;
t2 = 2 * sigam12 + C2;
t3 = t1.mul(t2);
t1 = mu1_2 + mu2_2 + C1;
t2 = sigma1_2 + sigam2_2 + C2;
t1 = t1.mul(t2);
cv::Mat ssim_map;
divide(t3, t1, ssim_map);
cv::Scalar mssim = cv::mean(ssim_map);
double ssim = (mssim.val[0] + mssim.val[1] + mssim.val[2]) / 3;
return ssim;
}
double ComputerIqaFunction::ComputerNRSS(Mat& image)
{
/*
step1:将待评价图像经过低通滤波处理,得到参考图像;
step2:提取待评价和参考图像的梯度信息;
step3:根据梯度信息,寻找前N个图像信息块;
step4:根据获得的信息块,计算待评价信息快和参考图像信息块之间的NRSS值
*/
cv::Mat gray_img, Ir, G, Gr;
if (image.channels() == 3) {
cv::cvtColor(image, gray_img, COLOR_BGR2GRAY);
}
//构造参考图像
cv::GaussianBlur(gray_img, Ir, cv::Size(7, 7), 6, 6);
//提取图像和参考图像的梯度信息
cv::Sobel(gray_img, G, CV_32FC1, 1, 1);//计算原始图像sobel梯度
cv::Sobel(Ir, Gr, CV_32FC1, 1, 1);//计算构造函数的sobel梯度
//找出梯度图像 G 中梯度信息最丰富的 N 个图像块,n=64(即划分为8x8的大小)
//计算每个小方块的宽/高
int block_cols = G.cols * 2 / 9;
int block_rows = G.rows * 2 / 9;
//获取方差最大的block
cv::Mat best_G, best_Gr;
double max_stddev = 0.;
int pos = 0;
for (int i = 0; i < 64; ++i) {
int left_x = (i % 8) * (block_cols / 2);
int left_y = (i / 8) * (block_rows / 2);
int right_x = left_x + block_cols;
int right_y = left_y + block_rows;
if (left_x < 0) left_x = 0;
if (left_y < 0) left_y = 0;
if (right_x >= G.cols) right_x = G.cols - 1;
if (right_y >= G.rows) right_y = G.rows - 1;
cv::Rect roi(left_x, left_y, right_x - left_x, right_y - left_y);
cv::Mat temp = G(roi).clone();
cv::Scalar mean, stddev;
cv::meanStdDev(temp, mean, stddev);
if (stddev.val[0] > max_stddev) {
max_stddev = static_cast<float>(stddev.val[0]);
pos = i;
best_G = temp;
best_Gr = Gr(roi).clone();
}
}
//计算结构清晰度NRSS
double result = 1 - ComputerSSIM(best_G, best_Gr);
return result;
}
void ComputerIqaFunction::All_Computer(Mat& H2M_IMG)
{
Brenner = ComputerBrenner(H2M_IMG);
Tenengrad = ComputerTenengrad(H2M_IMG);
LaplaValue = ComputerLaplacian(H2M_IMG);
SMD2 = ComputerSMD2(H2M_IMG);
Energy = ComputerEnergyGradient(H2M_IMG);
EAV = ComputerEAV(H2M_IMG);
NRSS = ComputerNRSS(H2M_IMG);
}
// QUA.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。
#include <iostream>
# include "ComputerIqaFunction.h"
//#include <opencv2/opencv.hpp>
#include <fstream>
#include <string>
#include <streambuf>
std::vector<Mat> ReadImage(cv::String pattrn);
int main()
{
// opencv遍历一个文本
ComputerIqaFunction* a = new ComputerIqaFunction();
cv::String files = "C:/Users/admin/Desktop/slider/4/*";
// 读取图片
std::vector<cv::Mat> slider = ReadImage(files);
// 计算图像质量
std::vector<Mat>::iterator start;
std::ofstream oFile;
oFile.open("C:/Users/admin/Desktop/4_QUA.csv", std::ios::out | std::ios::trunc); // 这样就很容易的输出一个需要的excel 文件
oFile << "Brenner" << "," << "TenGrad" << "," << "Laplac" << "," << "SMD2" << "," << "EnergyGradient" << "," << "EAV" << "," << "NRSS" << std::endl;
for (start = slider.begin(); start!=slider.end(); start++)
{
a->All_Computer(*start);
// 将计算结果写入到excel表格中
oFile << std::to_string(a->Brenner) << "," << std::to_string(a->Tenengrad) << "," << std::to_string(a->LaplaValue) << ","
<< std::to_string(a->SMD2) <<","<<std::to_string(a->Energy) <<"," <<std::to_string(a->EAV)<<"," <<std::to_string(a->NRSS)<< std::endl;
}
oFile.close();
delete a;
}
std::vector<Mat> ReadImage(cv::String pattrn)
{
std::vector<cv::String> fn;
glob(pattrn, fn, false);
std::vector<Mat> images;
int count = fn.size();
for (int i = 0; i < count; i++)
{
std::cout << fn[i] << std::endl;
images.emplace_back(imread(fn[i]));
}
return images;
}
- 代码待补充