一、图像挤压特效
1、原理
图像压效果本质的图像坐标的非线性变换,将图像向内挤压,挤压的过程产生压缩变形,从而形成的效果。
挤压效果的实现是通过极坐标的形式,设图像中心为O(x,y),某点距离中心O的距离为半径R,非线性方式改变半径R但不改变点的方向,就构成了图像挤压。也可以自定义加压中心点,计算半径方式相同。
图像像素变换倍率使用 y=sqrt(x)。
图像上点P与图像中心O的距离为R,图像挤压就是P点坐标映射到OP直线上的点R2位置,其中| OR2 |=sqrt(OP)*ratio。
2、实现
代码语言:javascript复制void Pinch(Mat& img, Mat& dst, int degree)
{
if (degree < 1) degree = 1;
if (degree > 32) degree = 32;
if (dst.empty())
dst.create(img.rows, img.cols, img.type());
dst = cv::Scalar::all(0);
int chns = img.channels();
int height = img.rows;
int width = img.cols;
int midX = width / 2;
int midY = height / 2;
int i, j, k;
int X, Y, offsetX, offsetY;
double radian, radius; //弧和半径
for (i = 0; i < height; i )
{
for (j = 0; j < width; j )
{
offsetX = j - midX;
offsetY = i - midY;
radian = atan2((double)offsetY, (double)offsetX);
// 半径
radius = sqrtf((float)(offsetX*offsetX offsetY * offsetY));
radius = sqrtf(radius)*degree;
X = (int)(radius*cos(radian)) midX;
Y = (int)(radius*sin(radian)) midY;
if (X < 0) X = 0;
if (X >= width) X = width - 1;
if (Y < 0) Y = 0;
if (Y >= height) Y = height - 1;
for (k = 0; k < chns; k )
{
dst.at<Vec3b>(i, j)[k] = img.at<Vec3b>(Y, X)[k];
}
}
}
}
Mat src_img;
Mat dst_img;
int rato = 15;
void call_back(int, void*)
{
Pinch(src_img, dst_img, rato);
imshow("Pinch图", dst_img);
}
int main() {
src_img = imread("D:\WORK\5.OpenCV\LeanOpenCV\pic_src\pic18.bmp");
imshow("原图", src_img);
Pinch(src_img, dst_img, rato);
imshow("Pinch图", dst_img);
namedWindow("Pinch图");
createTrackbar("Pinch倍率", "Pinch图", &rato, 50, call_back);
call_back(rato, 0);
waitKey(0);
}
3、测试效果
测试1:
测试2:不同倍率下棋盘格的挤压效果。
二、哈哈镜特效
1、原理
图像坐标的非线性变换,实现k的根号与k的比值,sqrt(k)/k, 当k为1时总倍率为1,当k小于1时,总倍率为渐变倍率。
2、实现
代码语言:javascript复制void Pinch(Mat& img, Mat& dst, int x, int y, int degree)
{
if (dst.empty())
dst.create(img.rows, img.cols, img.type());
dst = cv::Scalar::all(0);
cout << "x,y " << x << " " << y << endl;
int chns = img.channels();
int height = img.rows;
int width = img.cols;
midX = x;
midY = y;
int R = 100;
int i, j, k;
int X, Y, offsetX, offsetY;
double radian, radius; //弧和半径
for (i = 0; i < height; i )
{
for (j = 0; j < width; j )
{
offsetX = j - midX;
offsetY = i - midY;
radian = atan2((double)offsetY, (double)offsetX);
// 半径
radius = sqrtf((float)(offsetX*offsetX offsetY * offsetY));
if (radius <= R && radius > 1) {
float k = sqrtf(radius/R) * radius / R * degree;
X = (int)( cos(radian) * k) midX;
Y = (int)( sin(radian) * k) midY;
if (X < 0) X = 0;
if (X >= width) X = width - 1;
if (Y < 0) Y = 0;
if (Y >= height) Y = height - 1;
for (k = 0; k < chns; k )
{
dst.at<Vec3b>(i, j)[k] = img.at<Vec3b>(Y, X)[k];
}
}
else
{
for (k = 0; k < chns; k )
{
dst.at<Vec3b>(i, j)[k] = img.at<Vec3b>(i, j)[k];
}
}
}
}
cout << " midX, midY " << midX << " " << midY << endl;
}
3、测试效果
测试1:哈哈镜效果
测试2:大倍率呈现潜望镜效果。
测试3:
三、图像扭曲
对图像的像素坐标进行正弦变换,映射到对应坐标就完成了图像扭曲。关键代码如下:
代码语言:javascript复制for (int j = 0; j < width; j )
{
double temp = degree * sin(1.0 * j / width * pi * T ); // [-degree,degree]
temp = degree temp; // [0, 2*degree]
for (int i = int(temp 0.5); i < height temp - 2 * degree; i )
{
X = (int)((i - temp) * (height) / (height - degree));
if (X >= img.rows)
X = img.rows - 1;
if (X < 0)
X = 0;
for (int c = 0; c < chns; c )
{
dst.at<Vec3b>(i, j)[c] = img.at<Vec3b>(X, j)[c];
}
}
}
测试1:
测试2:
4、参考文献
1、《学习OpenCV》,清华大学出版社,Gary Bradski, Adrian kaehler著
2、仿射变换
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/imgtrans/warp_affine/warp_affine.html
3、PhotoShop算法实现高级篇--挤压特效(三十六)
https://blog.csdn.net/kezunhai/article/details/41873775