OpenCV C++案例实战七《生成蒙太奇图像》续

OpenCV C++案例实战七《生成蒙太奇图像》续

  • 前言
  • 一、基于直方图比较
    • 效果
  • 二、基于均方误差(MSE)比较
    • 效果
  • 三、源码
  • 总结

前言

本文将使用OpenCV C++ 在案例实战二的基础上,编写新算法生成蒙太奇图像。

一、基于直方图比较

原图如图所示。
请添加图片描述

double calmyHist(Mat src, Mat temp)
{//灰度图if (src.channels() == 1){int histSize = 256;float range[] = { 0,256 };const float*histRange = { range };Mat src_hist, temp_hist;//计算直方图calcHist(&src, 1, 0, Mat(), src_hist, 1, &histSize, &histRange);calcHist(&temp, 1, 0, Mat(), temp_hist, 1, &histSize, &histRange);//归一化normalize(src_hist, src_hist, 0, 1, NORM_MINMAX);normalize(temp_hist, temp_hist, 0, 1, NORM_MINMAX);//直方图比较double dis = compareHist(src_hist, temp_hist, HISTCMP_CORREL);return dis;}//彩色图else{//使用split进行通道分离vector<Mat>src_bgr, temp_bgr;split(src, src_bgr);split(temp, temp_bgr);int histSize = 256;float range[] = { 0,256 };const float*histRange = { range };//计算直方图Mat src_hist_b, src_hist_g, src_hist_r, temp_hist_b, temp_hist_g, temp_hist_r;calcHist(&src_bgr[0], 1, 0, Mat(), src_hist_b, 1, &histSize, &histRange);calcHist(&src_bgr[1], 1, 0, Mat(), src_hist_g, 1, &histSize, &histRange);calcHist(&src_bgr[2], 1, 0, Mat(), src_hist_r, 1, &histSize, &histRange);calcHist(&temp_bgr[0], 1, 0, Mat(), temp_hist_b, 1, &histSize, &histRange);calcHist(&temp_bgr[1], 1, 0, Mat(), temp_hist_g, 1, &histSize, &histRange);calcHist(&temp_bgr[2], 1, 0, Mat(), temp_hist_r, 1, &histSize, &histRange);//归一化normalize(src_hist_b, src_hist_b, 0, 1, NORM_MINMAX);normalize(src_hist_g, src_hist_g, 0, 1, NORM_MINMAX);normalize(src_hist_r, src_hist_r, 0, 1, NORM_MINMAX);normalize(temp_hist_b, temp_hist_b, 0, 1, NORM_MINMAX);normalize(temp_hist_g, temp_hist_g, 0, 1, NORM_MINMAX);normalize(temp_hist_r, temp_hist_r, 0, 1, NORM_MINMAX);vector<Mat>src_Mat = { src_hist_b ,src_hist_g ,src_hist_r };vector<Mat>temp_Mat = { temp_hist_b ,temp_hist_g ,temp_hist_r };//将b、g、r三通道进行合并Mat src_hist, temp_hist;merge(src_Mat, src_hist);merge(temp_Mat, temp_hist);//直方图比较double dis = compareHist(src_hist, temp_hist, HISTCMP_CORREL);return dis;}}

上述代码块可以帮助我们比较两幅图像直方图,以此来判断两幅图像的相似程度。关于compareHist API使用请自行百度。

效果

生成的蒙版图。
请添加图片描述
像素加权效果图。
请添加图片描述

二、基于均方误差(MSE)比较

//计算均方误差MSE
double getMSE(Mat src, Mat dst)
{double mse = 0.0;if (src.channels() == 1){for (int i = 0; i < src.rows; i++){for (int j = 0; j < src.cols; j++){double diff = pow(src.at<uchar>(i, j) - dst.at<uchar>(i, j), 2);mse += diff;}}}else{for (int i = 0; i < src.rows; i++){for (int j = 0; j < src.cols; j++){double b = pow(src.at<Vec3b>(i, j)[0] - dst.at<Vec3b>(i, j)[0], 2);double g = pow(src.at<Vec3b>(i, j)[1] - dst.at<Vec3b>(i, j)[1], 2);double r = pow(src.at<Vec3b>(i, j)[2] - dst.at<Vec3b>(i, j)[2], 2);double diff = b + g + r;mse += diff;}}}double MSE = mse / (src.rows*src.cols);  //均方误差return MSE;
}

我们通过计算MSE可以比较两幅图像的相似程度,MSE越小,表示两幅图像越相似;反之,MSE越大,则表示两幅图像越不相似。

效果

生成的蒙版图。
请添加图片描述
像素加权效果图。
请添加图片描述

三、源码

#include 
#include
using namespace std;
using namespace cv;const int step_x = 20;
const int step_y = 20;int getImagePathList(string folder, vector<String> &imagePathList)
{glob(folder, imagePathList);return 0;
}double calmyHist(Mat src, Mat temp)
{//灰度图if (src.channels() == 1){cvtColor(temp, temp, COLOR_BGR2GRAY);int histSize = 256;float range[] = { 0,256 };const float*histRange = { range };Mat src_hist, temp_hist;//计算直方图calcHist(&src, 1, 0, Mat(), src_hist, 1, &histSize, &histRange);calcHist(&temp, 1, 0, Mat(), temp_hist, 1, &histSize, &histRange);//归一化normalize(src_hist, src_hist, 0, 1, NORM_MINMAX);normalize(temp_hist, temp_hist, 0, 1, NORM_MINMAX);//直方图比较double dis = compareHist(src_hist, temp_hist, HISTCMP_CORREL);return dis;}//彩色图else{//使用split进行通道分离vector<Mat>src_bgr, temp_bgr;split(src, src_bgr);split(temp, temp_bgr);int histSize = 256;float range[] = { 0,256 };const float*histRange = { range };//计算直方图Mat src_hist_b, src_hist_g, src_hist_r, temp_hist_b, temp_hist_g, temp_hist_r;calcHist(&src_bgr[0], 1, 0, Mat(), src_hist_b, 1, &histSize, &histRange);calcHist(&src_bgr[1], 1, 0, Mat(), src_hist_g, 1, &histSize, &histRange);calcHist(&src_bgr[2], 1, 0, Mat(), src_hist_r, 1, &histSize, &histRange);calcHist(&temp_bgr[0], 1, 0, Mat(), temp_hist_b, 1, &histSize, &histRange);calcHist(&temp_bgr[1], 1, 0, Mat(), temp_hist_g, 1, &histSize, &histRange);calcHist(&temp_bgr[2], 1, 0, Mat(), temp_hist_r, 1, &histSize, &histRange);//归一化normalize(src_hist_b, src_hist_b, 0, 1, NORM_MINMAX);normalize(src_hist_g, src_hist_g, 0, 1, NORM_MINMAX);normalize(src_hist_r, src_hist_r, 0, 1, NORM_MINMAX);normalize(temp_hist_b, temp_hist_b, 0, 1, NORM_MINMAX);normalize(temp_hist_g, temp_hist_g, 0, 1, NORM_MINMAX);normalize(temp_hist_r, temp_hist_r, 0, 1, NORM_MINMAX);vector<Mat>src_Mat = { src_hist_b ,src_hist_g ,src_hist_r };vector<Mat>temp_Mat = { temp_hist_b ,temp_hist_g ,temp_hist_r };//将b、g、r三通道进行合并Mat src_hist, temp_hist;merge(src_Mat, src_hist);merge(temp_Mat, temp_hist);//直方图比较double dis = compareHist(src_hist, temp_hist, HISTCMP_CORREL);return dis;}}//计算均方误差MSE
double getMSE(Mat src, Mat dst)
{double mse = 0.0;if (src.channels() == 1){cvtColor(dst, dst, COLOR_BGR2GRAY);for (int i = 0; i < src.rows; i++){for (int j = 0; j < src.cols; j++){double diff = pow(src.at<uchar>(i, j) - dst.at<uchar>(i, j), 2);mse += diff;}}}else{for (int i = 0; i < src.rows; i++){for (int j = 0; j < src.cols; j++){double b = pow(src.at<Vec3b>(i, j)[0] - dst.at<Vec3b>(i, j)[0], 2);double g = pow(src.at<Vec3b>(i, j)[1] - dst.at<Vec3b>(i, j)[1], 2);double r = pow(src.at<Vec3b>(i, j)[2] - dst.at<Vec3b>(i, j)[2], 2);double diff = b + g + r;mse += diff;}}}double MSE = mse / (src.rows*src.cols);  //均方误差return MSE;
}int main()
{Mat src = imread("Taylor.jpg");if (src.empty()){cout << "No image!" << endl;system("pause");return 0;}resize(src, src, Size(step_x*30, step_y*30), 1, 1, INTER_CUBIC);vector<Mat>images;string filename = "images/";cout << "loading..." << endl;//素材照片vector<String> imagePathList;getImagePathList(filename, imagePathList);for (int i = 0; i < imagePathList.size(); i++){Mat img = imread(imagePathList[i]);resize(img, img, Size(step_x, step_y), 1, 1, INTER_AREA);images.push_back(img);}cout << "size:" << images.size() << endl;cout << "done!" << endl;int rows = src.rows;int cols = src.cols;//height:表示生成的蒙太奇图像需要多少张素材图像填充rows//width:表示生成的蒙太奇图像需要多少张素材图像填充colsint height = rows / step_y, width = cols / step_x;Mat dst = Mat(src.size(), CV_8UC3, Scalar(255, 255, 255));for (int i = 0; i < height; i++){for (int j = 0; j < width; j++){//计算当前ROI区域与图库中所有图片的直方图,找出最相似的一张作为填充ROI区域图片Mat ROI = src(Rect(j * step_x, i * step_y, step_x, step_y));double min =1000000.0;Mat result;for (int z = 0; z < images.size(); z++){double dis = getMSE(ROI, images[z]);if (dis < min){min = dis;result = images[z];}}//将图像赋值给需要生成的蒙太奇图像对应区域result.copyTo(dst(Rect(j * step_x, i * step_y, step_x, step_y)));	}}namedWindow("dst", WINDOW_NORMAL);imwrite("蒙版.jpg", dst);imshow("dst", dst);for (int i = 0; i < rows; ++i){for (int j = 0; j < cols; ++j){//像素RGB值修改dst.at<Vec3b>(i, j)[0] = 0.312*dst.at<Vec3b>(i, j)[0] + 0.688*src.at<Vec3b>(i, j)[0];dst.at<Vec3b>(i, j)[1] = 0.312*dst.at<Vec3b>(i, j)[1] + 0.688*src.at<Vec3b>(i, j)[1];dst.at<Vec3b>(i, j)[2] = 0.312*dst.at<Vec3b>(i, j)[2] + 0.688*src.at<Vec3b>(i, j)[2];}}namedWindow("蒙太奇图像", WINDOW_NORMAL);imwrite("蒙太奇图像.jpg", dst);imshow("蒙太奇图像", dst);waitKey(0);system("pause");return 0;
}

总结

本文使用OpenCV C++生成蒙太奇图像,关键步骤有以下几点。
1、基于直方图比较:找到与待填充区域(ROI)最相似的图片进行填充。
2、基于MSE比较:找到与待填充区域(ROI)最相似的图片进行填充。
对比以上两种方法,从效果上看,个人觉得使用MSE方法比较两幅图相似效果会更好。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部