OpenCV3入門(十四)圖像特效—擠壓、哈哈鏡、扭曲
- 2020 年 3 月 16 日
- 筆記
一、圖像擠壓特效
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
技術博客,轉載請註明。