数字图象处理——仿射变换

在《数字图象处理》中提供了基于

图像仿射矩阵T。图像旋转、偏移变换等变换原理相同,根据这些在自己实现时,可分为三个步骤:

  1. 坐标转换

    这些变换是以图像的中心为坐标原点(O’(x’,y’)),而图片原本设定是以图像左上角为坐标原点(O(x,y))。所以要进行坐标的变换。

    用矩阵的方式表示:

    逆运算:

  2. 图像变换

    变换公式:

    逆运算:

  3. 坐标还原

    因为此时,坐标原点是在图像的中心,为方便操作需要将坐标原点重新变换到图像左上角。

    从旋转后到旋转前的坐标变换为:

    (w’,h’是旋转后的图像的宽高);

    而逆运算为:

    **

下面是旋转的最邻近内插法和双线性内插法的实现:

void nearestInterpolation(Mat &src, Mat &dst, float dx, float dy, double theta);
int mainqqw()
{
    cv::Mat src = imread("D:/xitong/picture/rain.jpg");
    namedWindow("orginal");
    imshow("orginal", src);
    int srcwidth = src.cols;
    int srcheigh = src.rows;
    /*旋转角度*/
    double theta = 30.0f*3.1415926 / 180.0f;
    /*
        转换坐标原点到图像中心
                ∧y
                |
            0   |   1
                |
        --------o--------->x
                |
            2   |   3
                |
    */
    float srcX[4], srcY[4];
    srcX[0] = (float)(-((srcwidth - 1) / 2));
    srcX[1] = (float)((srcwidth - 1) / 2);
    srcX[2] = (float)(-(srcwidth - 1) / 2);
    srcX[3] = (float)((srcwidth - 1) / 2);
    srcY[0] = (float)((srcheigh - 1) / 2);
    srcY[1] = (float)((srcheigh - 1) / 2);
    srcY[2] = (float)(-(srcheigh - 1) / 2);
    srcY[3] = (float)(-(srcheigh - 1) / 2);

    /*
        旋转后的图像坐标,此时坐标原点依然是旋转中心
    */
    float dstX[4], dstY[4];
    for (int i = 0; i < 4; i++)
    {
        dstX[i] = cos(theta)*srcX[i] + sin(theta)*srcY[i];
        dstY[i] = -sin(theta)*srcX[i] + cos(theta)*srcY[i];
    }

    /*
        ==>旋转后图像长宽
    */
    int dstwidth = (max(fabs(dstX[3] - dstX[0]), fabs(dstX[2] - dstX[1])) + 0.5);
    int dstheigh = (max(fabs(dstY[3] - dstY[0]), fabs(dstY[2] - dstY[1])) + 0.5);

    /*Mat dst = Mat(Size(src.rows * 2, src.cols * 2), src.type(), Scalar::all(0));
    nearestInterpolation(src, dst, 0.5);*/
    Mat dst;
    dst.create(dstheigh, dstwidth, src.type());

    //**式在运算后的常量,为运算方便提前的出结果
    float dx = -0.5*dstwidth*cos(theta) - 0.5*dstheigh*sin(theta) + 0.5*srcwidth;
    float dy = 0.5*dstwidth*sin(theta) - 0.5*dstheigh*cos(theta) + 0.5*srcheigh;

    nearestInterpolation(src, dst, dx, dy, theta);
    waitKey();
    return 0;
}

/*
    最邻近内插旋转
*/
void nearestInterpolation(Mat &src, Mat &dst, float dx, float dy, double theta)
{
    int x, y;
    for (int i = 0; i < dst.rows; i++)
    {
        for (int j = 0; j < dst.cols; j++)
        {
            /*
                **式,的运算结果
            */
            x = cvFloor(float(j)*cos(theta) + float(i)*sin(theta) + dx);
            y = cvFloor(float(-j)*sin(theta) + float(i)*cos(theta) + dy);
            if ((x < 0) || (x >= src.cols) || (y < 0) || (y >= src.rows))
            {
                if (src.channels() == 3)
                    dst.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
                if (src.channels() == 1)
                    dst.at<uchar>(i, j) = 0;
            }
            else
            {
                if (src.channels() == 3)
                    dst.at<Vec3b>(i, j) = src.at<Vec3b>(y, x);
                if (src.channels() == 1)
                    dst.at<uchar>(i, j) = src.at<uchar>(y, x);
            }

        }
    }
    namedWindow("最邻近内插旋转");
    imshow("最邻近内插旋转", dst);
}
/*
    双线性内插法旋转
*/
void bilinearRotate(Mat &src, Mat &dst, float dx, float dy, double theta)
{
    float fu, fv;
    int x, y;
    Vec3b point[4];
    uchar upoint[4];
    for (int j = 0; j < dst.rows; j++)
    {
        for (int i = 0; i < dst.cols; i++)
        {
            fu = float(j)*cos(theta) + float(i)*sin(theta) + dx;
            fv = float(-j)*sin(theta) + float(i)*cos(theta) + dy;
            x = cvFloor(fu);
            y = cvFloor(fv);
            fu -= x;
            fv -= y;

            if ((x < 0) || (x >= src.cols-1) || (y < 0) || (y >= src.rows-1))
            {
                if (src.channels() == 3)
                    dst.at<Vec3b>(i, j) = Vec3b(0, 0, 0);
                if (src.channels() == 1)
                    dst.at<uchar>(i, j) = 0;
            }
            else
            {
                if (src.channels() == 3)
                {
                    point[0] = src.at<Vec3b>(y, x);
                    point[1] = src.at<Vec3b>(y + 1, x);
                    point[2] = src.at<Vec3b>(y, x + 1);
                    point[3] = src.at<Vec3b>(y + 1, x + 1);
                    dst.at<Vec3b>(i, j) = (1 - fu)*(1 - fv)*point[0] + (1 - fu)*(fv)*point[1] + (1 - fv)*(fu)*point[2] + fu*fv*point[3];
                }

                if (src.channels() == 1)
                {
                    upoint[0] = src.at<uchar>(y, x);
                    upoint[1] = src.at<uchar>(y + 1, x);
                    upoint[2] = src.at<uchar>(y, x + 1);
                    upoint[3] = src.at<uchar>(y + 1, x + 1);
                    dst.at<uchar>(i, j) = (1 - fu)*(1 - fv)*upoint[0] + (1 - fu)*(fv)*upoint[1] + (1 - fv)*(fu)*upoint[2] + fu*fv*upoint[3];
                }
            }
        }
    }
    namedWindow("双线性内插法旋转");
    imshow("双线性内插法旋转", dst);
}
时间: 2024-10-10 14:46:04

数字图象处理——仿射变换的相关文章

数字图象处理之读取显示——Matlab读取三维mat数据并灰度显示

目的: 手里面有一个(1040,1392,31)的数据,mat格式,我希望看到深度1的矩阵,也就是(1-1040,1-1391,1)符合这样下标的数据,并且显示出来.也就是鼠标处的ref.如果像我这样一个mat里面含有两个矩阵,那么双击load,会在工作区load两个矩阵. 代码: A=ref(:,:,1);//冒号就是表示这一个维度我都要,这样下来,我这个矩阵A是个二维的,大小是(1040,1392),之后就可以用 G=uint8(A);//转换成八位的灰度值(0-255),也可以用uint1

我用C++的理由——关于C和C++的选择

? 摘自:http://www.xue163.com/32/6/325715.html,作者:王可.整理的很好! 首先,我不会使用Java或C#,能力上不会,主观上也不会,因为两点原因:1,他们都属于解释型的语言,这有很多问题是我无法容忍的,程序的速度和封装的安全性:2,他们都不够底层,没有指针,却加载了内存管理器,对我来说这些都是麻烦和束缚,对我而言他们都不是足够自由的语言.或者说,我无法接受他们对使用者的理念,似乎他们认为使用他们的程序员都是懒惰和容易犯错误的,而他们的高级之处都是依赖各种类

计算机图形学学习方法和相关书籍,做游戏,GIS,虚拟现实,三维引擎的都可以看看.

本书参照<<图形学扫盲>> 整理的,原文内容引子: http://www.cppblog.com/lai3d/archive/2008/12/30/70796.html 前言: 以我现在的水平观之,3D图形学分3大块的学习内容: a.空间几何数学:空间几何变换,加速算法,多边形技术,曲线和曲面,相交测试,碰撞测试. b.光照着色系统:光照,纹理贴图,高级象素着色光照,艺术性渲染. c.程序技术性应用:公告板,精灵,天空盒,体绘制,材质系统,场景图,渲染队列. 在实际学习过程中,3个

Atitit.attilax软件研发与项目管理之道

1. 前言4 2. 鸣谢4 3. Genesis 创世记4 4. 软件发展史4 5. 箴言4 6. 使徒行传 4 7. attilax书 4 8. 启示录4 9. 技术标准的7条原则4 9.1. 后向兼容性4 10. 软件之道5 11. 计算机科学导论(原书第3版5 12. 数字电路5 13. 通用管理学5 14. 项目管理5 15. 团队建设与人力资源管理5 16. 软件工程5 16.1. 软件编写5 16.2. 软件构件化理论与技术5 16.3. 软件与编程理论6 16.4. 理论原则6 1

名校计算机科学与技术专业培养方案

PS:清华大学计算机科学与技术专业本科生培养方案 计算机科学与技术(0812) 一.研究方向 1.通信软件工程 2.网络技术与应用 3.分布计算理论与技术 4.信息安全与多媒体技术 二.课程设置 类别 课程编号 课程名称 学时 学分 学期 学位课 公共必修课 512.8*704 自然辩证法概论 54 2 秋 521.8*300 科学社会主义理论与实践 36 1 春 534.8*445 英语 144 4 秋/春 基础理论课(至少选1门) 813.8*279 近世代数及其应用 54 3 秋 513.

C#实现通过ffmpeg从flv视频文件中截图的方法

本文实例讲述了C#实现通过ffmpeg从flv视频文件中截图的方法.分享给大家供大家参考.具体分析如下: 需要先下载ffmpeg,这是开源的,代码如下所示: 代码如下: using System; using System.Configuration; public class PublicMethod:System.Web.UI.Page { public PublicMethod() { } //文件路径 public static string ffmpegtool = "ffmpeg/f

【练习4.7】使用键盘控制透视变换和仿射变换的变换矩阵:实现拉伸、收缩、扭曲、旋转

<学习OpenCV>中文版第4章第7题  注意:操作的使用将输入法状态切换到“英文”状态 提纲 题目要求 程序代码 结果图片 题目要求: a.使用数字键1~9以及数字键与Shift的组合,实现透视变换变换矩阵中对应元素的增大和缩小 b.使用上下方向键实现仿射变换变换矩阵中对应元素的增大和缩小,以实现对图片的缩放. c.使用左右方向键实现仿射变换变换矩阵中对应元素的增大和缩小,以实现对图片的旋转. 程序代码: 1 #include "stdafx.h" 2 #include

【数字图像处理之(一)】数字图像处理与相关领域概述

数字图像(Digital Image) 一副图像可以定义为一个二维函数f(x, y),这里的x和y是空间坐标,而在任意坐标(x, y)处的幅度f被称为这一坐标位置图像的亮度或灰度.当x.y和f的幅值都是有限的离散值时,称为数字图像.注意,数字图像由有限数量的元素组成,每个元素都有特殊的位置和数值.这些元素称为画像元素.图像元素和像素,像素是定义数字图像元素时使用最广泛的术语. --Digital Image Pricessing Using MATLAB( Rafacel C. Gonzalez

全数字锁相环(PLL)的原理简介以及verilog设计代码

随着数字电路技术的发展,数字锁相环在调制解调.频率合成.FM 立体声解码.彩色副载波同步.图象处理等各个方面得到了广泛的应用.数字锁相环不仅吸收了数字电路可靠性高.体积小.价格低等优点,还解决了模拟锁相环的直流零点漂移.器件饱和及易受电源和环境温度变化等缺点,此外还具有对离散样值的实时处理能力,已成为锁相技术发展的方向. 所谓数字PLL,就是指应用于数字系统的PLL,也就是说数字PLL中的各个模块都是以数字器件来实现的,是一个数字的电路. 数字锁相环的优点是电路最简单有效,可采用没有压控的晶振,