自动对焦核心方法

自动对焦常用方法

核心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;
}

在这里插入图片描述

  • 代码待补充
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值