Opencv图像识别从零到精通(7)----图像平移、旋转、镜像

根据vc6.0c++的学习经验,如果可以很好的自己编程,让图像进行平移旋转这些操作,那么就好像能够清楚的看见图像的内部结构当然这里你怎么访问像素,这个可以自己选一种适合的,最多的是ptr指针,at也是挺多的。看着很简单的变换,可以对图像处理上手的更快,当然对于旋转可能就稍微i难了一点,不过opencv提供了resize(0,remap()等这样的函数,可以方便的让我们进行学习-特别是旋转的时候,有很多的变换,你可以任意旋转一个角度,也可能一直旋转,当然还可以保持图像大小不变的旋转和大小变换的旋转。好了,这些后面都会慢慢的接触到。这里不过多的说理论的知识,不过相应的会加上对应的重要的公式或者图,如果对平移,旋转,镜像的概念或者公式不了解的话,可以百度一下,下面的主要是实践为主,代码和结果的应用。

首先,要简单的介绍一下两个函数,就是remap()和resize()

C++: void remap(InputArray src, OutputArraydst, InputArray map1, InputArray map2, int interpolation, intborderMode=BORDER_CONSTANT, const Scalar& borderValue=Scalar())
  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可,且需为单通道8位或者浮点型图像。
  • 第二个参数,OutputArray类型的dst,函数调用后的运算结果存在这里,即这个参数用于存放函数调用后的输出结果,需和源图片有一样的尺寸和类型。
  • 第三个参数,InputArray类型的map1,里面存储着源图像中各像素点的x坐标在目标图像中的x坐标,x坐标就是代表列号
  • 第四个参数,InputArray类型的map2,里面存储着源图像中各像素点的y坐标在目标图像中的y坐标,y坐标就是代表行号
  • 第五个参数,int类型的interpolation,插值方式,可选的插值方式如下:
    • INTER_NEAREST - 最近邻插值
    • INTER_LINEAR – 双线性插值(默认值)
    • INTER_CUBIC – 双三次样条插值(逾4×4像素邻域内的双三次插值)
    • INTER_LANCZOS4 -Lanczos插值(逾8×8像素邻域的Lanczos插值)
  • 第六个参数,int类型的borderMode,边界模式,有默认值BORDER_CONSTANT,表示目标图像中“离群点(outliers)”的像素值不会被此函数修改。具体什么叫离群点我现在也不清楚!
  • 第七个参数,const Scalar&类型的borderValue,当有常数边界时使用的值,其有默认值Scalar( ),即默认值为0。具什么叫有常数边界,我现在也不清楚!
(2)C++: void resize(InputArray src,OutputArray dst, Size dsize, double fx=0, double fy=0, int interpolation=INTER_LINEAR )
  • 第一个参数,InputArray类型的src,输入图像,即源图像,填Mat类的对象即可。
  • 第二个参数,OutputArray类型的dst,输出图像,当其非零时,有着dsize(第三个参数)的尺寸,或者由src.size()计算出来。
  • 第三个参数,Size类型的dsize,输出图像的大小;如果它等于零,由下式进行计算:

其中,dsize,fx,fy都不能为0。

  • 第四个参数,double类型的fx,沿水平轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:

  • 第五个参数,double类型的fy,沿垂直轴的缩放系数,有默认值0,且当其等于0时,由下式进行计算:

  • 第六个参数,int类型的interpolation,用于指定插值方式,默认为INTER_LINEAR(线性插值)。

可选的插值方式如下:

  • INTER_NEAREST - 最近邻插值
  • INTER_LINEAR - 线性插值(默认值)
  • INTER_AREA - 区域插值(利用像素区域关系的重采样插值)
  • INTER_CUBIC –三次样条插值(超过4×4像素邻域内的双三次插值)

INTER_LANCZOS4 -Lanczos插值(超过8×8像素邻域的Lanczos插值

一-、图像的平移

根据上面的公式,就可以编写代码了,平移还是很简单的,基础也是可以升级的,因为你会发现平移后,图像的大小少了一部分,在同样的大小的窗口中是不能打开的,所以你要改进,方法如下

#include<opencv2/core/core.hpp>
#include<opencv2/highgui/highgui.hpp>
using namespace cv;
//平移后大小不变
void translateTransform(cv::Mat const& src, cv::Mat& dst, int dx, int dy)
{
    CV_Assert(src.depth() == CV_8U);
    const int rows = src.rows;
    const int cols = src.cols;
    dst.create(rows, cols, src.type());
    Vec3b *p;
    for (int i = 0; i < rows; i++)
    {
        p = dst.ptr<Vec3b>(i);
        for (int j = 0; j < cols; j++)
        {
            //平移后坐标映射到原图像
            int x = j - dx;
            int y = i - dy;
            //保证映射后的坐标在原图像范围内
            if (x >= 0 && y >= 0 && x < cols && y < rows)
                p[j] = src.ptr<Vec3b>(y)[x];
        }
    }
}
//平移后大小变化
void translateTransformSize(cv::Mat const& src, cv::Mat& dst, int dx, int dy)
{
    CV_Assert(src.depth() == CV_8U);
    const int rows = src.rows + abs(dy); //输出图像的大小
    const int cols = src.cols + abs(dx);
    dst.create(rows, cols, src.type());
    Vec3b *p;
    for (int i = 0; i < rows; i++)
    {
        p = dst.ptr<Vec3b>(i);
        for (int j = 0; j < cols; j++)
        {
            int x = j - dx;
            int y = i - dy;
            if (x >= 0 && y >= 0 && x < src.cols && y < src.rows)
                p[j] = src.ptr<Vec3b>(y)[x];
        }
    }
}
int main(int argc,char** argv[])
{
	Mat srcimage,dst,dst1;
	srcimage=imread("lena.jpg");
	namedWindow("src_window");
	imshow("src_window",srcimage);
    translateTransform(srcimage, dst,50, 50);
	namedWindow("dst_window");
	imshow("dst_window",dst);
   translateTransformSize(srcimage,dst1, 50, 50);
   	namedWindow("dst_window1");
	imshow("dst_window1",dst1);
	waitKey(0);
}

二、镜像

镜像分为水平镜像和垂直镜像,分别是关于y轴和x轴的对称翻转,所以也会看到翻转的说法,原理都是差不多的,左为水平,右为垂直

                    

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int main()
{
    Mat src = imread("lena.jpg",CV_LOAD_IMAGE_UNCHANGED);
    imshow("src",src);
    Mat dst;
    dst.create( src.size(), src.type());
    Mat map_x;
    Mat map_y;
    map_x.create( src.size(), CV_32FC1);
    map_y.create( src.size(), CV_32FC1);
    for( int i = 0; i < src.rows; ++i)
    {
        for( int j = 0; j < src.cols; ++j)
        {
            map_x.at<float>(i, j) = (float) (src.cols - j) ;
			map_y.at<float>(i, j) = (float) i ;  //水平
			//map_x.at<float>(i, j) = (float) j ;
			//map_y.at<float>(i, j) = (float) (src.rows - i) ;  //垂直
        }
    }
    remap(src, dst, map_x, map_y, CV_INTER_LINEAR);
    imshow("dst", dst);
    imwrite("invert2.jpg", dst);
    waitKey(0);
    system("pause");
    return 0;
} 

  

三、旋转

这个我找了一张说的比较简洁来介绍,旋转的时候可以有旋转后图像不被掩盖的(就是图像窗变大),常见的是掩盖的

原理如下:

#include "cv.h"
#include "highgui.h"
#include "math.h"  

// clockwise 为true则顺时针旋转,否则为逆时针旋转
IplImage* rotateImage(IplImage* src, int angle, bool clockwise)
{
    angle = abs(angle) % 180;
    if (angle > 90)
    {
        angle = 90 - (angle % 90);
    }
    IplImage* dst = NULL;
    int width =
        (double)(src->height * sin(angle * CV_PI / 180.0)) +
        (double)(src->width * cos(angle * CV_PI / 180.0 )) + 1;
    int height =
        (double)(src->height * cos(angle * CV_PI / 180.0)) +
        (double)(src->width * sin(angle * CV_PI / 180.0 )) + 1;
    int tempLength = sqrt((double)src->width * src->width + src->height * src->height) + 10;
    int tempX = (tempLength + 1) / 2 - src->width / 2;
    int tempY = (tempLength + 1) / 2 - src->height / 2;
    int flag = -1;  

    dst = cvCreateImage(cvSize(width, height), src->depth, src->nChannels);
    cvZero(dst);
    IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), src->depth, src->nChannels);
    cvZero(temp);  

    cvSetImageROI(temp, cvRect(tempX, tempY, src->width, src->height));
    cvCopy(src, temp, NULL);
    cvResetImageROI(temp);  

    if (clockwise)
        flag = 1;  

    float m[6];
    int w = temp->width;
    int h = temp->height;
    m[0] = (float) cos(flag * angle * CV_PI / 180.);
    m[1] = (float) sin(flag * angle * CV_PI / 180.);
    m[3] = -m[1];
    m[4] = m[0];
    // 将旋转中心移至图像中间
    m[2] = w * 0.5f;
    m[5] = h * 0.5f;
    //
    CvMat M = cvMat(2, 3, CV_32F, m);
    cvGetQuadrangleSubPix(temp, dst, &M);
    cvReleaseImage(&temp);
    return dst;
}  

int main(int argc, char **argv)
{
    IplImage *src = 0;
    IplImage *dst = 0;
    // 旋转角度
    int angle = 90;  

    src = cvLoadImage("lena.jpg",CV_LOAD_IMAGE_COLOR);
    cvNamedWindow("src", 1);
    cvShowImage("src", src);  

    dst = rotateImage(src, angle, false);
    cvNamedWindow("dst", 2);
    cvShowImage("dst", dst);
    cvWaitKey(0);  

    cvReleaseImage(&src);
    cvReleaseImage(&dst);
    return 0;
}  

四、matlab辅助

本来想添加vc6.0的框架的,因为两者可以更好的比较,但是会带来篇幅过长,所以这里用matlab,来实现简单高效

i=imread('D:\lena.jpg'); %读一幅图像
j=imrotate(i,30);%图像旋转30度
k=imresize(i,2);%图像放大两倍
t=imresize(i,2,'bilinear');%采用双线性插值法进行放大两倍
m=imresize(i,0.8);%图像缩小到0.8倍
p=translate(strel(1), [25 25]);%图像平移
img=imdilate(i,p);
figure;
subplot(231);imshow(i);title('原图');
subplot(232);imshow(j);title('旋转');
subplot(233);imshow(k);title('放大');
subplot(234);imshow(t);title('双线性插值');
subplot(235);imshow(m);title('缩小');
subplot(236);imshow(img);title('平移');

时间: 2024-10-13 07:22:13

Opencv图像识别从零到精通(7)----图像平移、旋转、镜像的相关文章

Opencv图像识别从零到精通(19)----Robert,prewitt,Sobel边缘检测

图像的边缘检测,是根据灰度的突变或者说不连续来检测,对于其中的算子有一阶导数和二价导数,这里先说基础的三种方法---Robert,prewitt,Sobel边缘检测.  一.梯度 首先介绍下梯度,梯度并非是一个数值,梯度严格意义上是一个向量,这个向量指向当前位置变化最快的方向,可以这么理解,当你站在一个山上,你有360°的方向可以选择,哪个方向下降速度最快(最陡峭),便是梯度方向,梯度的长度,表示为向量的长度,表示最大的变化速率. 梯度的数学表达: 在二维中只取前两项,也就是由x方向的偏微分和y

Opencv图像识别从零到精通(29)-----图像金字塔,向上上下采样,resize插值

金字塔的底部是待处理图像的高分辨率表示,而顶部是低分辨率的近似.我们将一层一层的图像比喻成金字塔,层级越高,则图像越小,分辨率越低 一.两个金字塔 高斯金字塔(Gaussianpyramid): 用来向下采样,主要的图像金字塔 拉普拉斯金字塔(Laplacianpyramid): 用来从金字塔低层图像重建上层未采样图像,在数字图像处理中也即是预测残差,可以对图像进行最大程度的还原,配合高斯金字塔一起使用. 高斯金字塔不同(DoG)又称为拉普拉斯金字塔,给出计算方式前,先加强一下定义 记得在上面我

Opencv图像识别从零到精通(31)----图像修补,分离合并通道

一.图像修复简介 图像修复是图像复原中的一个重要内容,其目的是利用图像现有的信息来恢复丢失的信息.可用于旧照片中丢失信息的恢复,视频文字去除以及视频错误隐藏等.简言之,图像修复就是对图像上信息缺损区域进行信息填充的过程,其目的就是为了对有信息缺损的图像进行复原,并且使得观察者无法察觉到图像曾经缺损或者已经修复      图像修复技术简单来说,就是利用那些被破坏区域的边缘,即是边缘的颜色和结构,繁殖和混合到损坏的图像中,来进行修复图像 目前存在两大类图像修复技术:一类是用于修复小尺度缺损的数字图像

Opencv图像识别从零到精通(26)---分水岭

分水岭是区域分割三个方法的最后一个,对于前景背景的分割有不错的效果. 分水岭分割方法,是一种基于拓扑理论的数学形态学的分割方法,其基本思想是把图像看作是测地学上的拓扑地貌,图像中每一点像素的灰度值表示该点的海拔高度,每一个局部极小值及其影响区域称为集水盆,而集水盆的边界则形成分水岭.分水岭的概念和形成可以通过模拟浸入过程来说明.在每一个局部极小值表面,刺穿一个小孔,然后把整个模型慢慢浸入水中,随着浸入的加深,每一个局部极小值的影响域慢慢向外扩展,在两个集水盆汇合处构筑大坝,即形成分水岭. 分水岭

Opencv图像识别从零到精通(30)---重映射,仿射变换

一.序言 面对图像处理的时候,我们会旋转缩放图像,例如前面所提高的resize 插值改变,也是几何变换: 几何运算需要空间变换和灰度级差值两个步骤的算法,像素通过变换映射到新的坐标位置,新的位置可能是在几个像素之间,即不一定为整数坐标.这时就需要灰度级差值将映射的新坐标匹配到输出像素之间.最简单的插值方法是最近邻插值,就是令输出像素的灰度值等于映射最近的位置像素,该方法可能会产生锯齿.这种方法也叫零阶插值,相应比较复杂的还有一阶和高阶插值. 除了插值算法感觉只要了解就可以了,图像处理中比较需要理

Opencv图像识别从零到精通(33)----moravec角点、harris角点

一.角点 图像处理和与计算机视觉领域,兴趣点(interest points),或称作关键点(keypoints).特征点(feature points) 被大量用于解决物体识别,图像识别.图像匹配.视觉跟踪.三维重建等一系列的问题.我们不再观察整幅图,而是选择某些特殊的点,然后对他们进行局部有的放矢的分析.如果能检测到足够多的这种点,同时他们的区分度很高,并且可以精确定位稳定的特征,那么这个方法就有使用价值. 图像特征类型可以被分为如下三种: <1>边缘                   

Opencv图像识别从零到精通(24)------漫水填充,种子填充,区域生长、孔洞填充

可以说从这篇文章开始,就结束了图像识别的入门基础,来到了第二阶段的学习.在平时处理二值图像的时候,除了要进行形态学的一些操作,还有有上一节讲到的轮廓连通区域的面积周长标记等,还有一个最常见的就是孔洞的填充,opencv这里成为漫水填充,其实也可以叫种子填充,或者区域生长,基本的原理是一样的,但是应用的时候需要注意一下,种子填充用递归的办法,回溯算法,漫水填充使用堆栈,提高效率,同时还提供了一种方式是扫描行.经常用来填充孔洞,现在来具体看看. 漫水填充:也就是用一定颜色填充联通区域,通过设置可连通

Opencv图像识别从零到精通(27)---grabcut

这是基于图论的分割方法,所以开始就先介绍了 Graph cuts,然后再到Grab cut   一. Graph cuts Graph cuts是一种十分有用和流行的能量优化算法,在计算机视觉领域普遍应用于前背景分割(Image segmentation).立体视觉(stereo vision).抠图(Image matting)等. 此类方法把图像分割问题与图的最小割(min cut)问题相关联.首先用一个无向图G=<V,E>表示要分割的图像,V和E分别是顶点(vertex)和边(edge)

Opencv图像识别从零到精通(34)---SIFI

   一.理论知识 Scale Invariant Feature Transform,尺度不变特征变换匹配算法,对于算法的理论介绍,可以参考这篇文章http://blog.csdn.net/qq_20823641/article/details/51692415,里面很详细,可以更好的学习.这里就不多介绍.后面就挑选重点的来说 二.SIFT 主要思想 SIFT算法是一种提取局部特征的算法,在尺度空间寻找极值点,提取位置,尺度,旋转不变量. 三.SIFT算法的主要特点: a) SIFT特征是图像