Vinova tuyển lập trình viên Mobile & Web ở Hà Nội, lương $300-1000

Article: Xoay ảnh 3365

basic, forward mapping, giải thuật, reverse mapping
phananhvu.myopenid.com 125
Updated over 2 years ago

Bài này xin trình bày cách xoay ảnh như hình minh họa dưới.

 

 

Thuật toán sử dụng là quay tất cả các điểm quanh điểm trên cùng bên trái của ảnh một góc alpha theo chiều dương qui ước. Có hai cách để thực hiện việc xoay này là ánh xạ xuôi (forward mapping) và ánh xạ ngược (reverse mapping). Forward mapping sẽ làm cho ảnh có lỗ. Với reverse mapping, ảnh mượt hơn nhưng các mép vẫn không được mềm lắm.

Các thuật toán được minh họa bằng các chương trình C++ có sử dụng thư viện OpenCV.

Forward mapping

Trong forward mapping, từ mỗi điểm của ảnh gốc, xác định một điểm của ảnh kết quả. Do phải làm tròn số khi thực hiện phép xoay nên ở ảnh gốc, có những pixel không được "tô màu" làm cho trên ảnh xuất hiện những "lỗ" li ti.

Hình học giải tích cấp 3 và đại số tuyến tính đại học có trình bày về cách xoay điểm M(x, y) quanh gốc tọa độ góc a, thành N(x', y') như sau:

Forward mapping:

|x'|   |cos(a)  -sin(a)| |x|
| | = | | | |
|y'| |sin(a) cos(a)| |y|

Có thể tham khảo các hình thành công thức ở đây.

Hình 0: Minh hoạ thuật toán xoay điểm.


Chương trình sau đây duyệt lần lượt từng điểm của bức ảnh và xoay các điểm đó quanh điểm trên cùng bên trái. Các lệnh thực hiện việc xoay được in đậm. Chương trình sử dụng một trackbar để tiện theo dõi sự thay đổi chất lượng ảnh theo góc xoay.


// OpenCV_Xoay_anh.cpp : Defines the entry point for the console application.
// Use forward mapping
#include <stdafx.h>

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

#include <math.h>
#include <stdio.h>

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#define FILE_NAME "Image.jpg"

IplImage *img, *res;

void trackbarHandler(int pos);

int main()
{
// Load source picture
img = cvLoadImage(FILE_NAME);

// Create new image
CvSize size;
size.height = img->height *2;
size.width = img->width * 2;
res = cvCreateImage(size, img->depth, img->nChannels);


// Render
cvNamedWindow("Image:", 1);
cvShowImage("Image:", img);

cvNamedWindow("Result image:", 1);


int alpha = 15; // 0 - 90 [%]
cvCreateTrackbar("Alpha", "Image:", α, 90, trackbarHandler);
trackbarHandler(alpha);

// Loop and wait
cvWaitKey();

// Release
cvDestroyWindow("Image:");
cvReleaseImage(&img);
cvDestroyWindow("Result image:");
cvReleaseImage(&res);

return 0;
}

void trackbarHandler(int alpha) {
uchar * resData = (uchar *)res->imageData;
float ALPHA = alpha*M_PI/180;
uchar * imgData = (uchar *)img->imageData;

// Rotate
for (int i = 0; i < img->height; i++)
for (int j = 0; j < img->width; j++)
for (int k = 0; k < img->nChannels; k++) { resData[(int)(i*cos(ALPHA) + j*sin(ALPHA)
+ 50)*res->widthStep
+ (int)(j*cos(ALPHA) - i*sin(ALPHA)
+ 100)*res->nChannels + k] =
imgData[i*img->widthStep + j*img->nChannels + k]; }

cvShowImage("Result image:", res);
}

Reverse mapping

Với reverse mapping, từ mỗi điểm của ảnh kết quả, suy ngược lại từ ảnh gốc để lấy ra giá trị màu cần thiết. Với cách này, tất cả các điểm trên ảnh kết quả đều được gán giá trị màu của một điểm tương ứng (hay ít nhất cũng là điểm lân cận của điểm đó) ở ảnh gốc nên không có hiện tượng "lỗ" như ở trên.

Ta tìm ma trận xoay ảnh trong trường hợp này như sau:

Ta có:
|x'| |cos(a) -sin(a)| |x|
| | = | | | |
|y'| |sin(a) cos(a)| |y|
Nên:
|x| |cos(a) -sin(a)|-1 |x'|
| | = | | | |
|y| |sin(a) cos(a)| |y'|
Do đó:
|x| |cos(a) sin(a)| |x'|
| | = | | | |
|y| |-sin(a) cos(a)| |y'|

Sau đây là chương trình mẫu:

// OpenCV_Xoay_anh.cpp : Defines the entry point for the console application.
// Use reverse mapping
#include <stdafx.h>

#include <cv.h>
#include <cxcore.h>
#include <highgui.h>

#include <math.h>
#include <stdio.h>

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif
#define FILE_NAME "Image.jpg"
#define ALPHA 30*M_PI/180

int main()
{
// Load source picture
IplImage *img = cvLoadImage(FILE_NAME);
uchar * imgData = (uchar *)img->imageData;

// Create new image
CvSize size;
float delta;
delta = img->width * sin(ALPHA);
size.height = img->height + delta;
size.width = img->width * 2;
IplImage* res = cvCreateImage(size, img->depth, img->nChannels);
uchar * resData = (uchar *)res->imageData;

// Rotate
for (int i = -120; i < img->width*2; i++)
for (int j = 0; j < img->height*2; j++)
for (int k = 0; k < img->nChannels; k++) { int iN = (int)(i*cos(ALPHA) + j*sin(ALPHA));
int jN = (int)(j*cos(ALPHA) - i*sin(ALPHA));
if ((iN > 0) && (jN > 0)
&& (iN < img->width) && (jN < img->height))
resData[(j)*res->widthStep
+ (i+120)*res->nChannels + k] =
imgData[jN*img->widthStep
+ iN*img->nChannels + k];
}


// Render
cvNamedWindow("Image:", 1);
cvShowImage("Image:", img);

cvNamedWindow("Result image:", 1);
cvShowImage("Result image:", res);

// Loop and wait
cvWaitKey();

// Release
cvDestroyWindow("Image:");
cvReleaseImage(&img);
cvDestroyWindow("Result image:");
cvReleaseImage(&res);

return 0;
}

So sánh

Sau đây là kết quả của hai thuật toán trên. Dễ dàng nhận thấy là với reverse mapping, ảnh mịn hơn. Tuy nhiên, trong quá trình tính toán, vẫn phải làm tròn nên các mép có hiện tượng răng cưa.

Hình 1: Foward Mapping

 

Hình 2: Reverse mapping

 

1 2 

Editors
ngocdaothanh.myopenid.com 172
phananhvu.myopenid.com 125

Comments

phananhvu.myopenid.com 125
over 2 years ago

Bạn thử thay số -120 thành số khác xem nó thế nào rồi suy ra xem.

trungtuyet.myopenid.com
over 2 years ago

phananhvu.myopenid.com  biết chỉ mình đi, mình ko biết thật lòng đó,mình thay một vài số, có số chạy đc, có số bị cắt 1 phần ảnh, ko tìm ra quy luật mới lên đây nhờ mọi người giúp đó. ai biết chỉ giùm mình đi.

phananhvu.myopenid.com 125
over 2 years ago

Chương trình ngắn thế thì cố suy nghĩ xem đi. Còn không tìm ra thì đành trả lời thầy là nếu thay -120 bằng số nhỏ hơn thì được, còn lớn hơn thì hình kết quả bị cắt.

trungtuyet.myopenid.com
over 2 years ago

phananhvu.myopenid.com biet thi chi minh di, lam gi kho de nhau vay?minh ko ranh ve cai nay lam, cot chu yeu doi pho voi thay thoi, neu biet thi chi giup.Thanks

trungtuyet.myopenid.com
over 2 years ago

<!-- /* Style Definitions */ p.MsoNormal, li.MsoNormal, div.MsoNormal {mso-style-parent:""; margin:0in; margin-bottom:.0001pt; mso-pagination:widow-orphan; font-size:12.0pt; font-family:"Times New Roman"; mso-fareast-font-family:"Times New Roman";} @page Section1 {size:8.5in 11.0in; margin:1.0in 1.25in 1.0in 1.25in; mso-header-margin:.5in; mso-footer-margin:.5in; mso-paper-source:0;} div.Section1 {page:Section1;} --> Với Forward mapping thì:

int alpha = 15;  // 0 - 90 [%]

nếu câu lệnh trên với alpha >=25 thì bị lỗi sau:

loi: Unhandled exception at 0x0041178a in XoayAnh1.exe: 0xC0000005: Access violation writing location 0x00bb90a0.

Vì sao vậy bạn?

You must login to be able to comment

Uploaded files

No file uploaded yet

You must login to be able to upload

Nhà tài trợ:

Mọi người đều tự do viết bài, sửa bài của người khác, và bình luận ở trang web này. Bạn muốn chủ động tạo bài mới để chia sẻ kinh nghiệm với mọi người? Xin click link ở dưới.

Create new content