2016-08-08 09:55:25 qq_25867649 阅读数 752

旋转(rotation)有一个绕着什么转的问题,通常的做法是以图象的中心为圆心旋转,类似下面这种情况:


可以看出,旋转后图象变大了。另一种做法是不让图象变大,转出的部分被裁剪掉如图2.9所示。

我们采用第一种做法,首先给出变换矩阵。在我们熟悉的坐标系中,将一个点顺时针旋转a角后的坐标变换公式r为该点到原点的距离,在旋转过程中,r保持不变;brx轴之间的夹角。



旋转前:x0=rcosby0=rsinb

旋转a角度后:

x1=rcos(b-a)=rcosbcosa+rsinbsina=x0cosa+y0sina

y1=rsin(b-a)=rsinbcosa-rcosbsina=-x0sina+y0cosa

以矩阵的形式表示:


上面的公式中,坐标系xoy是以图象的中心为原点,向右为x轴正方向,向上为y轴正方向。它和以图象左上角点为原点o’,向右为x’轴正方向,向下为y’轴正方向的坐标系x’o’y’之间的转换关系如何呢?


设图象的宽为w,高为h,容易得到:


逆变换为:


有了上面的公式,我们可以把变换分成三步:

1.将坐标系o’变成o

2.将该点顺时针旋转a角;

3.将坐标系o变回o’,这样,我们就得到了变换矩阵,是上面三个矩阵的级联。

这样,对于新图中的每一点,我们就可以根据公式求出对应原图中的点,得到它的灰度。如果超出原图范围,则填成白色。要注意的是,由于有浮点运算,计算出来点的坐标可能不是整数,采用取整处理,即找最接近的点,这样会带来一些误差(图象可能会出现锯齿)

这里的打开bmp格式图像代码就不再贴出来了,就贴出来旋转类(这个类是新加的,对话框里面可以输入旋转角度)里面的代码和调用代码。

View.h

void CMyView::OnXuanzhuan() 
{
	// TODO: Add your command handler code here
	CMyDoc* pc=GetDocument();
	CXuanzhuan dlg;
	dlg.SetDocument(pc);
	dlg.DoModal();
	Invalidate();
}
Xuanzhuan.h

#if !defined(AFX_XUANZHUAN_H__B7192E70_1EA8_4EEF_BC0E_A30D3785DC93__INCLUDED_)
#define AFX_XUANZHUAN_H__B7192E70_1EA8_4EEF_BC0E_A30D3785DC93__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
// Xuanzhuan.h : header file
//
#include "图像旋转Doc.h"


/////////////////////////////////////////////////////////////////////////////
// CXuanzhuan dialog

class CXuanzhuan : public CDialog
{
public:
	int m_jiaodu;
	void xuanzhuan(BYTE *&shuju,int width,int height,int du);

	
    CMyDoc*poc;
	void SetDocument(CMyDoc *m);
	CMyDoc* GetDocument();
	int GetData();
// Construction
public:
	CXuanzhuan(CWnd* pParent = NULL);   // standard constructor

// Dialog Data
	//{{AFX_DATA(CXuanzhuan)
	enum { IDD = IDD_DIALOG1 };
		// NOTE: the ClassWizard will add data members here
	//}}AFX_DATA


// Overrides
	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CXuanzhuan)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:

	// Generated message map functions
	//{{AFX_MSG(CXuanzhuan)
	afx_msg void OnChangeJiaodu();
	virtual void OnOK();
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_XUANZHUAN_H__B7192E70_1EA8_4EEF_BC0E_A30D3785DC93__INCLUDED_)
Xuanzhuan.cpp:

// Xuanzhuan.cpp : implementation file
//

#include "stdafx.h"
#include "图像旋转.h"
#include "Xuanzhuan.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
#include "图像旋转Doc.h"
/////////////////////////////////////////////////////////////////////////////
// CXuanzhuan dialog


CXuanzhuan::CXuanzhuan(CWnd* pParent /*=NULL*/)
	: CDialog(CXuanzhuan::IDD, pParent)
{
	//{{AFX_DATA_INIT(CXuanzhuan)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT
	poc=NULL;
}


void CXuanzhuan::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CXuanzhuan)
		// NOTE: the ClassWizard will add DDX and DDV calls here
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CXuanzhuan, CDialog)
	//{{AFX_MSG_MAP(CXuanzhuan)
	ON_EN_CHANGE(IDC_JIAODU, OnChangeJiaodu)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
// CXuanzhuan message handlers

void CXuanzhuan::OnChangeJiaodu() 
{
	// TODO: If this is a RICHEDIT control, the control will not
	// send this notification unless you override the CDialog::OnInitDialog()
	// function and call CRichEditCtrl().SetEventMask()
	// with the ENM_CHANGE flag ORed into the mask.
	m_jiaodu=GetDlgItemInt(IDC_JIAODU);
	// TODO: Add your control notification handler code here
	
}

void CXuanzhuan::OnOK() 
{
	// TODO: Add extra validation here
	xuanzhuan(GetDocument()->bmpdata,GetDocument()->bmpinfo->bmiHeader.biWidth,GetDocument()->bmpinfo->bmiHeader.biHeight,GetData());

	CDialog::OnOK();
}


void CXuanzhuan::xuanzhuan(BYTE *&shuju,int width,int height,int du)
{
	float cosa;//余弦值
	float sina;//正弦值

	BYTE *data;//临时存储
	BYTE *yuan;
	BYTE *bian;
	
	float srcx1,srcx2,srcx3,srcx4,srcy1,srcy2,srcy3,srcy4;//定义原图像的四个角的坐标
	float dstx1,dstx2,dstx3,dstx4,dsty1,dsty2,dsty3,dsty4;//定义新图像的四个角的坐标
	
	int newW;//新的宽度
	int newH;//新的高度

	int x0,y0;//原来的图像坐标
	int x1,y1;//旋转后的图像坐标

	float num1,num2;//常用值
	float du1;
	du1=(float)RADIAN(du);//角度到弧度的转化

	cosa=(float)cos((double)du1);//计算cosa的值
	sina=(float)sin((double)du1);//计算sina的值
////计算出原来的坐标  以图像中心为原点  向右为x的正半轴 向下为y的正半轴
	srcx1=(float)(-0.5*width);  srcy1=(float)(0.5*height);
	srcx2=(float)(0.5*width);   srcy2=(float)(0.5*height);
	srcx3=(float)(-0.5*width);  srcy3=(float)(-0.5*height);
	srcx4=(float)(0.5*width);   srcy4=(float)(-0.5*height);
//////计算出新图像的四个角的坐标  还是以图像中心为原点
	dstx1=(float)(cosa*srcx1+sina*srcy1);  dsty1=(float)(-sina*srcx1+cosa*srcy1);
	dstx2=(float)(cosa*srcx2+sina*srcy2);  dsty2=(float)(-sina*srcx2+cosa*srcy2);
	dstx3=(float)(cosa*srcx3+sina*srcy3);  dsty3=(float)(-sina*srcx3+cosa*srcy3);
	dstx4=(float)(cosa*srcx4+sina*srcy4);  dsty4=(float)(-sina*srcx4+cosa*srcy4);
///////////////计算出新的宽度和高度/////////////////////////
	newW=(int)(max(fabs(dstx4-dstx1),fabs(dstx3-dstx2))+0.5);
	newH=(int)(max(fabs(dsty4-dsty1),fabs(dsty3-dsty2))+0.5);
//////////////计算出常用值//////////////////////////////////
	num1 = (float) (-0.5 * (newW - 1) * cosa - 0.5 * (newH - 1) * sina + 0.5 * (width  - 1));
	num2 = (float) ( 0.5 * (newW - 1) * sina - 0.5 * (newH - 1) * cosa + 0.5 * (height - 1));
//////////////////////////////////////////////////////////
	LONG zijie; 
	LONG newzijie;
	zijie = WIDTHBYTES(width * 8);//原来的一行字节数
	newzijie=WIDTHBYTES(newW*8);//新的一行的字节数

	GetDocument()->bmpinfo->bmiHeader.biWidth=newW;
	GetDocument()->bmpinfo->bmiHeader.biHeight=newH;//把新的宽度和高度传给bmpinfo

	data=(BYTE *)new BYTE[newzijie*newH];//给临时变量分配数据

	for(x0=0;x0<newH;x0++)
	{
		for(y0=0;y0<newW;y0++)
		{
			bian = data + newzijie * (newH - 1 - x0) + y0;
			//计算在原来图像的坐标
			x1 = (LONG) (-(y0)*sina+(x0)*cosa+num2+0.5);
			y1 = (LONG) ( (y0)*cosa+(x0)*sina+num1+0.5);
			if( (y1 >= 0) && (y1 < width) && (x1 >= 0) && (x1 < height))//判断坐标是否在原图像内
			{
				yuan = shuju + zijie * (height - 1 - x1) + y1;

				*bian = *yuan;
			}
			else
			{
				// 对于源图中没有的像素,直接赋值为0
				* ((unsigned char*)bian) = 0;
			}
		}
	}
	if(shuju)  delete [] shuju;
	shuju=data;
	Invalidate();	
}

void CXuanzhuan::SetDocument(CMyDoc *m)
{
	poc=m;
}

CMyDoc* CXuanzhuan::GetDocument()
{
	return poc;
}

int CXuanzhuan::GetData()
{
	return m_jiaodu;

}

2018-11-17 22:33:28 qq_34586921 阅读数 9907

前言

上节课学习了实现图像旋转的原理,下课后用matlab实现了一下图像旋转的功能,这里做个记录。


图像旋转原理

图像旋转的本质利用的是向量的旋转。

矩阵乘法的实质是进行线性变换,因此对一个向量进行旋转操作也可以通过矩阵和向量相乘的方式进行。

【ps:线性代数的本质这个视频很直观地解释了各种线性代数运算的实质,链接:https://www.bilibili.com/video/av6731067

因为图像都是通过二维矩阵存放的(单通道),所以对图像进行旋转时要先设定一个像素作为旋转轴,然后其他的像素位置可以看作从旋转轴出发的向量。

如图中间的红点为旋转轴,则旋转的实质就是将图中的各个向量进行旋转,然后将旋转前的位置的像素值赋值给旋转后的位置的像素值。

假设有二维向量v = [x ; y],若要进行逆时针旋转角度a。则旋转矩阵R为

旋转后的向量v2 = R * v。

在正式处理过程中可以这么表示,原像素位置记为p,中心点记为c,旋转后像素位置记为pp

则有(pp - c) = R*(p - c)

pp = R*(p-c) + c


代码实现过程

一共写了三份代码,依次改进了旋转图像的效果。

第一次实现代码

第一次实现代码的思路是正向的思路,也就是把原图进行向量的旋转,找到旋转后的向量的位置,然后将原图的像素值赋值过去即可。

代码实现

% 读入图片
im = imread('1.jpg');

% 求出旋转矩阵
a = 30 / 180 * pi;
R = [cos(a), -sin(a); sin(a), cos(a)];

% 求出图片大小 ch为通道数 h为高度 w为宽度
sz = size(im);
h = sz(1);
w = sz(2);
ch = sz(3);
c = [h; w] / 2;

% 初始化结果图像
im2 = uint8(zeros(h, w, 3));
for k = 1:ch
    for i = 1:h
       for j = 1:w
          p = [i; j];
          % round为四舍五入
          pp = round(R*(p-c)+c);
          if (pp(1) >= 1 && pp(1) <= h && pp(2) >= 1 && pp(2) <= w)
              im2(pp(1), pp(2), k) = im(i, j, k); 
          end
       end
    end
end

% 显示图像
figure;
imshow(im2);

结果显示

原图:     旋转后:

分析:可以看到有这么几个瑕疵,首先是图像的大小和原图一样导致边缘被裁剪了,其次是图像中会出现很多噪声很多杂点,出现杂点的原因是从原图旋转后的像素位置在原图可能找不到,解决方法是用逆向思维,从目标图片反向旋转到原图进行像素查找。

第二次实现代码

这次我先算出了旋转后要显示完整图像所需的画布大小,然后像素赋值的顺序反过来,从目标图片反向旋转到原图进行像素查找。此外还要注意,在改变目标画布大小后,图像中心点即旋转轴改变了,要单独进行计算。

% 读入图片
im = imread('1.jpg');

% 求出旋转矩阵
a = 30 / 180 * pi;
R = [cos(a), -sin(a); sin(a), cos(a)];
R = R'; % 求出旋转矩阵的逆矩阵进行逆向查找

% 计算原图大小
sz = size(im);
h = sz(1);
w = sz(2);
ch = sz(3);
c1 = [h; w] / 2;

% 计算显示完整图像需要的画布大小
hh = floor(w*sin(a)+h*cos(a))+1;
ww = floor(w*cos(a)+h*sin(a))+1;
c2 = [hh; ww] / 2;

% 初始化目标画布
im2 = uint8(ones(hh, ww, 3)*128);
for k = 1:ch
    for i = 1:hh
       for j = 1:ww
          p = [i; j];
          pp = round(R*(p-c2)+c1);
          % 逆向进行像素查找
          if (pp(1) >= 1 && pp(1) <= h && pp(2) >= 1 && pp(2) <= w)
             im2(i, j, k) = im(pp(1), pp(2), k); 
          end
       end
    end
end

% 显示图像
figure;
imshow(im2);

结果显示

原图:     旋转后:

分析:可以看到噪声已经消失,同时显示的也是完整的旋转图像。但是这里的插值方式十分简陋,是直接用四舍五入的,还可以再次改进,通过使用线性插值的方法:https://blog.csdn.net/qq_34586921/article/details/84192850

第三次代码实现

和第二次代码实现比起来就是插值方法改变了,其他的都没有改变,插值方法改变后旋转后的图像质量更好了。

% 读入图片
im = imread('1.jpg');

% 求出旋转矩阵
a = 30 / 180 * pi;
R = [cos(a), -sin(a); sin(a), cos(a)];
R = R'; % 求出旋转矩阵的逆矩阵进行逆向查找

% 计算原图大小
sz = size(im);
h = sz(1);
w = sz(2);
ch = sz(3);
c1 = [h; w] / 2;

% 计算显示完整图像需要的画布大小
hh = floor(w*sin(a)+h*cos(a))+1;
ww = floor(w*cos(a)+h*sin(a))+1;
c2 = [hh; ww] / 2;

% 初始化目标画布
im2 = uint8(ones(hh, ww, 3)*128);
for k = 1:ch
    for i = 1:hh
       for j = 1:ww
          p = [i; j];
          pp = (R*(p-c2)+c1);
          mn = floor(pp);
          ab = pp - mn;
          a = ab(1);
          b = ab(2);
          m = mn(1);
          n = mn(2);
          % 线性插值方法
          if (pp(1) >= 2 && pp(1) <= h-1 && pp(2) >= 2 && pp(2) <= w-1)
             im2(i, j, k) = (1-a)*(1-b)*im(m, n, k) + a*(1-b)*im(m+1, n, k)...
                          + (1-a)*b*im(m, n, k)     + a*b*im(m, n, k);
          end
       end
    end
end

% 显示图像
figure;
imshow(im2);

结果显示:

原图:     旋转后: 


总结

学习不息,继续加油                  

2019-04-26 16:25:59 weixin_44804536 阅读数 98
import numpy as np
import os
import cv2
from math import *


def rotate_images(path, angle):
    image_dir = path
    pathDir = os.listdir(image_dir)
    for s in pathDir:
        newDir = os.path.join(image_dir, s)
        # print(newDir)
        if os.path.isfile(newDir):
            if os.path.splitext(newDir)[1] == ".jpg":
                # lenth = len(newDir)
                # print(lenth)
                filename = int(os.path.basename(newDir)[:-4]) + 270

                """
                imgs = cv2.imread(newDir)
                imgs = skimage.transform.rotate(imgs, angle)
                # now = time.strftime("%Y-%m-%d-%H_%M_%S", time.localtime(time.time()))
                plt.figure("rotated")
                plt.imshow(imgs)
                plt.axis("off")
                plt.savefig("D:/learn/lianxi/python_cnn/save/"+str((i+17)) + ".jpg")
                height, width = img.shape[:2]
                """
                img = cv2.imread(newDir)
                degree = angle
                # 旋转后的尺寸
                height, width = img.shape[:2]
                heightNew = int(width * fabs(sin(radians(degree))) + height * fabs(cos(radians(degree))))
                widthNew = int(height * fabs(sin(radians(degree))) + width * fabs(cos(radians(degree))))

                matRotation = cv2.getRotationMatrix2D((width / 2, height / 2), degree, 1)

                matRotation[0, 2] += (widthNew - width) / 2  # 重点在这步,目前不懂为什么加这步
                matRotation[1, 2] += (heightNew - height) / 2  # 重点在这步

                imgRotation = cv2.warpAffine(img, matRotation, (widthNew, heightNew), borderValue=(255, 255, 255))

                cv2.imwrite("D:/learn/lianxi/python_cnn/save2/"+str(filename) + ".jpg", imgRotation)


def rotate_txt(path, angle):
    image_dir = path
    print(path)
    pathDir = os.listdir(image_dir)
    print(pathDir)
    for s in pathDir:
        newDir = os.path.join(image_dir, s)
        print(newDir)
        if os.path.isfile(newDir):
            if os.path.splitext(newDir)[1] == ".txt":
                file = open(newDir, 'r+')
                filename = int(os.path.basename(newDir)[:-4]) + 270
                # print("+++++++++++++")
                print(filename)
                for line in file.readlines():
                    line = line.strip()  # 去掉每行头尾空白
                    line_split = line.split()
                    # print(line_split)

                    int_line = [float(x) for x in line_split]
                    a = np.array(int_line)
                    print(a)
                    x_center = cos(angle)*(a[1]-0.5)-sin(angle)*(a[2]-0.5)
                    y_center = cos(angle)*(a[2]-0.5)+sin(angle)*(a[1]-0.5)
                    x_w = a[4]
                    y_h = a[3]
                    # new = [line_split[0], x_center, y_center, x_w, y_h]
                    # new_line = [str(x) for x in new]
                    # new_file = open("17_1.txt", "a+")
                    # new_file.writelines(str(new_line)+'\n')
                    new = [line_split[0], x_center+0.5, y_center+0.5, x_w, y_h]
                    new_line = [str(x) for x in new]
                    filename1 = str(filename)
                    new_file = open(filename1+'.txt', "a+")
                    lenth = len(new_line)
                    for i in range(0, lenth):
                        if i == 4:
                            new_file.writelines(str(new_line[i]) + '\n')
                        else:
                            new_file.writelines(str(new_line[i]) + " ")


if __name__ == "__main__":

    # rotate_images("D:/learn/lianxi/python_cnn/imgs/", 180)
    rotate_txt("D:/learn/lianxi/python_cnn/imgs/",  pi * 1)


    # alter(r"D:\learn\lianxi\python_cnn\17.txt", 90)
2017-11-20 17:41:00 weixin_34378922 阅读数 126

对于倾斜的图像,可以通过矩阵转换将其进行矫正。

先计算出图像倾斜矩形的长宽,而后将矫正后的形状的矩形坐标确定,通过getPerspectiveTransform获取矫正前与矫正后坐标的变换矩阵,而后利用warpPerspective将图像进行变换

 1                         float dstSize1 = dist(Final_poly[i][0], Final_poly[i][1]);
 2             float dstSize2 = dist(Final_poly[i][1], Final_poly[i][2]);
 3             float dstSizeW, dstSizeH;
 4             Point2f pts0[4], pts1[4];
 5             pts0[0].x = x1; pts0[0].y = y1;
 6             pts0[1].x = x2; pts0[1].y = y2;
 7             pts0[2].x = x3; pts0[2].y = y3;
 8             pts0[3].x = x4; pts0[3].y = y4;
 9             //cout<<pts0[0].x<<" "<<pts0[0].y<<" "<<pts0[1].x<<" "<<pts0[1].y<<" "<<pts0[2].x<<" "<<pts0[2].y<<" "<<pts0[3].x<<" "<<pts0[3].y<<endl;
10             //cout<<dstSize1<<" "<<dstSize2<<endl;
11             //if(dstSize1 > dstSize2 && fabs(Final_theta[i]) > 45.0)
12             if (fabs(Final_theta[i]) > 45.0)
13             {
14                 dstSizeW = dstSize1;
15                 dstSizeH = dstSize2;
16                 pts1[0].x = dstSizeW; pts1[0].y = dstSizeH;
17                 pts1[1].x = 0; pts1[1].y = dstSizeH;
18                 pts1[2].x = 0; pts1[2].y = 0;
19                 pts1[3].x = dstSizeW; pts1[3].y = 0;
20             }
21             //else if(dstSize1 < dstSize2 && fabs(Final_theta[i]) < 45.0)
22             else if (fabs(Final_theta[i]) <= 45.0)
23             {
24                 dstSizeW = dstSize2;
25                 dstSizeH = dstSize1;
26                 pts1[0].x = 0; pts1[0].y = dstSizeH;
27                 pts1[1].x = 0; pts1[1].y = 0;
28                 pts1[2].x = dstSizeW; pts1[2].y = 0;
29                 pts1[3].x = dstSizeW; pts1[3].y = dstSizeH;
30             }
31             Mat M_perspective = getPerspectiveTransform(pts0, pts1);
32             Mat img_perspective;
33             warpPerspective(image, img_perspective, M_perspective, Size(dstSizeW, dstSizeH), cv::INTER_CUBIC, cv::BORDER_CONSTANT);

 

getPerspectiveTransform函数

warpPerspective函数

没有更多推荐了,返回首页