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

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

前言

本文将使用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 <iostream>
      #include<opencv2/opencv.hpp>
      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_x30, step_y30), 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:表示生成的蒙太奇图像需要多少张素材图像填充cols
      int 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.312dst.at<Vec3b>(i, j)[0] + 0.688src.at<Vec3b>(i, j)[0];
      dst.at<Vec3b>(i, j)[1] = 0.312dst.at<Vec3b>(i, j)[1] + 0.688src.at<Vec3b>(i, j)[1];
      dst.at<Vec3b>(i, j)[2] = 0.312dst.at<Vec3b>(i, j)[2] + 0.688src.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方法比较两幅图相似效果会更好。

        • 0
          点赞
        • 0
          收藏
          觉得还不错? 一键收藏
        • 0
          评论
        评论
        添加红包

        请填写红包祝福语或标题

        红包个数最小为10个

        红包金额最低5元

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

        抵扣说明:

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

        余额充值