哈哈哈哈哈哈镜~

时间:2022-07-23
本文章向大家介绍哈哈哈哈哈哈镜~,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

我绝对是无聊爆炸了,所以我又丧心病狂处理二次元图片了。

今天基于像素变换来实现图像的哈哈镜变换,效果就是下面这样了:

哈哈镜分两种,一种是挤压,一种是放大。分别对应凹函数和凸函数。

输入一副图像,首先设置缩放中心center,我们取原图鼻子处为中心。

设置图像上任意一点到中心点的相对坐标tx= x-cx,ty=y-cy。

左边为挤压哈哈镜,对应像素映射函数:

//变换后新的坐标
x = cos(atan2(ty , tx))* 12*(sqrt(tx*tx + ty*ty))+cx
y = sin(atan2(ty , tx))* 12*(sqrt(tx*tx + ty*ty))+cy

公式中的常数12代表强度,越大则图像越扭曲。

自定义挤压函数(C++版)(Python版可以点击文末阅读原文查看):

Mat MinFrame(Mat frame) {
  Mat srcImage;
  frame.copyTo(srcImage);
  int radius = 400;//定义哈哈镜的半径

  int height = frame.rows;
  int width = frame.cols;  //图片的长宽
  Point2d center;//人脸中心
  center.x = 130;
  center.y = 180;

  int newX, newY;//变换后的坐标

  for (int x = 0; x < width; x++)
    for (int y = 0; y < height; y++) {
      double tX = x - center.x;
      double tY = y - center.y;

      double theta = atan2(tY, tX);
      radius = sqrt((tX * tX) + (tY * tY)); //与上面一样,计算公式不一样

      int newR = sqrt(radius) *8;
      newX = int(center.x + (newR * cos(theta)));
      newY = int(center.y + (newR * sin(theta)));

      if (newX<0 && newX>width)
        newX = 0;
      if (newY<0 && newY>height)
        newY = 0;

      if (newX < width && newY < height) {
        srcImage.at<Vec3b>(y, x)[0] = frame.at<Vec3b>(newY, newX*3)[0];    //将计算后的坐标移动到原坐标
        srcImage.at<Vec3b>(y, x)[1] = frame.at<Vec3b>(newY, newX*3+1)[1];
        srcImage.at<Vec3b>(y, x)[2] = frame.at<Vec3b>(newY, newX*3+2)[2];
      }
      else {
        srcImage.at<Vec3b>(y, x)[0] = frame.at<Vec3b>(y, x)[0];  //将计算后的坐标移动到原坐标
        srcImage.at<Vec3b>(y, x)[1] = frame.at<Vec3b>(y, x)[1];
        srcImage.at<Vec3b>(y, x)[2] = frame.at<Vec3b>(y, x)[2];
      }
    }
  return srcImage;
}

右边为放大哈哈镜,对应像素映射函数:

//变换后的新坐标
x = (tx/2)*(sqrt(tx*tx + ty*ty)/radius)+cx
y = (ty/2)*(sqrt(tx*tx + ty*ty)/radius)+cy

自定义放大函数(C++版):

Mat MaxFrame(Mat frame) {
  Mat srcImage;
  frame.copyTo(srcImage);

  int radius = 400;//定义哈哈镜的半径
  int height = frame.rows;
  int width = frame.cols;  //图片的长宽
  Point2d center;//图片中心
  center.x = 130;//人脸中心像素坐标
  center.y = 180;

  int newX, newY;//变换后的坐标
  int real_radius = int(radius / 2.0);  //计算公式

  for (int x = 0; x < width; x++) 
    for (int y = 0; y < height; y++) {
      int tX = x - center.x;
      int tY = y - center.y;

      int distance = tX * tX + tY * tY;
      if (distance < radius*radius) {
        newX = int(tX / 2.0);
        newY = int(tY / 2.0);

        newX = int(newX * (sqrt(distance) / real_radius));
        newY = int(newY * (sqrt(distance) / real_radius));

        newX = int(newX + center.x);
        newY = int(newY + center.y);
        if (newX < width && newY < height) {
          srcImage.at<Vec3b>(y, x)[0] = frame.at<Vec3b>(newY, newX)[0]; //将计算后的坐标移动到原坐标
          srcImage.at<Vec3b>(y, x)[1] = frame.at<Vec3b>(newY, newX)[1];
          srcImage.at<Vec3b>(y, x)[2] = frame.at<Vec3b>(newY, newX)[2];
        }
      }
      else {
        srcImage.at<Vec3b>(y, x)[0] = frame.at<Vec3b>(y, x)[0]; //将计算后的坐标移动到原坐标
        srcImage.at<Vec3b>(y, x)[1] = frame.at<Vec3b>(y, x)[1];
        srcImage.at<Vec3b>(y, x)[2] = frame.at<Vec3b>(y, x)[2];
      }
    }
  return srcImage;
}

至于这俩函数怎么来的,,我也说不出啥门道来,,欢迎评论告知哈。

再看最后一眼:

THE END