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

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

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Unity在Project右键点击物体之后获取到点击物体的名称

Unity在Project右键点击物体之后获取到点击物体的名称 描述&#xff1a; 在Unity的Project右键点击物体之后选择对应的菜单选项点击之后打印出物体的名称 注意事项 如果获取到文件或者预制体需要传递objcet类型&#xff0c;然后使用 GameObject.Instantiate((GameObject)se…

vue-advanced-chat使用指南

demo地址:— vue-advanced-chat的git地址:https://github.com/advanced-chat/vue-advanced-chat 1.搭建demo demo地址克隆后在demo目录安装依赖并启动 启动之后的页面如下: 2.前端代码分析 2.1 重点api分析 current-user-id:当前用户id room-id:可随时加载特定房间?…

Rhino 8 for Mac(犀牛8)中文激活版

Rhino 8是一款功能强大的三维构建软件&#xff0c;它可以帮助用户创建各种类型的3D模型&#xff0c;包括产品设计、建筑设计、工业设计计划等。Rhino 7具有直观的界面和丰富的工具库&#xff0c;让用户可以快速轻松地进行建模、编辑、分析和漂染。 Rhino 8支持多种文件格式的导…

回归预测 | Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测

Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测 目录 Matlab实现POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机多变量回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.POA-CNN-SVM鹈鹕算法优化卷积神经网络-支持向量机的多变量回归…

多测师肖sir___app测试_001

app测试 一、app测试分为两大类 app手工测试&#xff08;讲&#xff09; app自动化测试&#xff08;讲&#xff09; &#xff08;1&#xff09;手工app测试&#xff1f; 就是通过手点击app上的应用&#xff0c;cs架构上 &#xff08;2&#xff09;app自动化测试&#xff1f; 通…

C# list<T>去重

文章目录 C# list<T>去重值类型去重List<object>object is intobject is decimalobject is charobject is boolobject is string List<int>List<string> 引用类型去重 C# list去重 值类型去重 List object is int //object is intList<object&g…

工业级的电表对精度有哪些要求?

工业级电表在设计和技术上有着严格的精度要求&#xff0c;以此来保证生产过程的能耗监控和成本控制。接下来&#xff0c;就由小编来为大家介绍下工业级的电表对精度的要求&#xff0c;一起来看下吧&#xff01; 一、工业级电表精度等级的划分 工业级电表的精度等级主要分为以下…

HTML5+CSS3+JS小实例:简约的黑色分页

实例:简约的黑色分页 技术栈:HTML+CSS+JS 效果: 源码: 【HTML】 <!DOCTYPE html> <html><head><meta http-equiv="content-type" content="text/html; charset=utf-8"><meta name="viewport" content="…

纷享销客荣获最佳制造业数字营销服务商奖

2023年10月26日&#xff0c;第二届中国制造业数智化发展大会在上海盛大召开。本次大会汇聚了制造行业的顶尖企业和专家&#xff0c;共同探讨如何通过数字化转型赋能企业自身成长&#xff0c;实现信息化向数字化的升级转型。 在本次盛会上&#xff0c;纷享销客以其卓越的基本面、…

UE5——网络——RPC

RPC&#xff08;这个是官方文档的资料&#xff09; 要将一个函数声明为 RPC&#xff0c;您只需将 Server、Client 或 NetMulticast 关键字添加到 UFUNCTION 声明。 例如&#xff0c;若要将某个函数声明为一个要在服务器上调用、但需要在客户端上执行的 RPC&#xff0c;您可以…

大语言模型?生成式AI?分不清楚的话可以看aws这个例子

大语言模型和生成式AI有什么紧密联系呢&#xff1f;为什么大语言模型近期受到如此大的关注呢&#xff1f;当提到大语言模型&#xff0c;大家可能首先会想到像ChatGPT这样的自然语言处理工具。那么大语言模型究竟是什么&#xff1f;它和生成式AI又存在怎样的关系呢&#xff1f;接…

R语言绘图-5-条形图(修改坐标轴以及图例等)

0. 说明&#xff1a; 1. 绘制条形图&#xff1b; 2. 添加文本并调整位置&#xff1b; 3. 调整x轴刻度的字体、角度及颜色&#xff1b; 4. 在导出pdf时&#xff0c;如果没有字体&#xff0c;该怎么解决问题&#xff1b; 1. 结果&#xff1a; 2. 代码&#xff1a; library(ggp…

组件局部注册和全局注册

普通组件的注册使用-局部注册 1.特点&#xff1a; 只能在注册的组件内使用 2.实现效果 3.步骤&#xff1a; 创建.vue文件&#xff08;三个组成部分&#xff09;在使用的组件内先导入再注册&#xff0c;最后使用 4.使用方式&#xff1a; 当成html标签使用即可 <组件名&…

Redis系统学习(高级篇)-Redis主从集群

目录 一、搭建主从集群 二、主从数据同步的原理 三、repl_backlog原理 四、主从同步优化 五、小结 一、搭建主从集群 为了提高高并发&#xff0c;主从集群是很有必要的&#xff0c;因为这样可以实现读写分离&#xff0c;主节点负责写&#xff0c;从节点就只负责读&#xf…

diffusers-Load pipelines,models,and schedulers

https://huggingface.co/docs/diffusers/using-diffusers/loadinghttps://huggingface.co/docs/diffusers/using-diffusers/loading 有一种简便的方法用于推理是至关重要的。扩散系统通常由多个组件组成&#xff0c;如parameterized model、tokenizers和schedulers&#xff0c…

BEM:css命名规范

BEM BEM(Block-Element-Modifier)&#xff0c;块、元素、修饰符&#xff0c;是一种CSS命名规范&#xff0c;旨在前端开发中创建可重用组件和代码共享的方法&#xff0c;使样式易于扩展&#xff0c;易于维护&#xff0c;易于理解 规范&#xff1a; 1、块&#xff08;Block&am…

华为防火墙 配置 SSLVPN

需求&#xff1a; 公司域环境&#xff0c;大陆客户端居家办公室需要连到公司域&#xff0c;这里可以在上海防火墙上面开通SSLVPN&#xff0c;员工就可以透过SSLVPN连通上海公司的内网&#xff0c;但是由于公司域控有2个站点&#xff0c;一个在上海&#xff0c;一个在台北&…

关于docker网络实践中遇到的问题

1.禁用docker自动修改iptables规则 查看docker.service文件/usr/lib/systemd/system/docker.service 默认在宿主机部署容器&#xff0c;映射了端口的话&#xff0c;docker能自己修改iptables规则&#xff0c;把这些端口暴露到公网。 如果要求这些端口不能暴露到公网&#xf…

11.1~11.2双端口RAM(报错复盘,一些理解(循环,阻塞非阻塞),三目运算符解决使能端)

双端口RAM 分别用于读写数据&#xff0c;同时进行 当读使能端有效时可以读出来数据 当写使能端有效时可以覆写数据 读写并行操作 报错 1.reg必须在always里 这个不能assign,因为reg型不能assign&#xff0c;单端口的那个可以assign是因为其定义为了wire型&#xff0c;就不…

Cgroups定义及验证

sudo lsb_release -a可以看到操作系统版本是20.04&#xff0c;sudo uname -r可以看到内核版本是5.4.0-156-generic。 Linux Cgroups 的全称是 Linux Control Group。它最主要的作用&#xff0c;就是限制一个进程组能够使用的资源上限&#xff0c;包括 CPU、内存、磁盘、网络带…