openCV2马拉松第18圈——坐标变换
- 仿射变换
- 坐标映射
- 利用坐标映射做一些效果,例如以下
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWJjZDE5OTI3MTln/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" width="300" height="300" alt="">上面是原图,以下是利用坐标映射后的结果
url=OvyNHG3WKjwxbiJDOWund5lfoAPXkxXdzSyFcQqI3NKsJZkqOZjlPegJ4DG75vOosupgaM3iklTPnq3TSzHnZq
fr=aladdin
我们仅仅要知道3个相应点,就能知道这个矩阵。OpenCV提供了这样一个计算的函数
getAffineTransform
Image1中的点1。2,3相应到了Image2中的点1,2。3。这样我们就能得到仿射矩阵。于是Image1中的全部点都能通过这个仿射矩阵映射到Image2中

仿射变换后

坐标映射
在做图像增强时。我们改变的时图像的值域,g(x)output f(x)是input h是我们的方法,比方对照度增强直方图均衡化 用g(x)
= h(f(x))
可是在坐标变换,改变的是定义域, g(x)
= f(h(x))
watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvYWJjZDE5OTI3MTln/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">
如果我们有源图像f和坐标映射函数h,我们要怎么计算输出图像g呢?
大部分人都会这样
procedure forwardWarp(f,h,outg):
For every pixelxinf(x)
1. Compute the destination locationx′=h(x).
2. Copy the pixelf(x)tog(x′).
我一開始也是这样想的。那就是遍历源图像,对一个Point p,应用变换函数h计算其在输出图像的坐标P',然后复制
procedure inverseWarp(f,h,outg):
For every pixelx′ing(x′)
1. Compute the source locationx=hˆ(x′)
2. Resamplef(x)at
locationx
and copy tog(x′)
遍历输出图像的点。映射到源图像,再去取点。
映射函数h就是原来h的逆矩阵。
当然了,也会碰到非整数的情况,接下来还要作一些别的处理。就不细述。
另一种简单的坐标映射,就是人工指定了输出图像的哪个点相应到源图像的哪个点。在OpenCV里叫remapping
一眼就能看出。我们的映射函数是

-
C++: void warpAffine(InputArray src,
OutputArray dst, InputArray M, Size dsize, int flags=INTER_LINEAR, int borderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
-
- src – 输入图像
- dst – 输出图像。有dsize的大小(由于仿射会把图片变大变小),type和src一样
- M –
仿射矩阵. - dsize – 输出图像的大小.
- flags –WARP_INVERSE_MAP意味着M是逆变换 (
). - borderMode .
- borderValue 默认是0.
获得我们的变换矩阵M
/// 设置源图像和相应图像的3组相应点
srcTri[0] = Point2f( 0,0 );
srcTri[1] = Point2f( src.cols - 1, 0 );
srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 );/// 获得变换矩阵
warp_mat = getAffineTransform( srcTri, dstTri );附上官方的sample
#include "opencv2/imgproc.hpp"
#include <iostream>
#include "opencv2/highgui.hpp"
using namespace cv;
using namespace std; /// Global variables
char* source_window = "Source image";
char* warp_window = "Warp";
char* warp_rotate_window = "Warp + Rotate"; /** @function main */
int main( int argc, char** argv )
{
Point2f srcTri[3];
Point2f dstTri[3]; Mat rot_mat( 2, 3, CV_32FC1 );
Mat warp_mat( 2, 3, CV_32FC1 );
Mat src, warp_dst, warp_rotate_dst; /// Load the image
src = imread( argv[1], 1 ); /// Set the dst image the same type and size as src
warp_dst = Mat::zeros( src.rows, src.cols, src.type() ); /// Set your 3 points to calculate the Affine Transform
srcTri[0] = Point2f( 0,0 );
srcTri[1] = Point2f( src.cols - 1, 0 );
srcTri[2] = Point2f( 0, src.rows - 1 ); dstTri[0] = Point2f( src.cols*0.0, src.rows*0.33 );
dstTri[1] = Point2f( src.cols*0.85, src.rows*0.25 );
dstTri[2] = Point2f( src.cols*0.15, src.rows*0.7 ); /// Get the Affine Transform
warp_mat = getAffineTransform( srcTri, dstTri ); /// Apply the Affine Transform just found to the src image
warpAffine( src, warp_dst, warp_mat, warp_dst.size() ); /** Rotating the image after Warp */ /// Compute a rotation matrix with respect to the center of the image
Point center = Point( warp_dst.cols/2, warp_dst.rows/2 );
double angle = -50.0;
double scale = 0.6; /// 这里获得旋转矩阵,中心是center,角度为-50度,并缩放为原来的0.6倍,也是简单的样例
rot_mat = getRotationMatrix2D( center, angle, scale ); /// Rotate the warped image
warpAffine( warp_dst, warp_rotate_dst, rot_mat, warp_dst.size() ); /// Show what you got
namedWindow( source_window, CV_WINDOW_AUTOSIZE );
imshow( source_window, src ); namedWindow( warp_window, CV_WINDOW_AUTOSIZE );
imshow( warp_window, warp_dst ); namedWindow( warp_rotate_window, CV_WINDOW_AUTOSIZE );
imshow( warp_rotate_window, warp_rotate_dst ); /// Wait until user exits the program
waitKey(0); return 0;
}怎样实现
这种变换呢for( int j = 0; j < src.rows; j++ ) {
for( int i = 0; i < src.cols; i++ ) {
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = j ; }}然后再调用
remap( src, dst, map_x, map_y, INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) );
remap很好懂,INTER_LINEAR是线性插值
以下是官方的sample,比較简单就不解释啦
-
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include <iostream>
#include <stdio.h> using namespace cv; /// Global variables
Mat src, dst;
Mat map_x, map_y;
char* remap_window = "Remap demo";
int ind = 0; /// Function Headers
void update_map( void ); /**
* @function main
*/
int main( int argc, char** argv )
{
/// Load the image
src = imread( argv[1], 1 ); /// Create dst, map_x and map_y with the same size as src:
dst.create( src.size(), src.type() );
map_x.create( src.size(), CV_32FC1 );
map_y.create( src.size(), CV_32FC1 ); /// Create window
namedWindow( remap_window, CV_WINDOW_AUTOSIZE ); /// Loop
while( true )
{
/// Each 1 sec. Press ESC to exit the program
int c = waitKey( 1000 ); if( (char)c == 27 )
{ break; } /// Update map_x & map_y. Then apply remap
update_map();
remap( src, dst, map_x, map_y, CV_INTER_LINEAR, BORDER_CONSTANT, Scalar(0,0, 0) ); /// Display results
imshow( remap_window, dst );
}
return 0;
} /**
* @function update_map
* @brief Fill the map_x and map_y matrices with 4 types of mappings
*/
void update_map( void )
{
ind = ind%4; for( int j = 0; j < src.rows; j++ )
{ for( int i = 0; i < src.cols; i++ )
{
switch( ind )
{
case 0:
if( i > src.cols*0.25 && i < src.cols*0.75 && j > src.rows*0.25 && j < src.rows*0.75 )
{
map_x.at<float>(j,i) = 2*( i - src.cols*0.25 ) + 0.5 ;
map_y.at<float>(j,i) = 2*( j - src.rows*0.25 ) + 0.5 ;
}
else
{ map_x.at<float>(j,i) = 0 ;
map_y.at<float>(j,i) = 0 ;
}
break;
case 1:
map_x.at<float>(j,i) = i ;
map_y.at<float>(j,i) = src.rows - j ;
break;
case 2:
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = j ;
break;
case 3:
map_x.at<float>(j,i) = src.cols - i ;
map_y.at<float>(j,i) = src.rows - j ;
break;
} // end of switch
}
}
ind++;
}
荷枪实弹remap还有很多其它的功能,比方能帮我们实现图像的缩放,k是缩放系数#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream> #define K 1.1
using namespace cv; // Global variables
Mat src, dst;
Mat map_x, map_y;
char* remap_window = "Remap demo";
void update_map( void ); int main( int argc, char** argv ) { src = imread( argv[1], 1 ); dst.create( src.rows*K, src.cols*K, src.type() );
map_x.create( src.rows*K, src.cols*K, CV_32FC1 );
map_y.create( src.rows*K, src.cols*K, CV_32FC1 ); namedWindow( remap_window, CV_WINDOW_AUTOSIZE ); update_map();
remap( src, dst, map_x, map_y, INTER_LANCZOS4, BORDER_CONSTANT, Scalar(0, 0, 0) ); imshow( remap_window, dst ); waitKey(0);
return 0;
} void update_map( void )
{
for( int j = 0; j < (int)(K*src.rows); j++ ) {
for( int i = 0; i < (int)(K*src.cols); i++ ) {
map_x.at<float>(j,i) = i/K;
map_y.at<float>(j,i) = j/K;
}
}
}举一反三我们一開始就见到了这图片,那么怎么实现呢?
我在y轴事实上没有缩放。仅仅在x轴进行了缩放。分别分成了三段。
我的程序首先要用户在图片上点两次,各自是要扩大的举行的左上角和右下角。然后,就以左上角的x和右下角的x为界。分为3段进行映射。
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream> using namespace cv;
using namespace std; #define K 2 //表示取2个点
#define threshold 0.1 //缩放的程度 // Global variables
Mat src, src_copy, dst;
Mat map_x, map_y;
char* window = "Scale demo";
int samplePointNum = 0; //已经点了几次
Point myPoints[K]; //存放用户点击的坐标
bool flag = false; //点击2次,就设置flag,開始更新 void update_map( void )
{
int leftX = threshold*src.cols;
int rightX = src.cols - leftX;
int recLeftX = myPoints[0].x;
int recRightX = myPoints[1].x; for( int j = 0; j < src.rows; j++ ) {
for( int i = 0; i < src.cols; i++ ) {
if(i > leftX && i < rightX) {
map_x.at<float>(j,i) = recLeftX + (i - leftX) * (recRightX - recLeftX)/(rightX - leftX);
map_y.at<float>(j,i) = j;
} else if(i <= leftX) {
map_x.at<float>(j,i) = i * recLeftX/leftX;
map_y.at<float>(j,i) = j;
} else {
map_x.at<float>(j,i) = recRightX + (i- rightX) * (src.cols - recRightX)/(src.cols - rightX);
map_y.at<float>(j,i) = j;
}
}
}
flag = true;
remap( src_copy, dst, map_x, map_y, INTER_LANCZOS4, BORDER_CONSTANT, Scalar(0, 0, 0) );
imshow( window, dst );
imwrite( "./result.jpg", dst );
return;
} static void onMouse( int event, int x, int y, int, void* )
{
if(samplePointNum == K){
if(!flag)
update_map();
return;
}
if( event != EVENT_LBUTTONDOWN)
return;
rectangle(src, Point(x-3,y-3), Point(x+3,y+3), Scalar(255,0,0), 1);
myPoints[samplePointNum++] = Point(x,y);
imshow( window, src );
return;
} int main( int argc, char** argv ) {
src = imread( argv[1], 1 );
src_copy = src.clone(); dst.create( src.rows, src.cols, src.type() );
map_x.create( src.rows, src.cols, CV_32FC1 );
map_y.create( src.rows, src.cols, CV_32FC1 ); namedWindow( window, CV_WINDOW_AUTOSIZE ); setMouseCallback( window, onMouse, 0 );
imshow( window, src );
waitKey(0);
return 0;
}
openCV2马拉松第18圈——坐标变换的更多相关文章
- OpenCV2马拉松第15圈——边缘检測(Laplace算子,LOG算子)
收入囊中 拉普拉斯算子 LOG算子(高斯拉普拉斯算子) OpenCV Laplacian函数 构建自己的拉普拉斯算子 利用拉普拉斯算子进行图像的锐化 葵花宝典 在OpenCV2马拉松第14圈--边缘检 ...
- OpenCV2马拉松第17圈——边缘检測(Canny边缘检測)
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g 收入囊中 利用OpenCV Canny函数进行边缘检測 掌握Canny算法基本理论 ...
- OpenCV2马拉松第22圈——Hough变换直线检測原理与实现
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/27220445 收入囊中 Hough变换 概率Ho ...
- OpenCV2马拉松第14圈——边缘检測(Sobel,prewitt,roberts)
收入囊中 差分在边缘检測的角色 Sobel算子 OpenCV sobel函数 OpenCV Scharr函数 prewitt算子 Roberts算子 葵花宝典 差分在边缘检測究竟有什么用呢?先看以下的 ...
- OpenCV2马拉松第2圈——读写图片
收入囊中 用imread读取图片 用nameWindow和imshow展示图片 cvtColor彩色图像灰度化 imwrite写图像 Luv色彩空间转换 初识API 图像读取接口 image = im ...
- OpenCV2马拉松第10圈——直方图反向投影(back project)
收入囊中 灰度图像的反向投影 彩色图像的反向投影 利用反向投影做object detect 葵花宝典 什么是反向投影?事实上没有那么高大上! 在上一篇博文学到,图像能够获得自己的灰度直方图. 反向投影 ...
- OpenCV2马拉松第12圈——直方图比較
收入囊中 使用4种不同的方法进行直方图比較 葵花宝典 要比較两个直方图, 首先必需要选择一个衡量直方图相似度的对照标准.也就是先说明要在哪个方面做对照. 我们能够想出非常多办法,OpenCV採用了下面 ...
- openCV2马拉松第19圈——Harris角点检測(自己实现)
计算机视觉讨论群162501053 转载请注明:http://blog.csdn.net/abcd1992719g/article/details/26824529 收入囊中 使用OpenCV的con ...
- OpenCV2马拉松第5圈——线性滤波
收入囊中 这里的非常多内容事实上在我的Computer Vision: Algorithms and ApplicationsのImage processing中都有讲过 相关和卷积工作原理 边界处理 ...
随机推荐
- Nginx+PHPSTORM+Xdebug 配置
1. php Xdebug扩展 下载 https://xdebug.org/download.php 选择自己的版本下载,会得到一个dll文件 例如 php_xdebug-2.7.0alpha1-7 ...
- python中的 __xxx__ 方法
1 __class__ instance.__class__ The class to which a class instance belongs def foo(): pass class A(o ...
- HTTP DNS
试用地址 https://www.dnspod.cn/httpdns/demo 哪些人适合使用HTTP DNS 1.希望降低访问延迟.减少跨网访问的资讯.游戏类APP: 2.希望降低连接失败率,提升业 ...
- mongodb window安装学习
https://blog.csdn.net/u011692780/article/details/81223525 教程:http://www.runoob.com/mongodb/mongodb-t ...
- HRBUST 2078:糖果(模拟,贪心)
题不难,但作为一道恶心到了我的题,我还是记录一下的好. 题意:n个人围一圈,要求:相邻两人,分数高的要比分数低的得到更多的糖果,若分数相同则必须得到相同数量的糖果.问满足要求的最少需要分配的糖果数.( ...
- 大视野 1016: [JSOI2008]最小生成树计数(最小生成树)
总结:此类题需要耐心观察规律,大胆猜想,然后证明猜想,得到有用的性质,然后解答. 简单的说:找隐含性质. 传送门:http://61.187.179.132/JudgeOnline/problem.p ...
- 转:c++ Oracle OCCI 编程
原地址http://blog.sina.com.cn/s/blog_53a72add01015zj4.html 找不到具体的出处,只好不写了. OCCI数据库ORACLE编程步骤1. 配置环境(1) ...
- 获取某个元素相对于视窗的位置-getBoundingClientRect
1. getBoundingClientRect用于获取某个元素相对于视窗的位置集合.集合中有top, right, bottom, left等属性. 语法:这个方法没有参数 rectObject = ...
- 洛谷——P1187 3D模型
P1187 3D模型 题目描述 一座城市建立在规则的n×m网格上,并且网格均由1×1正方形构成.在每个网格上都可以有一个建筑,建筑由若干个1×1×1的立方体搭建而成(也就是所有建筑的底部都在同一平面上 ...
- Java中泛型T和Class<T>以及Class<?>的理解(转)
注意:class是java的关键字, 在声明Java类时使用; Class类的实例表示Java应用运行时的类(class ans enum)或接口(interface and annotation)( ...