Pinch Filter 產生向內/向外捏合的幾何變形,類似透過凸/凹透鏡觀看影像。中心區域被壓縮或拉伸,邊緣區域相對變形較小。
(centreX, centreY)(歸一化 0~1)、半徑 radius、扭曲強度 amount、旋轉角度 angle。(x, y),計算相對中心向量 (dx, dy) 及距離平方 distance2。new_x=x, new_y=y。d = sqrt(distance2) / radius。t = pow(sin(π/2 * d), -amount)。amount > 0 向內捏合,amount < 0 向外膨脹。更新 dx *= t, dy *= t。a = angle * (1-d)^2,距離中心越遠旋轉越小。套用旋轉矩陣:new_x = cx + cos(a)*dx - sin(a)*dy,new_y = cy + sin(a)*dx + cos(a)*dy。radius=0 時自動設為 min(width, height)/2。amount=0.5 為中等捏合強度。[0, width-2] × [0, height-2] 確保雙線性插值不越界。#include <opencv2/opencv.hpp>
#include <cmath>
using namespace cv;
using namespace std;
void showImage(const Mat& img, const string& name) {
namedWindow(name, WINDOW_AUTOSIZE);
imshow(name, img);
}
int main() {
Mat src = imread("4.jpg");
if (src.empty()) return -1;
Mat dst(src.size(), src.type());
int w = src.cols, h = src.rows;
float cx = w * 0.5f, cy = h * 0.5f;
float radius = 200.0f;
if (radius == 0) radius = min(w, h) * 0.5f;
float radius2 = radius * radius;
float amount = 0.5f;
float angle = CV_PI / 2;
for (int y = 0; y < h; ++y) {
for (int x = 0; x < w; ++x) {
float dx = x - cx, dy = y - cy;
float dist2 = dx*dx + dy*dy;
float nx = x, ny = y;
if (dist2 < radius2 && dist2 > 0) {
float d = sqrt(dist2) / radius;
float t = pow(sin(CV_PI * 0.5f * d), -amount);
dx *= t; dy *= t;
float e = 1.0f - d;
float a = angle * e * e;
float s = sin(a), c = cos(a);
nx = cx + c*dx - s*dy;
ny = cy + s*dx + c*dy;
}
// 邊界裁剪
nx = max(0.0f, min(nx, w - 2.0f));
ny = max(0.0f, min(ny, h - 2.0f));
int x1 = (int)nx, y1 = (int)ny;
float px = nx - x1, py = ny - y1;
// 雙線性插值
for (int k = 0; k < 3; ++k) {
dst.at<Vec3b>(y, x)[k] = saturate_cast<uchar>(
(1-px)*(1-py)*src.at<Vec3b>(y1, x1)[k] +
px*(1-py)*src.at<Vec3b>(y1, x1+1)[k] +
(1-px)*py*src.at<Vec3b>(y1+1, x1)[k] +
px*py*src.at<Vec3b>(y1+1, x1+1)[k]
);
}
}
}
showImage(dst, "Pinch Effect");
waitKey(0);
return 0;
}
