Xoay ảnh
Computer Graphics
5
White

Ngoc Dao viết ngày 22/03/2016

Bài này do bạn Phan Anh Vũ chia sẻ, khi còn là sinh viên BKHN, nay bạn sắp lấy vợ.

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.

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>

#define M_PI      3.1415926535897932384626433832795 
#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;


  // 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>

#define M_PI       3.1415926535897932384626433832795 
#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

  float delta = img->width  * sin(ALPHA);

  CvSize size;
  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

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Ngoc Dao

102 bài viết.
284 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
62 8
Làm thế nào để nâng cấp trang web mà không làm gián đoạn dịch vụ? Đây là câu hỏi phỏng vấn các công ty lớn thường hỏi khi bạn xin vào vị trí làm lậ...
Ngoc Dao viết hơn 2 năm trước
62 8
White
40 1
Bài viết này giải thích sự khác khác nhau giữa hai ngành khoa học máy tính (computer science) và kĩ thuật phần mềm (software engineering), hi vọng ...
Ngoc Dao viết hơn 2 năm trước
40 1
White
34 1
Nếu là team leader, giám đốc công ty hay tướng chỉ huy quân đội, vấn đề cơ bản bạn gặp phải là “hướng mọi người đi theo con đường bạn chỉ ra”. Thử...
Ngoc Dao viết hơn 2 năm trước
34 1
Bài viết liên quan
White
11 0
Giới thiệu Mình rất thích học đồ hoạ máy tính nhưng luôn lười vì: Học thư viện đồ hoạ trên native platform tốn thời gian. API khó hiểu khó hình ...
Bùi Hồng Hà viết 3 năm trước
11 0
White
1 0
Có bạn trẻ bảo: nghe các anh lớn bảo muốn giỏi xử lí ảnh phải học bằng MATLAB. Quả vậy, muốn giỏi phải học nhiều, muốn học được nhiều phải học nhan...
Ngoc Dao viết hơn 2 năm trước
1 0
White
49 17
(Ảnh) Video demo: (Link) Tình hình là đợt vừa rồi mình có ngó Kiaplog profile của anh (Link), lướt lướt thấy có chủ đề (Link) có lượng kipalog ...
ngohado viết hơn 1 năm trước
49 17
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
102 bài viết.
284 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!