OpenCV3入门(十四)图像特效—挤压、哈哈镜、扭曲
一、图像挤压特效
1、原理
图像压效果本质的图像坐标的非线性变换,将图像向内挤压,挤压的过程产生压缩变形,从而形成的效果。
挤压效果的实现是通过极坐标的形式,设图像中心为O(x,y),某点距离中心O的距离为半径R,非线性方式改变半径R但不改变点的方向,就构成了图像挤压。也可以自定义加压中心点,计算半径方式相同。
图像像素变换倍率使用 y=sqrt(x)。
图像上点P与图像中心O的距离为R,图像挤压就是P点坐标映射到OP直线上的点R2位置,其中| OR2 |=sqrt(OP)*ratio。
2、实现
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、实现
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:
三、图像扭曲
对图像的像素坐标进行正弦变换,映射到对应坐标就完成了图像扭曲。关键代码如下:
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、仿射变换
3、PhotoShop算法实现高级篇--挤压特效(三十六)
https://blog.csdn.net/kezunhai/article/details/41873775
技术博客,转载请注明。