Skip to content

catchcodes/DIP_GUI

Repository files navigation

数字图像处理C++

 LICENSE  Github stars


 _______                     __________ 
/  .__. \ ____   ____ ___.__.\______   \
|  |  | |/  _ \ /  _ \  ||  | |    |  _/
|  |__|  ( <_> )  <_> )___  | |    |   \
 \______/\____/ \____// ____| |______  /
                      \/             \/ 

Demo

Demo

Requirements

This project is based on Visual Studio 2022 IDE
Using Opencv 4.7.0 ; Qt 5.14.2 ; Qt VS Tool 2.10.1 ; Python 3.10

Description

 depict
功能函数

  • 直方图均衡化 : $ s_k = \displaystyle \sum_{j=0}^{k}\frac{n_j}{n}$ 累计频率 哪些灰度数量多,映射曲线越陡峭,均衡后越均匀

  • 拉普拉斯锐化 : $ g(x,y) = f(x,y)+[8\cdot f(x,y)-\displaystyle \sum_{D8}f(i,j)] $ 当邻域中心像素灰度低于它所在的领域内其它像素的平均灰度时,此中心像素的灰度应被进一步降低,当邻域中心像素灰度高于它所在的邻域内其它像素的平均灰度时,此中心像素的灰度应被进一步提高

  • 频域滤波 : DFT: $ F(u,v) = \frac{1}{MN}\displaystyle \sum_{x=0}^{M-1} \sum_{y=0}^{N-1}f(x,y)\exp{-j2\pi (\frac{ux}{M}+\frac{vy}{N}) }$

Python部分

  • 人脸特征点检测 :
  1. 调用Dlib的HOG人脸检测(将图像分割成一些 16×16 像素的小方块,计算指向性最强的梯度方向)

HOG

  1. 用训练好的81个人脸关键点位置模型去寻找人脸中的81个特征点

mark


UI部分
1、用Qt Designer设计好初步界面
2、连接Button、SpinBox等信号与相应的槽函数,信号产生调用对应槽函数,在槽函数中进行相关功能函数的调用

Function Codes

Codes of Digital-Image-Processing homework

  • 直方图均衡化

    代码展开
    
      // 直方图均衡化的定义
      void HistEqual(Mat& gray, Mat& result)
      {
          // 哈希表统计0~255像素值的个数
          mappixelCounter;
          for (int i = 0; i < gray.rows; i++)
          {
              for (int j = 0; j < gray.cols; j++)
              {
                  int value = gray.at(i, j);
                  pixelCounter[value]++;
              }
          }
          //统计0~255像素值的频率,并计算累计频率
          map pixel_fre;
          int pixel_sum = gray.cols * gray.rows;
          double cumul_fre = 0;
          for (int i = 0; i < 256; i++)
          {
              // 累计频率 哪些灰度数量多,映射曲线越陡峭,均衡后越均匀
              cumul_fre += double(pixelCounter[i]) / pixel_sum;
              pixel_fre[i] = cumul_fre;
          }
          //根据累计频率进行转换
          for (int i = 0; i < gray.rows; i++)
          {
              for (int j = 0; j < gray.cols; j++)
              {
                  int value = gray.at(i, j);
                  double fre = pixel_fre[value];
                  // 原始灰度值乘以累计频率
                  result.at(i, j) = fre * value;
              }
          }
      }
    
  • 拉普拉斯锐化

    代码展开
    
      // 默认0填充
      void Laplacian(Mat& gray, Mat& result, int padding)
      {
          //result.convertTo(result, CV_64F);
          Mat gray_buf(gray.rows + 2, gray.cols + 2, gray.depth());
          // 0填充
          if (padding == 0)
          {
              cv::copyMakeBorder(gray, gray_buf, 1, 1, 1, 1, cv::BORDER_CONSTANT);
          }
          // 镜像填充
          else if (padding == 1)
          {
              cv::copyMakeBorder(gray, gray_buf, 1, 1, 1, 1, cv::BORDER_REFLECT);
          }
          for (int i = 0; i < gray.rows; i++)
          {
              for (int j = 0; j < gray.cols; j++)
              {
                  // cv::saturate_cast()保证范围为0~255
                  // 直接访问
                  result.at(i, j) = cv::saturate_cast(gray.at(i, j) + 8 * gray_buf.at(i + 1, j + 1) - gray_buf.at(i, j) - gray_buf.at(i, j + 1) - gray_buf.at(i, j + 2) \
                      - gray_buf.at(i + 1, j) - gray_buf.at(i + 1, j + 2) - gray_buf.at(i + 2, j) - gray_buf.at(i + 2, j + 1) - gray_buf.at(i + 2, j + 2));
              }
          }
      }
    
  • 理想低通滤波器

    代码展开
    
      // 理想低通滤波器
      void ILPF(Mat& gray, Mat& result, int fc)
      {
      	// 扩展图像矩阵,为2,3,5的倍数时运算速度快
      	int m = cv:: getOptimalDFTSize(gray.rows);
      	int n = cv::getOptimalDFTSize(gray.cols);
      	Mat padded;
      	// 零填充
      	cv::copyMakeBorder(gray, padded, 0, m - gray.rows, 0, n - gray.cols, cv::BORDER_CONSTANT);
      	padded.convertTo(padded, CV_32FC1);
      	int row = padded.rows;
      	int col = padded.cols;
      	if (fc > MIN(row, col))	
      		throw "截止频率超出图像范围";
      	Mat filter = Mat::zeros(padded.size(), CV_32FC1);
      	for (int i = 0; i < row; i++)
      	{
      		for (int j = 0; j < col; j++)
      		{
      			double d = sqrt(pow((i - row / 2.0), 2) + pow((j - col / 2.0), 2));
      			if (d <= fc)
      			{
      				filter.at(i, j) = 1;
      			}
      		}
      	}
      	// imshow("滤波器", filter);
      	// 实部和虚部
      	Mat plane[] = {padded, Mat::zeros(padded.size(), CV_32FC1)};
      	Mat complexIm;
      	merge(plane, 2, complexIm); //合并通道 (把两个矩阵合并为一个2通道的Mat类容器)
      	dft(complexIm, complexIm);  //进行傅立叶变换,结果保存在自身
      	split(complexIm, plane);    //分离通道
      	fftshift(plane[0], plane[1]);
      	Mat Real, Imag, BLUR;
      	Real = plane[0].mul(filter);
      	Imag = plane[1].mul(filter);
      	// fftshift(Real, Imag); //效果一样 周期性
      	Mat plane1[] = { Real, Imag };
      	merge(plane1, 2, BLUR);//实部与虚部合并
      	idft(BLUR, BLUR);
      	split(BLUR, plane);                     //分离通道,主要获取通道
      	magnitude(plane[0], plane[1], result);  //求幅值(模)
      	normalize(result, result, 0, 1.0, NORM_MINMAX);  //归一化便于显示
      }
      // 低频移动到中心
      void fftshift(Mat plane0, Mat plane1)
      {
      	// -2 : 1111_……_1110
      	plane0 = plane0(Rect(0, 0, plane0.cols & -2, plane0.rows & -2));
      	int cx = plane0.cols / 2;
      	int cy = plane0.rows / 2;
      	Mat part1_r(plane0, Rect(0, 0, cx, cy));
      	Mat part2_r(plane0, Rect(cx, 0, cx, cy));
      	Mat part3_r(plane0, Rect(0, cy, cx, cy));
      	Mat part4_r(plane0, Rect(cx, cy, cx, cy));
      	Mat temp;
      	part1_r.copyTo(temp);  //左上与右下交换位置(实部)
      	part4_r.copyTo(part1_r);
      	temp.copyTo(part4_r);
      	part2_r.copyTo(temp);  //右上与左下交换位置(实部)
      	part3_r.copyTo(part2_r);
      	temp.copyTo(part3_r);
      	Mat part1_i(plane1, Rect(0, 0, cx, cy));  //元素坐标(cx,cy)
      	Mat part2_i(plane1, Rect(cx, 0, cx, cy));
      	Mat part3_i(plane1, Rect(0, cy, cx, cy));
      	Mat part4_i(plane1, Rect(cx, cy, cx, cy));
      	part1_i.copyTo(temp);  //左上与右下交换位置(虚部)
      	part4_i.copyTo(part1_i);
      	temp.copyTo(part4_i);
      	part2_i.copyTo(temp);  //右上与左下交换位置(虚部)
      	part3_i.copyTo(part2_i);
      	temp.copyTo(part3_i);
      }
    
  • 巴特沃斯低通滤波器

    代码展开
    
      // 巴特沃斯的滤波器
    	Mat filter = Mat::zeros(padded.size(), CV_32FC1);
      for (int i = 0; i < row; i++)
      {
      	//float* data = filter.ptr(i);
      	for (int j = 0; j < col; j++)
      	{
      		float d = sqrt(pow((i - row / 2.0), 2) + pow((j - col / 2.0), 2));
      		filter.at(i, j) = 1 / (1 + pow(float(d / fc), 2 * level));
      	}
      }
    
  • 高斯低通滤波器

    代码展开
    
      // 高斯滤波器
    	Mat filter = Mat::zeros(padded.size(), CV_32FC1);
      for (int i = 0; i < row; i++)
      {
      	for (int j = 0; j < col; j++)
      	{
      		float d = sqrt(pow((i - row / 2.0), 2) + pow((j - col / 2.0), 2));
      		filter.at(i, j) = exp(-pow(d, 2) / (2 * pow(fc, 2)));
      	}
      }
    
  • 人脸检测(Python)

    代码展开
    
    # HOG人脸检测器寻找人脸
    def find_face(filename):
      face_detector = dlib.get_frontal_face_detector()  # 创建HOG人脸检测器
      image = cv2.imread(filename)
      detected_faces = face_detector(image, 1)
      # 存储人脸矩形框的坐标信息
      location = []
      for i, face_rect in enumerate(detected_faces):
          location.append(face_rect.left())
          location.append(face_rect.top())
          location.append(face_rect.right())
          location.append(face_rect.bottom())
      return location
    
  • 人脸81个特征点位置(Python)

    代码展开
    
    def find_face_landmarks(filename):
      # 人脸81个关键点模型位置 
      predictor_model = r"E:\Face_Landmarks\shape_predictor_81_face_landmarks.dat"
      face_detector = dlib.get_frontal_face_detector()  # 创建HOG人脸检测器
      face_pose_predictor = dlib.shape_predictor(predictor_model)  # 创建人脸特征点检测器
      image = cv2.imread(filename)
      detected_faces = face_detector(image, 1)
      mark = []
      for i, face_rect in enumerate(detected_faces):
          pose_landmarks = face_pose_predictor(image, face_rect)  # 获取面部的姿势
          for j in range(81):
      	    # 以tuple形式存放到mark列表中,方便C++调用
              mark.append((pose_landmarks.part(j).x, pose_landmarks.part(j).y))            
      return mark
    
  • UI部分

    代码展开
    
      // 接收并响应拖拽事件
      // 过滤非图片文件
      void DIP_GUI::dragEnterEvent(QDragEnterEvent* event)
      {
          QStringList FileTypes;
          FileTypes.append("jpg");
          FileTypes.append("png");
          FileTypes.append("bmp");
          if (event->mimeData()->hasUrls() && event->mimeData()->urls().count() == 1) 
          {
              // 对象是否可以返回URL列表,并且只有一个
              QFileInfo file(event->mimeData()->urls().at(0).toLocalFile());
              // 在FileTypes查找文件后缀是否符合
              if (FileTypes.contains(file.suffix().toLower())) 
                  event->acceptProposedAction();
          }
      }
      // 响应拖拽事件
      void DIP_GUI::dropEvent(QDropEvent* event)
      {
          // 接收文件
          QString url = event->mimeData()->urls().first().toLocalFile();
          if (url.isEmpty()) 
              return;
          // 具体将拿到的数据进行处理
          QImage img;
          img.load(url);
          // 设置图片适应QLabel大小
          img.scaled(ui.originImg->size(), Qt::KeepAspectRatio);
          ui.originImg->setScaledContents(true);
          ui.originImg->setPixmap(QPixmap::fromImage(img));
          // 保存图片路径
          filepath = url.toStdString();
      }
      void DIP_GUI::setupView()
      {
          this->setAcceptDrops(true);    //可以接收图片
      }
      // 连接信号与槽函数
      connect(ui.histbtn, &QPushButton::clicked, this, &DIP_GUI::hist);
      connect(ui.laplacebtn, &QPushButton::clicked, this, &DIP_GUI::laplace);
      connect(ui.ilpfbtn, &QPushButton::clicked, this, &DIP_GUI::ideal);
      connect(ui.blpfbtn, &QPushButton::clicked, this, &DIP_GUI::butter);
      connect(ui.glpfbtn, &QPushButton::clicked, this, &DIP_GUI::gauss);
      connect(ui.face, &QPushButton::clicked, this, &DIP_GUI::fac);
      // Lamda表达式
      connect(ui.fc_i, &QSpinBox::editingFinished, this, [&]() {fc = ui.fc_i->value(); });
      connect(ui.fc_b, &QSpinBox::editingFinished, this, [&]() {fc = ui.fc_b->value(); });
      connect(ui.fc_g, &QSpinBox::editingFinished, this, [&]() {fc = ui.fc_g->value(); });
    

ToDoList

  • 人脸部分具体处理
  • 人脸眼部、鼻部、唇部、发际线进行可选性处理
  • 对视频中的人脸进行滤波

Bug Buddha

                            _ooOoo_
                           o8888888o
                           88" . "88
                           (| -_- |)
                            O\ = /O
                        ____/`---'\____
                      .   ' \\| |// `.
                       / \\||| : |||// \
                     / _||||| -:- |||||- \
                       | | \\\ - /// | |
                     | \_| ''\---/'' | |
                      \ .-\__ `-` ___/-. /
                   ___`. .' /--.--\ `. . __
                ."" '< `.___\_<|>_/___.' >'"".
               | | : `- \`.;`\ _ /`;.`/ - ` : | |
                 \ \ `-. \_ __\ /__ _/ .-` / /
         ======`-.____`-.___\_____/___.-`____.-'======
                            `=---='

         .............................................
                  佛祖镇楼                  BUG辟易
          佛曰:
                  V我50
                  v我50
                  V我50