我绝对是无聊爆炸了,所以我又丧心病狂处理二次元图片了。
今天基于像素变换来实现图像的哈哈镜变换,效果就是下面这样了:
哈哈镜分两种,一种是挤压,一种是放大。分别对应凹函数和凸函数。
输入一副图像,首先设置缩放中心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