opencv中自定义的双线性二次插值的图像旋转及缩放

news/2024/10/13 14:23:41
#include <iostream>
#include<opencv2/opencv.hpp>
using namespace cv;
using namespace std;void coordinateTransform(Point2d*p4Corner,Point2d*np4Corner,double rotAngle,double gamma,Point2d center)
{double cx=center.x,cy=center.y;double rad=rotAngle*CV_PI/180;double alpha=std::cos(rad)*gamma;double beta=std::sin(rad)*gamma;for(int i=0;i<4;i++){np4Corner[i].x= alpha*p4Corner[i].x+beta*p4Corner[i].y+(1-alpha)*cx-beta*cy;np4Corner[i].y=-beta*p4Corner[i].x+alpha*p4Corner[i].y+beta*cx+(1-alpha)*cy;}
}
void find4Borders(Point2d* np4Corner,double borders[4])
{borders[0]=np4Corner[0].y;borders[1]=np4Corner[0].y;borders[2]=np4Corner[0].x;borders[3]=np4Corner[0].x;for(int i=1;i<4;i++){borders[0]=np4Corner[i].y<borders[0]?np4Corner[i].y:borders[0];borders[1]=np4Corner[i].y>borders[1]?np4Corner[i].y:borders[1];borders[2]=np4Corner[i].x<borders[2]?np4Corner[i].x:borders[2];borders[3]=np4Corner[i].x>borders[3]?np4Corner[i].x:borders[3];}for(int i=0;i<4;i++){if(borders[i]<0)borders[i]=floor(borders[i]);elseborders[i]=ceil(borders[i]);}
}
//    Mat mat=(Mat_<double>(2,3)<<alpha,  beta,  (1-alpha)*x+beta*y,
//                                -beta,  alpha,   beta*x+(1-alpha)*y);
void rotImg(const Mat&src,Mat&dst,double rotAngle,double gamma,Point2d center)
{double rad=rotAngle*CV_PI/180;double cosRad=std::cos(rad) ;double sinRad=std::sin(rad);int oldRows=src.rows, oldCols=src.cols;//逆向旋转的矩阵描述double x=center.x,y=center.y;double alpha=cosRad*gamma,beta=sinRad*gamma;//正向缩放//计算新图像的大小//1、获取原图像的四个角点坐标p4Corner,以及变换后的四个角点坐标np4CornerPoint2d p4Corner[]={ Point2d(0,0),              Point2d(src.cols,0),Point2d(0,src.rows) ,  Point2d(src.cols,src.rows)};Point2d np4Corner[4];coordinateTransform(p4Corner,np4Corner,rotAngle,gamma,center);//对原图像四个角点做坐标正向变换//根据np4Corner,找到变换后新图像的上、下、左、右边界坐标double borders[4];find4Borders(np4Corner,borders);//计算新图的行数、列数int newRows=borders[1]-borders[0];int newCols=borders[3]-borders[2];//为目标图像分配内存dst=Mat::zeros(newRows,newCols,src.type());//计算二维插值int step0=src.step[0],step1=src.step[1];int channels=src.channels();alpha =cosRad/gamma;//逆向缩放beta=sinRad/gamma;//逆向缩放for(int i=0;i<newRows;i++)for(int j=0;j<newCols;j++){//注意:在新图中,原点坐标偏移了(borders[2],borders[0])。所以要把每个像素坐标偏移回来(j+borders[2])、(i+borders[0])double x_inv=alpha*(j+borders[2])-beta*(i+borders[0])+(1-alpha)*x+beta*y;//逆映射回原图坐标double y_inv=beta*(j+borders[2])+alpha*(i+borders[0])-beta*x+(1-alpha)*y;//逆映射回原图坐标int j_inv=floor(x_inv);int i_inv=floor(y_inv);if(j_inv>=0&&j_inv<oldCols&&i_inv>=0&&i_inv<oldRows){double a=x_inv-j_inv, b=y_inv-i_inv;//找到四个坐标点uchar* p1=src.data+step0*i_inv+step1*j_inv;uchar* p2=src.data+step0*i_inv+step1*(j_inv+1);uchar* p3=src.data+step0*(i_inv+1)+step1*j_inv;uchar* p4=src.data+step0*(i_inv+1)+step1*(j_inv+1);//获取目标图像中的第i、j坐标uchar* pdst=dst.data+dst.step[0]*i+dst.step[1]*j;for(int k=0;k<channels;k++){pdst[k]=static_cast<uchar>((1-a)*(1-b)*p1[k]+a*(1-b)*p2[k]+b*(1-a)*p3[k]+a*b*p4[k]) ;}}}
}int main()
{Mat src=imread("D:/Qt/MyImage/baboon.jpg",1);Mat dst;rotImg(src,dst,315,0.5,Point(100,100));imshow("rotated image",dst);imshow("original image",src);waitKey();return 0;
}



 





 

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

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

相关文章

从0到10Wqps,大厂的智能客服平台,如何实现架构演进?

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,…

蚂蚁面试:Springcloud核心组件的底层原理,你知道多少?

文章很长,且持续更新,建议收藏起来,慢慢读!疯狂创客圈总目录 博客园版 为您奉上珍贵的学习资源 : 免费赠送 :《尼恩Java面试宝典》 持续更新+ 史上最全 + 面试必备 2000页+ 面试必备 + 大厂必备 +涨薪必备 免费赠送 :《尼恩技术圣经+高并发系列PDF》 ,帮你 实现技术自由,…

x32dbg 手动脱NsPack 壳

记一下步骤 文件名字(太长遂改):1111.exe 文件来源:攻防世界Reverse三星题,crackme 工具选择:下载的文件出现病毒报错,一开始是用OD脱壳,但是修复表的时候,无法运行程序,所以改用x64 脱壳方法:ESP在PE中 在die中发现存在NsPack壳 丢到x32dbg中 找到程序代码入口 F8…

初中中考英语词汇大全003掌握常用词汇,轻松应对考试

PDF格式公众号回复关键字:ZKCH0031 ancient 古代的;古老的 modern 现代的;时髦的 official 官方的;正式的;公务员 foreign 外国的;外来的 2 sooner or later 迟早;早晚有一天 all the time 一直;始终 over and over 一遍又一遍;反复地 in a hurry 匆忙地;立即;很快…

mit6.828 - lab1笔记

安装环境编译qemu1. PC启动 打开两个窗口,在第一个窗口中 make qemu-gdb,会启动内核,但在执行第一个指令之前停下; 在第二个窗口中make gdb,实时观察第一个窗口中的执行过程。从这里可以观察到:IBM PC 在物理地址 0x000ffff0 开始执行, 位于为 ROM BIOS 保留的 64KB 区域…

混入、插件、插槽、vuex、本地存储

【混入】# mixin(混入)功能:可以把多个组件共用的配置提取成一个混入对象,不需要在每个组件中都写了 使用步骤 。 。 。 【插件】1 # 1 写plugins/index.js2 import Vue from "vue";3 import axios from "axios";4 import hunru from "@/mixin&quo…

Linux进程

程序与进程 程序:是可执行文件,其本质是是一个文件,程序是静态的,同一个程序可以运行多次,产生多个进程 进程:它是程序的一次运行过程,当应用程序被加载到内存中运行之后它就称为了一个进程,进程是动态的,进程的生命周期是从程序运行到程序退出 父子进程:当一个进程A…

RISC-V SoC研发flow的总结

RISC-V SoC研发flow的总结 今年的流片接近尾声了,我个人的评价是相比去年,在进度管理和流程管理上做的更好了一些。对比今年一月份开会时开会的PPT,基本上当时的规划和目标基本上都达成了。这次聊聊整个研发过程中的一些感悟。 首先是对于整个团队的研发方向做了一个比较大的…