Article:
Xoay ảnh
3365
phananhvu.myopenid.com 125Updated 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:
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 
basic, forward mapping, giải thuật, reverse mapping
125
over 2 years ago
over 2 years ago
over 2 years ago
over 2 years ago
over 2 years ago