图像仿射变换

仿射变换


问题描述:读取图像,然后对图像多种仿射变换。

仿射变换-缩放与平移

现在我们可以利用 3 × 3 3\times3 3×3的矩阵来对图像进行多种仿射变换。

现,我们记原图像为 ( x , y ) (x,y) (x,y),变换后的图像为 ( x ′ , y ′ ) (x',y') (x,y)

对于二维空间上的坐标操作,其变换关系满足(在此处,我们可以将此变换操作理解为图像的放大缩小矩阵)下式:
( x ′ y ′ ) = ( a b c d )   ( x y ) \left( \begin{matrix} x'\\ y' \end{matrix} \right)= \left( \begin{matrix} a&b\\ c&d \end{matrix} \right)\ \left( \begin{matrix} x\\ y \end{matrix} \right) (xy)=(acbd) (xy)
另一方面,二维空间中的平移操作可按照下面的式子计算:
( x ′ y ′ ) = ( x y ) + ( t x t y ) \left( \begin{matrix} x'\\ y' \end{matrix} \right)= \left( \begin{matrix} x\\ y \end{matrix} \right)+ \left( \begin{matrix} t_x\\ t_y \end{matrix} \right) (xy)=(xy)+(txty)
把上面两个式子在三维空间中张开,就可以写作以下等式:
( x ′ y ′ 1 ) = ( a b t x c d t y 0 0 1 )   ( x y 1 ) \left( \begin{matrix} x'\\ y'\\ 1 \end{matrix} \right)= \left( \begin{matrix} a&b&t_x\\ c&d&t_y\\ 0&0&1 \end{matrix} \right)\ \left( \begin{matrix} x\\ y\\ 1 \end{matrix} \right) xy1=ac0bd0txty1 xy1

但是在实际操作的过程中,如果一个一个地计算原图像的像素的话,处理后的像素可能没有在原图像中有对应的坐标。因此,我们有必要对处理后的图像中各个像素进行仿射变换逆变换,取得变换后图像中的像素在原图像中的坐标。仿射变换的逆变换如下:
( x y ) = 1 a   d − b   c   ( d − b − c a )   ( x ′ y ′ ) − ( t x t y ) \left( \begin{matrix} x\\ y \end{matrix} \right)= \frac{1}{a\ d-b\ c}\ \left( \begin{matrix} d&-b\\ -c&a \end{matrix} \right)\ \left( \begin{matrix} x'\\ y' \end{matrix} \right)- \left( \begin{matrix} t_x\\ t_y \end{matrix} \right) (xy)=a db c1 (dcba) (xy)(txty)
这回的平行移动操作使用下面的式子计算。 t x t_x tx t y t_y ty是像素移动的距离。
( x ′ y ′ 1 ) = ( 1 0 t x 0 1 t y 0 0 1 )   ( x y 1 ) \left( \begin{matrix} x'\\ y'\\ 1 \end{matrix} \right)= \left( \begin{matrix} 1&0&t_x\\ 0&1&t_y\\ 0&0&1 \end{matrix} \right)\ \left( \begin{matrix} x\\ y\\ 1 \end{matrix} \right) xy1=100010txty1 xy1

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <math.h>

cv::Mat affine(cv::Mat img, double a, double b, double c, double d, double tx, double ty)
{
    int row = img.rows;
    int col = img.cols;
    int channel = img.channels();

    // get detriment
    double det = a * d - b * c;

    // Resize width and height
    int resized_col = (int)(col * a);
    int resized_row = (int)(row * d);

    cv::Mat new_image = cv::Mat::zeros(resized_row, resized_col, CV_8UC3);

    int pos_before_x, pos_before_y;

    // 仿射变换
    for (int i = 0; i < resized_row; i++)
    {
        for (int j = 0; j < resized_col; j++)
        {
            pos_before_x = (int)((d * j - b * i) / det - tx);
            if (pos_before_x < 0 || pos_before_x > col)
            {
                continue;
            }

            pos_before_y = int((-c * j + a * i) / det - ty);
            if (pos_before_y < 0 || pos_before_y > row)
            {
                continue;
            }

            for (int chan = 0; chan < channel; chan++)
            {
                new_image.at<cv::Vec3b>(i, j)[chan] = img.at<cv::Vec3b>(pos_before_y, pos_before_x)[chan];
            }
        }
    }

    return new_image;
}


int main(){
    // read image
    cv::Mat img = cv::imread("../fate.jpeg", cv::IMREAD_COLOR);

    // x向向右平移100,y向向上平移100
//    cv::Mat new_image = affine(img, 1, 0, 0, 1, 100, -100);
    // x向放大1.3倍再向右平移100,y向放大0.8倍再向上平移100
    cv::Mat new_image = affine(img, 1.3, 0, 0, 0.8, 100, -100);

//    cv::imwrite("../1-10/fate_affine.jpeg", new_image);
    cv::imwrite("../1-10/fate_affine1.jpeg", new_image);
    cv::imshow("vv", new_image);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}
输入图像 (fate.jpeg)输出图像 (fate_trans.jpeg)输出图像 (fate_trans_scale.jpeg)
在这里插入图片描述在这里插入图片描述在这里插入图片描述

仿射变换-旋转

  1. 使用仿射变换,逆时针旋转 30 30 30度。
  2. 使用仿射变换,逆时针旋转 30 30 30度并且能让全部图像显现(也就是说,单纯地做仿射变换会让图片边缘丢失,这一步中要让图像的边缘不丢失)。

可以借助二维旋转矩阵,利用下面的式子进行逆时针方向旋转 A A A度的仿射变换:
( x ′ y ′ 1 ) = ( cos ⁡ ( A ) − sin ⁡ ( A ) t x sin ⁡ ( A ) cos ⁡ ( A ) t y 0 0 1 )   ( x y 1 ) \left( \begin{matrix} x'\\ y'\\ 1 \end{matrix} \right)= \left( \begin{matrix} \cos(A)&-\sin(A)&t_x\\ \sin(A)&\cos(A)&t_y\\ 0&0&1 \end{matrix} \right)\ \left( \begin{matrix} x\\ y\\ 1 \end{matrix} \right) xy1=cos(A)sin(A)0sin(A)cos(A)0txty1 xy1

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <math.h>

cv::Mat affine(cv::Mat img, double a, double b, double c, double d, double tx, double ty, double theta)
{
    int row = img.rows;
    int col = img.cols;
    int channel = img.channels();

    // get detriment
    double det = a * d - b * c;

    if (theta != 0)
    {
        // 如果存在旋转就重新定义仿射矩阵参量
        double rad = theta / 180 * M_PI;
        a = std::cos(rad);
        b = -std::sin(rad);
        c = std::sin(rad);
        d = std::cos(rad);
        tx = 0;
        ty = 0;

        double det = a * d - b * c;

        // 定义旋转前后的中心,以及两个中心的偏移量
        double center_x = col / 2.;
        double center_y = row / 2.;
        double rot_center_x = (d * center_x - b * center_y) / det;
        double rot_center_y = (-c * center_x + a * center_y) / det;
        tx = rot_center_x - center_x;
        ty = rot_center_y - center_y;
    }

    // Resize width and height
    int resized_col = (int)(col * a);
    int resized_row = (int)(row * d);

    if (theta != 0) {
        resized_col = (int)(col);
        resized_row = (int)(row);
    }

    cv::Mat new_image = cv::Mat::zeros(resized_row, resized_col, CV_8UC3);

    int pos_before_x, pos_before_y;

    // 仿射变换
    for (int i = 0; i < resized_row; i++)
    {
        for (int j = 0; j < resized_col; j++)
        {
            pos_before_x = (int)((d * j - b * i) / det - tx);
            if (pos_before_x < 0 || pos_before_x >= col)
            {
                continue;
            }

            pos_before_y = int((-c * j + a * i) / det - ty);
            if (pos_before_y < 0 || pos_before_y >= row)
            {
                continue;
            }

            for (int chan = 0; chan < channel; chan++)
            {
                new_image.at<cv::Vec3b>(i, j)[chan] = img.at<cv::Vec3b>(pos_before_y, pos_before_x)[chan];
            }
        }
    }

    return new_image;
}


int main(){
    // read image
    cv::Mat img = cv::imread("../Neopolitan.png", cv::IMREAD_COLOR);

    // 先旋转50度,将其旋转后的图像拉回图像中心
    cv::Mat new_image = affine(img, 1, 0, 0, 1, 0, 0, 50);

//    cv::imwrite("../1-10/fate_affine.jpeg", new_image);
    cv::imwrite("../1-10/Neopolitan_rot.png", new_image);
    cv::imshow("vv", new_image);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}
输入图像 (Neopolitan.png)输出图像 (Neopolitan_rot.png)
在这里插入图片描述在这里插入图片描述

仿射变换-倾斜

  1. 使用仿射变换,输出 x x x轴倾斜 30 30 30度的图像( t x = 30 t_x=30 tx=30),这种变换被称为X-sharing。
  2. 使用仿射变换,输出y轴倾斜 30 30 30度的图像( t y = 30 t_y=30 ty=30),这种变换被称为Y-sharing。
  3. 使用仿射变换,输出 x x x轴、 y y y轴都倾斜 30 30 30度的图像( t x = 30 t_x = 30 tx=30 t y = 30 t_y = 30 ty=30)。

引入非正交参量 a a a来表达倾斜,其值定义为 t x   t y t_x\ t_y tx ty与原图像的大小 h   w h\ w h w的比值,可以使用下面各式进行仿射变换:

  • X-sharing
    a = t x h [ x ′ y ′ 1 ] = [ 1 a t x 0 1 t y 0 0 1 ]   [ x y 1 ] a=\frac{t_x}{h}\\ \left[ \begin{matrix} x'\\ y'\\ 1 \end{matrix} \right]=\left[ \begin{matrix} 1&a&t_x\\ 0&1&t_y\\ 0&0&1 \end{matrix} \right]\ \left[ \begin{matrix} x\\ y\\ 1 \end{matrix} \right] a=htxxy1=100a10txty1 xy1

  • Y-sharing
    a = t y w [ x ′ y ′ 1 ] = [ 1 0 t x a 1 t y 0 0 1 ]   [ x y 1 ] a=\frac{t_y}{w}\\ \left[ \begin{matrix} x'\\ y'\\ 1 \end{matrix} \right]=\left[ \begin{matrix} 1&0&t_x\\ a&1&t_y\\ 0&0&1 \end{matrix} \right]\ \left[ \begin{matrix} x\\ y\\ 1 \end{matrix} \right] a=wtyxy1=1a0010txty1 xy1

#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <math.h>

cv::Mat affine(cv::Mat img, double a, double b, double c, double d, double tx, double ty, double theta, double dx, double dy)
{
    int row = img.rows;
    int col = img.cols;
    int channel = img.channels();

    // get detriment
    double det = a * d - b * c;

    if (dx != 0){
        b = dx / row;
    }
    if (dy != 0){
        c = dy / col;
    }

    if (theta != 0)
    {
        // 如果存在旋转就重新定义仿射矩阵参量
        double rad = theta / 180 * M_PI;
        a = std::cos(rad);
        b = -std::sin(rad);
        c = std::sin(rad);
        d = std::cos(rad);
        tx = 0;
        ty = 0;

        double det = a * d - b * c;

        // 定义旋转前后的中心,以及两个中心的偏移量
        double center_x = col / 2.;
        double center_y = row / 2.;
        double rot_center_x = (d * center_x - b * center_y) / det;
        double rot_center_y = (-c * center_x + a * center_y) / det;
        tx = rot_center_x - center_x;
        ty = rot_center_y - center_y;
    }

    // Resize width and height
    int resized_col = (int)(col * a + dx);
    int resized_row = (int)(row * d + dy);

    if (theta != 0) {
        resized_col = (int)(col + dx);
        resized_row = (int)(row + dy);
    }

    cv::Mat new_image = cv::Mat::zeros(resized_row, resized_col, CV_8UC3);

    int pos_before_x, pos_before_y;

    // 仿射变换
    for (int i = 0; i < resized_row; i++)
    {
        for (int j = 0; j < resized_col; j++)
        {
            pos_before_x = (int)((d * j - b * i) / det - tx);
            if (pos_before_x < 0 || pos_before_x >= col)
            {
                continue;
            }

            pos_before_y = int((-c * j + a * i) / det - ty);
            if (pos_before_y < 0 || pos_before_y >= row)
            {
                continue;
            }

            for (int chan = 0; chan < channel; chan++)
            {
                new_image.at<cv::Vec3b>(i, j)[chan] = img.at<cv::Vec3b>(pos_before_y, pos_before_x)[chan];
            }
        }
    }

    return new_image;
}


int main(){
    // read image
    cv::Mat img = cv::imread("../fate.jpeg", cv::IMREAD_COLOR);

    // 倾斜
    cv::Mat new_image = affine(img, 1, 0, 0, 1, 0, 0, 0, 60, 0);

//    cv::imwrite("../1-10/fate_affine.jpeg", new_image);
    cv::imwrite("../1-10/fate_test12.jpeg", new_image);
    cv::imshow("vv", new_image);
    cv::waitKey(0);
    cv::destroyAllWindows();

    return 0;
}
输入图像 (fate.jpeg)输出图像 (fate_x.jpeg)输出图像 (fate_y.jpeg)输出图像 (fate_xy.jpeg)
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值