基于B样条的FFD自由变换原理与C++实现

news2025/1/11 5:15:19

原理:
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);
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1167514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

行业追踪,2023-11-03

自动复盘 2023-11-03 凡所有相&#xff0c;皆是虚妄。若见诸相非相&#xff0c;即见如来。 k 线图是最好的老师&#xff0c;每天持续发布板块的rps排名&#xff0c;追踪板块&#xff0c;板块来开仓&#xff0c;板块去清仓&#xff0c;丢弃自以为是的想法&#xff0c;板块去留让…

QT5通过openss1.1.1实现RSA加密

QT5通过openss1.1.1实现RSA加密 1.查看当前版本的QT依赖哪个版本的openssl #include <QSslSocket> #include <QDebug>qDebug() << QSslSocket::sslLibraryBuildVersionString();控制台输出&#xff1a; "OpenSSL 1.1.1w 11 Sep 2023"可以看到大…

Sulfo-CY3 azide水溶型橘红色荧光染料-星戈瑞

Sulfo-CY3 azide通常被认为是一种红色至近红外光谱区的荧光染料&#xff0c;其发射波长约为560-580纳米左右&#xff0c;而不是橘黄色。它属于Cyanine家族的染料&#xff0c;与其他Cyanine染料一样&#xff0c;其颜色主要是由其分子结构和共轭体系所决定的。 Sulfo-CY3 azide是…

【软件测试】银行项目具体操作流程

项目背景&#xff1a; XXXX银行项目采用的是B/S架构&#xff0c;主要是为了解决银行业务中的柜员、凭证、现金、账务等来自存款、贷款、会计模块的管理。 手工弊端&#xff1a; 1.项目业务复杂度高&#xff0c;回归测试工作量大 2.单个接口功能比较简单&#xff0c;容易实现自…

css:元素居中整理水平居中、垂直居中、水平垂直居中

目录 1、水平居中1.1、行内元素1.2、块级元素 2、垂直居中2.1、单行文字2.2、多行文字2.3、图片垂直居中 3、水平垂直居中参考文章 1、水平居中 1.1、行内元素 行内元素&#xff08;比如文字&#xff0c;span&#xff0c;图片等&#xff09;的水平居中&#xff0c;其父元素中…

GDPU 数据结构 天码行空8

实验八 二叉树的建立及遍历应用 一、【实验目的】 1、掌握二叉树的建立方法 2、掌握二叉树遍历的基本方法&#xff08;前序、中序、后序&#xff09; 3、掌握递归二叉树遍历算法的应用 二、【实验内容】 1.构造一棵二叉树,树的形态如下图(亦见附件)所示&#xff0c;打印出先…

PHP foreach 循环跳过本次循环

$a [[id>1],[id>2],[id>3],[id>4],[id>5],[id>6],[id>7],[id>18],];foreach($a as $v){if($v[id] 5){continue;}$b[] $v[id];}return show_data(,$b); 结果&#xff1a;

Java智慧工地源码(项目端+监管端+数据大屏+APP)

智慧工地系统功能介绍 【智慧工地PC项目端功能总览】 一.项目人员管理 包括&#xff1a;信息管理、信息采集、证件管理、考勤管理、考勤明细、工资管理、现场统计、WIFI教育、工种管理、分包商管理、班组管理、项目管理。 1.信息管理&#xff1a;头像、姓名、性别、身份证、…

“AI换脸诈骗”来势汹汹,三个层面科学应对……

当前&#xff0c;AI技术的广泛应用为社会公众提供了个性化智能化的信息服务&#xff0c;也给网络诈骗带来可乘之机&#xff0c;如不法分子通过面部替换语音合成等方式制作虚假图像、音频、视频仿冒他人身份实施诈骗、侵害消费者合法权益。你认为AI诈骗到底应该如何防范&#xf…

热点报告 | 健身人群抵抗入冬肥,Dirtyfit引领23秋冬潮流?

您是否曾有以下困惑&#xff1f;打开小红书首页推荐&#xff0c;似乎已经被算法教育成了成熟的信息茧房&#xff0c;想要找到下一个热点&#xff0c;又忧虑一叶以障目&#xff1b;看着搜索框热词&#xff0c;又担心无法掌握热词背后的话题命脉&#xff0c;难以在浮光掠影中寻找…

零基础成人英语哪里可以学,柯桥成人英语培训

写作中经常会用到“有利于”的表达&#xff0c;一说到“有利于”&#xff0c;大家最先想到的是 be good for 或者 benefit&#xff0c;很滥&#xff0c;很简单&#xff0c;没有亮点&#xff0c;写作中很难提分。 还有一点&#xff0c;英文写作中很忌讳相同的表达反复出现&…

成集云 | 项目管理系统集成金蝶云星空ERP | 解决方案

方案介绍 项目管理系统是项目的管理者应用专门管理项目的系统软件&#xff0c;在有限的资源约束下&#xff0c;运用系统的观点、方法和理论&#xff0c;对项目涉及的全部工作进行有效地管理。它从项目的投资决策开始到项目结束的全过程进行计划、组织、指挥、协调、控制和评价…

从“别人家的孩子”到创业失败:人生的起伏与自我救赎

在我们的生活中&#xff0c;常常会遇到这样一种人&#xff0c;他们从小优秀&#xff0c;备受瞩目&#xff0c;是那种“别人家的孩子”。他们一路走来&#xff0c;无论是在学业还是工作中&#xff0c;都表现得极为出色&#xff0c;让父母引以为傲。然而&#xff0c;人生并非总是…

CVE-2023-45852:Viessmann Vitogate远程代码执行漏洞复现【附POC】

文章目录 Viessmann Vitogate远程代码执行漏洞(CVE-2023-45852)复现0x01 前言0x02 漏洞描述0x03 影响版本0x04 漏洞环境0x05 漏洞复现1.访问漏洞环境2.构造POC3.复现 0x06 修复建议 Viessmann Vitogate远程代码执行漏洞(CVE-2023-45852)复现 0x01 前言 免责声明&#xff1a;请…

炫云客户端信用额度如何修改?

现在炫云新注册用户信用额度是100元&#xff0c;但是有人觉得信用额度太高了&#xff0c;想修改信用额度&#xff0c;不知道炫云的信用额度如何修改&#xff0c;今天就教大家如何修改炫云的信用额度。炫云的信用额度在炫云官网和客户端都可以修改。 我们先来看炫云官网如何修改…

跨境电商的地方风味:文化多样性的市场

在数字时代&#xff0c;世界已经变得更加紧密相连&#xff0c;跨越国界的电子商务已成为全球经济的一部分。随着跨境电商的兴起&#xff0c;不仅商品跨国流通&#xff0c;文化也以一种前所未有的方式融合。本文将探讨跨境电商如何推动文化多样性&#xff0c;以及这一市场的前景…

Redis 的另一个集群版之 Codis 集群

文章目录 一、概述二、Codis 组织架构图三、Codis 测试规划四、Codis 安装及配置4.1 安装 Codis4.2 启动 Codis Dashboard4.3 启动 Codis Proxy4.4 启动 Codis Server4.5 启动 Codis FE&#xff08;可选&#xff09; 五、Codis-FE 配置六、测试 Codis 服务 如果您对Redis的了解…

【借力打力】记一次由于堆栈信息不详细的错误排查方法,利用访问日志进行定位问题

【借力打力】记一次由于堆栈信息不详细的错误排查方法&#xff0c;利用访问日志进行定位问题 1&#xff0c;背景2&#xff0c;排查步骤2.1 调用方问题2.2 Nginx手段2.3 运维工具辅助2.4 嵌入tomcat日志记录 3&#xff0c;结果 1&#xff0c;背景 异常信息每隔50分钟显示一次&a…

FFmpeg——使用Canvas录制视频尚存问题的解决方案

个人简介 &#x1f440;个人主页&#xff1a; 前端杂货铺 &#x1f64b;‍♂️学习方向&#xff1a; 主攻前端方向&#xff0c;正逐渐往全干发展 &#x1f4c3;个人状态&#xff1a; 研发工程师&#xff0c;现效力于中国工业软件事业 &#x1f680;人生格言&#xff1a; 积跬步…

DELPHI安卓开发如何初始化frame 生成create方法

记住名字需要一样create 点击create这时按住ctrlshiftc 弹出以后就就完成frame的初始化create方法