码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2016-07-16 23:49:37      阅读:1764      评论:0      收藏:0      [点我收藏+]

标签:

         根据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('平移');

   技术分享

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

标签:

原文地址:http://blog.csdn.net/qq_20823641/article/details/51925091

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!