【对二寸照片的摧残】一:人脸马赛克

时间:2022-07-23
本文章向大家介绍【对二寸照片的摧残】一:人脸马赛克,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

一:前言废话

前两天在CSDN刷到个上首页推荐的利用python进行二寸照片换底色的博文,然后看着我丑不拉几(帅出天际)的二寸平头小照片,我也忍不住了。

想想从小学开始到上大学,拍了无数次一寸二寸的各种底色的证件照,每一个我都喜欢的不得了,但竟然没一份是留了电子版的,这可真是脑子进水了。

所以我只能每样留一张自己看了,但没电子版就意味着没法再印一样的证件照了。再看看我这越长越歪的颜值,再也拍不出以前的巅峰了。

所以我就手机拍一下然后各种PS,大概流程如下:

1:首先随便拍一张,保证清晰

2:然后剪切一下进行放大

3:然后进行图像透视矫正得到常规证件照

最后换底色就简简单单轻轻松松啦。

他们都说我拍的像刚从监狱出来一样,所以坚-决-不-露-脸-

今天整个流程的效果代码还没做完,但为了达到我暑期日更的小flag,今天就分享下如何给照片人脸打码。

这个功能用程序实现真的鸡肋的很呀,打码随便用手机点两下不就OK了,所以大家不必较真实用性,了解程序本身所涉及的知识点才是重点。

二:程序如何检测到人脸

人脸的识别当然方法很多啦,尤其是二寸照片这么简单直接的类型。不过实在没必要人脸检测,可以再观察下照片:

除了背景的蓝色就是衣服的白色后者头发的黑色还有皮肤的黄色,所以很好区分的嘛,可以利用直方图反投影,关于直方图反投影不清楚的可以点这篇:唉,再再再学一下直方图:直方图反投影

int main()
{
  //【1】读取原图片以及投影模板
  Mat srcImage, dstImage;
  srcImage = imread("肖像.jpg",33);
  imshow("原图", srcImage);

  Mat RoiImage = imread("投影模板.png");
  //【2】转HSV模型
  Mat HsvImage, RoiImage_HSV;
  cvtColor(srcImage, HsvImage, COLOR_BGR2HSV);
  cvtColor(RoiImage, RoiImage_HSV, COLOR_BGR2HSV);
  //【3】计算公路的直方图
  MatND roiHist; //直方图对象
  int dims = 2;  //特征数目(直方图维度)
  float hranges[] = { 0,180 }; //特征空间的取值范围
  float Sranges[] = { 0,256 };
  const float *ranges[] = { hranges,Sranges };
  int size[] = { 20,32 };  //存放每个维度的直方图的尺寸的数组
  int channels[] = { 0,1 };  //通道数
  calcHist(&RoiImage_HSV, 1, channels, Mat(), roiHist, dims, size, ranges);
  //【4】直方图归一化
  normalize(roiHist, roiHist, 0, 255, NORM_MINMAX);

  //【5】反向投影
  Mat proImage; //投影输出图像
  calcBackProject(&HsvImage, 1, channels, roiHist, proImage, ranges);
  imshow("投影", proImage);
  //图像掩码Mask操作
  Mat maskImage;
  threshold(proImage, maskImage, 1, 255, THRESH_BINARY);//对mask进行二值化,将mask进一步处理
  imshow("二值腌膜", maskImage);
  dstImage = Mat::zeros(srcImage.size(), CV_8UC3);
  srcImage.copyTo(dstImage, maskImage);
  imshow("掩码操作", dstImage);

  waitKey();
  return 0;
}

这段代码就是利用直方图得到了一幅mask图像,其中人脸部分为255,其余部分为0:

左图为直方图反投影之后得到的投影(灰度图),右图为对投影图进行二值化后得到的二值mask。

之后调用自定义打码函数对标记mask区域进行打码:

  //自定义打码函数
  Mat MosaicImage = trans2Mosaic(srcImage, maskImage, 10);
  imshow("马赛克效果图", MosaicImage);

是不是很好看!感觉我就像变成了X战警钻石女王那样似的~

自定义函数代码,函数输入顺次为原图,图像腌膜,马赛克大小:

//原图,腌膜,马赛克大小
Mat trans2Mosaic(Mat srcImage, Mat maskImage, int half_patch) {
  Point2d point;//
  Mat frame, mask;
  srcImage.copyTo(frame);
  maskImage.copyTo(mask);
  int height = frame.rows;//获取图像的长宽
  int width = frame.cols;
  //遍历图像,步长为马赛克大小
  for (int x = 0; x < height; x = x + half_patch) {
    for (int y = 0; y < width; y = y + half_patch) {
      //如果不是标记区域,则跳过
      if (mask.at<uchar>(x, y) ==0 ) {
        continue;
      }
      else { //如果是标记区域,对像素进行重赋值
        int b = theRNG().uniform(0, 255);
        int g = theRNG().uniform(0, 255);
        int r = theRNG().uniform(0, 255);
        for (int dx = 0; dx < half_patch; dx++) {
          for (int dy = 0; dy < half_patch; dy++) {
            frame.at<Vec3b>(x + dx, y + dy)[0] = (uchar)b;
            frame.at<Vec3b>(x + dx, y + dy)[1] = (uchar)g;
            frame.at<Vec3b>(x + dx, y + dy)[2] = (uchar)r;
          }
        }
      }
    }
  }
  return frame;
}

该函数思路很简单,就是遍历图像像素点,遍历的步长为自定义的马赛克大小,然后判断该像素点是否属于标记(人脸)区域,否则跳过,是则以改点为起点遍历改点左侧以及下侧一个马赛克大小的区域,并将该区域像素随机赋值。

THE END