原理:
https://blog.csdn.net/shandianfengfan/article/details/113706496
https://blog.csdn.net/qq_38517015/article/details/99724916
https://blog.csdn.net/qq_38517015/article/details/99724916
三次B样条
cubicBSplineFFD .h
#pragma once
#include "BaseFFD.h"
class cubicBSplineFFD :
public BaseFFD
{
public:
cubicBSplineFFD() ;
virtual ~cubicBSplineFFD();
virtual void transform(Mat& srcimg, Mat& dstimg, std::vector<std::vector<Point2D>>& ctrlPoints, int col_block_num, int row_block_num) override;
virtual void test();
};
cubicBSplineFFD .cpp
#include "cubicBSplineFFD.h"
cubicBSplineFFD::cubicBSplineFFD()
{
}
cubicBSplineFFD::~cubicBSplineFFD()
{
}
//================================================================
// 函数功能: 三次样条基函数
//
//================================================================
inline double N03(double t)
{
return 1.0 / 6 * (-t * t * t + 3 * t * t - 3 * t + 1);
}
inline double N13(double t)
{
return 1.0 / 6 * (3 * t * t * t - 6 * t * t + 4);
}
inline double N23(double t)
{
return 1.0 / 6 * (-3 * t * t * t + 3 * t * t + 3 * t + 1);
}
inline double N33(double t)
{
return 1.0 / 6 * t * t * t;
}
void cubicBSplineFFD::transform(Mat& srcimg, Mat& dstimg, std::vector<std::vector<Point2D>>& ctrlPoints, int col_block_num, int row_block_num)
{
dstimg.create(srcimg.size(), srcimg.type());
float blockWidth = srcimg.cols * 1.0 / col_block_num;
float blockHeight = srcimg.rows * 1.0 / row_block_num;
for (int y = 0; y < srcimg.rows; y++) //B_spline 变形
{
for (int x = 0; x < srcimg.cols; x++)
{
int XBlockIndex, YBlockIndex;
double u, v;
calculeLocalCoordinates(blockWidth, blockHeight, x, y, XBlockIndex, YBlockIndex, u, v);
// 缓存基函数的值
double pX[4], pY[4];
pX[0] = N03(u);
pX[1] = N13(u);
pX[2] = N23(u);
pX[3] = N33(u);
pY[0] = N03(v);
pY[1] = N13(v);
pY[2] = N23(v);
pY[3] = N33(v);
double Tx = 0, Ty = 0;
for (int i = 0; i < 4; i++)
{
for (int j = 0; j < 4; j++)
{
// 使用缓存的值
Tx += pX[i] * pY[j] * ctrlPoints[XBlockIndex + i][YBlockIndex + j].getX();
Ty += pX[i] * pY[j] * ctrlPoints[XBlockIndex + i][YBlockIndex + j].getY();
}
}
float src_x = x + Tx;
float src_y = y + Ty;
uchar gray = bilinearInterpolation(src_x, src_y, srcimg);
dstimg.at<uchar>(y, x) = gray;
}
}
}
void cubicBSplineFFD::test()
{
Mat img = imread("C:\\Users\\Lenovo\\Pictures\\1.png", cv::IMREAD_GRAYSCALE);
Mat out;
int row_block_num = 10;
int col_block_num = 10;
std::vector<std::vector<Point2D>> ctrlPoints(row_block_num + 3, std::vector<Point2D>(col_block_num + 3));
init_bpline_para(row_block_num, col_block_num, ctrlPoints, -10, 10);
transform(img, out, ctrlPoints, row_block_num, col_block_num);
imshow("img", img);
imshow("out", out);
waitKey();
}
int main() {
cubicBSplineFFD cubicBSpline;
cubicBSpline.test();
}
二次B样条
BiQuadFFD.h
#pragma once
#include "BaseFFD.h"
/*
FFD 二次B样条(均匀)
*/
class BiQuadFFD :
public BaseFFD
{
public:
BiQuadFFD();
virtual ~BiQuadFFD();
virtual void transform(Mat& srcimg, Mat& dstimg, std::vector<std::vector<Point2D>>& ctrlPoints, int col_block_num, int row_block_num) override;
virtual void test();
};
BiQuadFFD.cpp
#include "BiQuadFFD.h"
BiQuadFFD::BiQuadFFD()
{
}
BiQuadFFD::~BiQuadFFD()
{
}
inline double N02(double t) {
return 0.5 * (t - 1) * (t - 1);
}
inline double N12(double t) {
return 0.5 * (-2 * t * t + 2 * t + 1);
}
inline double N22(double t) {
return 0.5 * t * t;
}
void BiQuadFFD::transform(Mat& srcimg, Mat& dstimg, std::vector<std::vector<Point2D>>& ctrlPoints, int col_block_num, int row_block_num)
{
dstimg.create(srcimg.size(), srcimg.type());
float blockWidth = srcimg.cols * 1.0 / col_block_num;
float blockHeight = srcimg.rows * 1.0 / row_block_num;
for (int y = 0; y < srcimg.rows; y++) //B_spline 变形
{
for (int x = 0; x < srcimg.cols; x++)
{
int XBlockIndex, YBlockIndex;
double u, v;
calculeLocalCoordinates(blockWidth, blockHeight,x,y, XBlockIndex, YBlockIndex, u, v);
// 缓存基函数的值
double pX[3], pY[3];
pX[0] = N02(u);
pX[1] = N12(u);
pX[2] = N22(u);
pY[0] = N02(v);
pY[1] = N12(v);
pY[2] = N22(v);
double Tx = 0, Ty = 0;
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 3; j++)
{
// 使用缓存的值
Tx += pX[i] * pY[j] * ctrlPoints[XBlockIndex+i][YBlockIndex+j].getX();
Ty += pX[i] * pY[j] * ctrlPoints[XBlockIndex+i][YBlockIndex+j].getY();
}
}
float src_x = x + Tx;
float src_y = y + Ty;
uchar gray = bilinearInterpolation(src_x, src_y , srcimg);
dstimg.at<uchar>(y, x) = gray;
}
}
}
void BiQuadFFD::test()
{
Mat img = imread("C:\\Users\\Lenovo\\Pictures\\1.png", cv::IMREAD_GRAYSCALE);
Mat out;
int row_block_num = 10;
int col_block_num = 10;
std::vector<std::vector<Point2D>> ctrlPoints(row_block_num + 2, std::vector<Point2D>(col_block_num + 2));
init_bpline_para(row_block_num, col_block_num, ctrlPoints, -10, 10);
transform(img, out, ctrlPoints, row_block_num, col_block_num);
imshow("img", img);
imshow("out", out);
waitKey();
}
基础类BaseFFD
BaseFFD.h
#pragma once
#include"Point2D.h"
#include<opencv2/opencv.hpp>
#include<opencv2/core/core.hpp>
#include<vector>
using namespace cv;
#define randf(a, b) (((rand()%10000+rand()%10000*10000)/100000000.0)*((b)-(a))+(a))
class BaseFFD
{
public:
BaseFFD() {};
virtual~BaseFFD() {};
virtual void transform(Mat& srcimg, Mat& dstimg, std::vector<std::vector<Point2D>> & ctrlPoints, int col_block_num, int row_block_num) = 0;
virtual void test() {};
void calculeLocalCoordinates(double blockWidth,double blockHeight, int x,int y,
int &XBlockIndex, int& YBlockIndex, double &u , double &v);
uchar bilinearInterpolation(float src_x, float src_y, Mat& srcimg);
void init_bpline_para(int row_block_num, int col_block_num, std::vector<std::vector<Point2D>>& ctrlPoints, float min, float max);
};
#include "BaseFFD.h"
void BaseFFD::calculeLocalCoordinates(double blockWidth, double blockHeight, int x, int y,
int& XBlockIndex, int& YBlockIndex, double& u, double& v)
{
float x_block = x / blockWidth;
float y_block = y / blockHeight;
XBlockIndex = floor(x_block);
YBlockIndex = floor(y_block);
u = x_block - XBlockIndex;
v = y_block - YBlockIndex;
}
uchar BaseFFD::bilinearInterpolation(float src_x, float src_y, Mat& srcimg)
{
int x1 = cvFloor(src_x);
int y1 = cvFloor(src_y);
if (x1 < 1 || x1 >= srcimg.cols - 1 || y1 < 1 || y1 >= srcimg.rows - 1)//越界
{
return 0;
}
else
{
//dstimg.at<uchar>(y, x) = srcimg.at<uchar>(y1, x1); //最邻近插值
//双线性插值
int x2 = x1 + 1;
int y2 = y1 + 1;
uchar pointa = srcimg.at<uchar>(y1, x1);
uchar pointb = srcimg.at<uchar>(y1, x2);
uchar pointc = srcimg.at<uchar>(y2, x1);
uchar pointd = srcimg.at<uchar>(y2, x2);
uchar gray = (uchar)((x2 - src_x) * (y2 - src_y) * pointa - (x1 - src_x) * (y2 - src_y) * pointb - (x2 - src_x) * (y1 - src_y) * pointc + (x1 - src_x) * (y1 - src_y) * pointd);
return gray;
}
}
void BaseFFD::init_bpline_para(int row_block_num, int col_block_num, std::vector<std::vector<Point2D>>& ctrlPoints, float min, float max)
{
int grid_rows = row_block_num + 2;
int grid_cols = col_block_num + 2;
srand((unsigned int)time(NULL));
for (int i = 0; i < grid_rows; i++)
{
for (int j = 0; j < grid_cols; j++)
{
ctrlPoints[i][j] = Point2D(randf(min, max), randf(min, max));
}
}
}
基础类 点
#pragma once
#include <iostream>
class Point2D
{
private:
double x;
double y;
public:
Point2D();
Point2D(double xCoord, double yCoord);
Point2D(const Point2D& other);
// 析构函数
~Point2D() {}
// 获取x坐标
double getX() const;
// 获取y坐标
double getY() const;
// 设置x坐标
void setX(double xCoord);
// 设置y坐标
void setY(double yCoord);
// 计算两点之间的距离
double distanceTo(const Point2D& other) const;
// 重载赋值运算符
Point2D& operator=(const Point2D& other);
// 重载加法运算符
Point2D operator+(const Point2D& other) const;
// 重载减法运算符
Point2D operator-(const Point2D& other) const;
// 重载乘法运算符(点与标量的乘法)
Point2D operator*(double scalar) const;
// 重载除法运算符(点与标量的除法)
Point2D operator/(double scalar) const;
// 重载输出运算符
friend std::ostream& operator<<(std::ostream& os, const Point2D& Point2D) {
os << "(" << Point2D.x << ", " << Point2D.y << ")";
return os;
}
};
#include "Point2D.h"
Point2D::Point2D()
: x(0.0), y(0.0)
{
}
Point2D::Point2D(double xCoord, double yCoord)
: x(xCoord), y(yCoord)
{
}
Point2D::Point2D(const Point2D& other)
: x(other.x), y(other.y)
{}
// 获取x坐标
double Point2D::getX() const {
return x;
}
// 获取y坐标
double Point2D::getY() const {
return y;
}
// 设置x坐标
void Point2D::setX(double xCoord) {
x = xCoord;
}
// 设置y坐标
void Point2D::setY(double yCoord) {
y = yCoord;
}
// 计算两点之间的距离
double Point2D::distanceTo(const Point2D& other) const {
double dx = x - other.x;
double dy = y - other.y;
return std::sqrt(dx * dx + dy * dy);
}
// 重载赋值运算符
Point2D& Point2D::operator=(const Point2D& other) {
if (this != &other) {
x = other.x;
y = other.y;
}
return *this;
}
// 重载加法运算符
Point2D Point2D::operator+(const Point2D& other) const {
return Point2D(x + other.x, y + other.y);
}
// 重载减法运算符
Point2D Point2D::operator-(const Point2D& other) const {
return Point2D(x - other.x, y - other.y);
}
// 重载乘法运算符(点与标量的乘法)
Point2D Point2D::operator*(double scalar) const {
return Point2D(x * scalar, y * scalar);
}
// 重载除法运算符(点与标量的除法)
Point2D Point2D::operator/(double scalar) const {
if (scalar == 0) {
std::cerr << "Error: Division by zero" << std::endl;
return *this;
}
return Point2D(x / scalar, y / scalar);
}