Sep 5, 2013

Load ảnh từ OpenCV

Chào mừng năm học mới, thắng lợi mới nên mình sẽ viết một loạt series về opencv và một số kiến thức liên quan đến thị giác máy tính. Một phần để phục vụ việc học tập( ngâm cứu) của bản thân, một phần chia sẻ kiến thức với bạn nào có đam mê và muốn thử với lĩnh vực này.

Chủ đề của hôm nay. Mở đầu năm học bằng 1 công việc rất đơn giản: load ảnh dùng opencv.
Ngôn ngữ:C++, sẽ bàn một chút đến C và Python.
Để load 1 ảnh đã tồn tại trong bộ nhớ ngoài, ta dùng API imread.
Tham số của API này:
  • filename (const char*): đường dẫn đến file cần load.
  • flags (int): các tùy chọn khi load ảnh. Một số tham số hay ho:
    • &gt 0: Trả lại ảnh có 3 kênh màu. Kênh $ \alpha $ sẽ bị lược bỏ.
    • = 0: Trả về ảnh mức xám.
    • &lt 0: Trả về ảnh nguyên gốc.
imread sẽ trả lại là một đối tượng kiểu Mat. Nếu không thành công, kết quả trả về sẽ là một ma trận rỗng, tức Mat::data = NULL. Ta có thể dùng kết quả này để check xem file thực sự đã được load hay chưa. Bởi vì imread không có cơ chế exeptions nên khi có lỗi xảy ra, ta phải đợi đến các hàm xử lý tiếp theo (có exceptions hoặc assert) mới biết được. Điều này rất là bất lợi khi debug: ta cứ nghĩ ảnh đã được load vào, nhưng thực sự là chưa.
Sau khi load xong, công việc tiếp theo sẽ là show tấm ảnh đó ra màn hình. OpenCV hỗ trợ tốt tác vụ này với API: imshow. Tham số lần lượt của API này như sau:
  1. windows name (string): Tên của cửa sổ màn hình.
  2. image (Mat): biến lưu trữ ảnh đã được load
Đoạn mã ví dụ:

1
2
3
4
5
6
7
8
9
#include <opencv2\opencv.hpp>
using namespace cv;
int main()
{
    Mat img = imread("hello.png", 1);
    imshow("show image", img);
    waitKey(0);
    return 0;
}

Hàm waitKey có một chức năng nho nhỏ. Nó sẽ đợi người dùng nhấn một phím bất kỳ rồi mới thực hiện công việc tiếp theo. Tham số đầu vào là 0, mặc định là vậy. Lớn hơn 0 là số milisecond mà waitKey sẽ delay.
Tất cả các api vừa kể trên là của C++, OpenCV hỗ trợ cả ngôn ngữ C. Để tránh nhầm lẫn đáng tiếc( vì C/C++ là hai anh em rất là gần gũi nhau), nên các API kể trên đều nằm trong namespace cv. Còn các cấu trúc khác thuộc C sẽ có tiền tố CV đứng trước.
Vậy ứng dụng của đoạn code nho nhỏ này là gì? Một điều hay ho của các API bên OpenCV là nó cross-platform. Rinh qua bên Linux chạy ngon, và để Windows cũng tốt chán. Vì cái sự cross-platform đó, và vì đây là thư viện tập trung vào xử lý ảnh, nên số các API hỗ trợ user interface rất ít. Nhưng, nhưng ít ra, nó cũng đủ cho ta chế 1 tool graphic debug. :D
Ý tưởng:
  • Một cửa sổ Windows cho phép resize, đồng thời là 1 điểm breakpoint kiểm tra xem đoạn code của ta tới đó đã thỏa mãn chưa.
  • (Option): Click vào mỗi pixel sẽ trả về giá trị ở pixel đó.
Hướng giải quyết:
  • Việc tạo 1 của sổ cho phép resize ảnh không khó, việc tạo breakpoint đơn thuần là gọi hàm waitKey().
  • Việc đọc giá trị của pixel phụ thuộc rất lớn vào: số kênh của ảnh (1, 3), và kiểu lưu trữ của mỗi kênh đó (uchar, float, double, int)... Việc click để chọn vị trí pixel khá đơn giản. Tuy nhiên, hiện nay mình vẫn chưa có cách thỏa đáng nào (ngoại trừ việc if...else một đống các trường hợp và gọi từng hàm phù hợp.
Tạo một header file. ví dụ là debugtool.h có nội dung sau:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#ifndef __DEBUGTOOL
#define __DEBUGTOOL

#include <cassert>
#include <opencv2/opencv.hpp>
void __clickevent(int evnt, int x, int y, int flags, void* params);
#ifdef NDEBUG
#define debug_img(img) 
#else
#define debug_img(img) {\
 cv::namedWindow("Debug:"#img, CV_WINDOW_NORMAL | CV_WINDOW_FREERATIO);\
 cv::setMouseCallback("Debug:"#img, __clickevent,(void*) &img);\
 cv::imshow("Debug:"#img, img);\
    cv::waitKey(0);\
 cv::destroyWindow("Debug:"#img);\
 }
#endif

#endif
Sau đó tạo file cpp tên debugtool.cpp có nội dung:
1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#include "debugtool.h"

void __clickevent(int event, int x, int y, int flags, void* params)
{
    if (event != CV_EVENT_LBUTTONUP) return;
    IplImage *iplImg = new IplImage(*(cv::Mat*) params);   
    assert(iplImg->imageData != NULL);
    std::cout << "(" << x << "," << y << "): " 
                << cv::Scalar(cvGet2D(iplImg ,x, y)) << std::endl;
    std::cout.flush();
}

Xong, giờ có thể dùng debug_img bất cứ đâu như 1 breakpoint kiểm tra 1 ảnh. Click vào pixe để xem các giá trị. :)

30 comments:

  1. chào anh. em đang làm bài đếm người đi qua khung hình bằng opencv.
    với thuật toán:
    1. Load video lên
    2. đặt khung hình
    3.so sánh back ground giữa 2 frame liên tiếp,nếu khác nhau thì tăng đếm lên
    thầy chỉ yêu cầu thế thôi ạ.
    nhưng vấn đề em đang thắc mắc là tạo khung hình như thế nào? làm sao biết được người đi qua khung ạ. và em xin hỏi có thể lấy back ground tại 1 điểm không ạ.
    mong anh giúp đỡ em

    ReplyDelete
  2. Chào bạn. Mình xin giải đáp ngắn gọn 2 vấn đề trên:
    - Thư viện OpenCV cho phép bạn capture từng frame trên các thiết bị ghi hình và thực hiện tính toán trên đó. Bạn có thể Google và có kha khá các bài viết liên qua. Mình nhớ là trên trang chủ OpenCV có tutorial này :).
    - (Mình nghĩa) là thầy bạn chỉ nêu khái quát vấn đề thôi. Dĩ nhiên để biết xem chuyển động đó là cái gì (cái xe, người, con vật ...) là một mảng hoàn toàn khác. Bạn có thể "né" bằng cách:
    * Tìm các thuật toán (cơ bản) về detect dáng người.
    * Tạm chấp nhận cái gì nó chuyển động trên khung hình thì là 1 người.
    * Phác thảo 1 hình chữ nhật có kích thước xấp xỉ dáng người. Nếu sự sai khác giữa 2 khung hình khớp với hình chữ nhật mình tạo thì tính đó là 1 người.
    * Heuristic, hoặc nhẩm thử tốc độ di chuyển của 1 người trung bình là nhiêu. Vì giữa 1 cái xe chạy và 1 người đi bộ thì độ sai lệch các khối pixel sẽ khác nhau rất nhiều.

    Hi vọng có thể giúp bạn phần nào đó :D.

    ReplyDelete
  3. Em chào anh! Em đang làm bài nhận diện một số đồ vật trong video. Em muốn load tất cả các ảnh template trong 1 thư mục và lưu vào 1 mảng temp[].
    Nếu sử dụng hàm IplImage để load thì cần phải viết rất nhiều lần. Anh có cách nào giải quyết không ạ? Em cám ơn.

    ReplyDelete
  4. Chào bạn, mình không rõ ý bạn lắm. Theo mình hiểu là bạn có nhiều ảnh trong 1 folder và muốn load tất cả lên và bỏ vào temp[]. Mình nghĩ có sau:
    - Dùng 1 ngôn ngữ script tự động sinh ra một file text chứa tất cả các file ảnh (đường dẫn). Sau đó bạn chỉ việc đọc file text đó lên, đọc từng dòng rồi load ảnh vô. Dùng 1 hàm for là xong :)

    ReplyDelete
  5. Vâng, đúng ý em hỏi rồi :v.
    Khó cái là em không biết về script. Em sẽ thử viết bằng C++.
    Ah, em dùng hàm cvMatchTemp() để nhận diện 1 số đồ vật khi quay camera. Có thể sử dụng thêm hàm nào xử lý ảnh template và hình ảnh từ camera để nhận diện đồ vật chuẩn xác hơn không ạ?
    (Em mới chỉ Smooth hình ảnh từ camera)

    ReplyDelete
  6. Không nhất thiết phải code C++ đâu bạn. Một số ngôn ngữ script như Perl, Python hỗ trợ tốt việc này. Nhưng nếu không biết thì không sao, mình có thể dùng *DOS* thay thế. Đơn cử:

    " dir * /B > tên_file.txt"
    Sẽ tự động liệt kê danh sách tất cả các file trong thư mục hiện hành rồi chép nó vào file tên là tên_file.txt

    Về chuyện hàm xử lý ảnh:
    - Tùy vào bài toán, tùy vào đối tượng ta xử lý mà có nhiều phương pháp khác nhau. Tuy nhiên có phép "histogram equalized" - cân bằng histogram là có thể dùng chung cho các trường hợp. Ngoài ra, đối với một số môi trường và vật thể khác nhau phải dùng các phép khác nhau (heuristic :D ), bạn có thể search keyword "image enhancement" để biết thêm chi tiết. Hầu hết các phép này đều có sẵn hàm hoặc là dễ cài đặt :)

    ReplyDelete
  7. Vâng, em cám ơn anh.
    Hic, hiểu biết của em hạn hẹp, non quá. :(

    ReplyDelete
  8. em chào anh ạ. anh có thể cho em hỏi cách tạo khung hình trong opencv được không ạ?

    ReplyDelete
  9. Khung hình ý bạn là khung hình chứa video hay là khung hình chứa ảnh? Nếu là khung hình chứa ảnh thì là tutorial này. Còn nếu là khung hình chứa video thì mình sẽ viết 1 tutorial khác. Có thể là ngày mai :)

    ReplyDelete
    Replies
    1. Anonymous23/9/13 07:52

      ý em là trên cái frame đọc ra từ video.mình tạo 1 vùng mà khi người đi qua thì mình đếm ạ

      Delete
  10. Chào anh.Vấn đề của em là em không biết tạo cái khung hình ạ. theo như thấy nói thì trên cái ảnh là mình đọc ra từ video mình chỉ đếm người đi qua cái khung ấy thôi ạ,ngoài ra ko đếm ạ

    ReplyDelete
  11. À, vậy thì các bạn search từ khóa "ROI - Rectangle of Interest" nhé :)

    ReplyDelete
  12. Chào Anh. a có thể hướng dẫn em chuyển text ví dụ: Lê Văn Linh thành dạng ảnh được không ạ?

    ReplyDelete
    Replies
    1. http://docs.opencv.org/modules/core/doc/drawing_functions.html
      Dùng hàm putText nhé bạn, tuy nhiên nhớ là tùy chỉnh cái text trước khi vẽ. Trong link đó có cả code demo :D

      Delete
  13. chào anh.
    giả sử em cần tìm tên "Lê Văn Linh" ở dạng (jpg) trong danh sách tên (.jpg)chứa 2 tên Lê Văn Linh
    để tìm được cả 2 tên thì em phải làm ntn ạ? mong anh giúp, cảm ơn anh ạ

    ReplyDelete
  14. Chào anh Khoa ạ.!
    Em đang làm niên luận "trích chọn đặc điểm hình ảnh, đầu vào là một anh, đầu ra là những vecto", em muốn sử dụng thư viện opencv với công cụ xử lý ảnh là qt creator. Em mong anh chỉ giúp em với.! Dù chúng ta ko quen biết nhau, nhưng em vẫn hi vọng vì thời gian chỉ còn 2 tuần nữa thôi, mà công việc của em thì nhiều quá, em sẽ hậu tạ anh ạ.! ...

    ReplyDelete
    Replies
    1. Bài toán bạn đưa ra mơ hồ quá. Thư viện OpenCV là công cụ xử lý ảnh, còn Qt Creator là thư viện giao diện.

      Delete
  15. Dạ, vì em đang học môn "Xử lý ảnh" trường ĐHCT dùng QT creator làm công cụ xử lý như: load ảnh, chuyển ảnh màu -> ảnh xám, thay đổi độ sáng tối cho ảnh, thay đổi độ tương phản của ảnh, vẽ histogram của ảnh màu - ảnh xám, trích lọc đường biên của ảnh, lọc ảnh để cải thiện chất lượng ảnh. Niên luận của em, Thầy yêu cầu viết một phần mềm với mục tiêu input ảnh và output là các vecto đặc trưng. Nên ý tưởng em là dùng thư viện Opencv add vào QT creator, sử dụng hàm trong Opencv để xử lý và QT creator để làm giao diện cho phần mềm. Anh Khoa thấy có được không anh? giúp em với... em đang ở Cần Thơ, điện thoại em là: 096586.6589, facebook: Bùi Viễn Thông. Em mong được anh giúp đỡ và liên lạc.! ...

    ReplyDelete
  16. Chào bạn.
    Hiện giờ công việc của mình khá bận nên mình không hỗ trợ bạn chi tiết được. Tuy nhiên mình có thể giúp một phần nào đó. Việc xử lý ảnh thì không sao, nhưng cái vector đặc trưng rất mập mờ, bạn phải nói cụ thể bạn dùng đặc trưng nào: local feature, global feature?

    ReplyDelete
  17. Da em cung ko biet la loai nao trong hai loai tren nua.! Thay em chi noi trich tu anh ra nhung vecto la duoc, giong nhu the nay anh !
    (khong dan hinh anh duoc) !!!
    Cu the la em include opencv vao qt duoc roi, em dang dung giai thuat sift de trich ra nhung vecto, nhung khong biet viet chuong trinh nhu the nao cho dung, rac roi qua anh oi.!!! ...

    ReplyDelete
  18. Đã.. em cám ơn anh.! em cũng hok biết là đặc trưng nào trong hai đặc trưng trên nữa.! Thay em chỉ nói là iput một anh và output là những vectơ, vậy là được...
    Hiện tại, em đã include opencv vào qt được rồi, em muốn dùng giải thuật sift để trích ra những vectơ, nhưng em chưa viết được code nữa anh.!!! ... Mong anh giúp.!

    ReplyDelete
    Replies
    1. Ngay từ đầu nói là SIFT thì đã dẽ hiểu hơn rồi.
      Code mẫu từ OpenCV lib: https://gist.github.com/dangkhoasdc/7426870 . Code ngắn và đọc dễ hiểu, còn muốn biết nó làm gì thì chịu khó vào trang chủ của OpenCV và search :D.
      Code SIFT và có sử dụng Bag-Of-Visual-Words: http://www.codeproject.com/Articles/619039/Bag-of-Features-Descriptor-on-SIFT-Features-with-O

      Delete
  19. Anh Khoa oi.!!! em cam on anh lam.!!! nhung em dua vao Qt chay bi loi hoai ha.!!! hjchjcxx... anh giup em fix loi voi.!!! thiet em dot' qua ha....

    ReplyDelete
  20. umh.! lam sao em up bai len cho anh duoc bay gio?! ...
    Anh cho em xin email cua anh nhe anh Khoa !

    ReplyDelete
  21. Anh có thể giúp em nhận diện khuôn mặt bằng OpenCV trên Qt creator được không !

    ReplyDelete
    Replies
    1. Chào bạn,
      Mình nghĩ bạn thử cài đặt thuật toán trên console trước thử coi. Sau đó mới cài trên qt. Thuật toán thì có rất là nhiều, có sẵn demo và code nữa.

      Delete
  22. anh khoa ơi!cho em hỏi với.em đang phải dùng visual studio nhúng opencv để làm phần mềm bắt ảnh cho con robot.để làm đồ án anh có tài liệu nào hay về vấn đề này không chỉ e với.còn hàm event_mousecallback nữa em chưa thể làm được.làm sao lấy được 1 ảnh rồi chuyển nó từ hsv-rgb sau đấy click chuột làm nổi được vùng màu mà minh click sau đó tính tâm.anh rảnh chỉ em với!e cảm ơn!

    ReplyDelete
  23. chào anh, tình cờ tìm đc blog của anh, e đang viết chương trình về xử lí ảnh bằng C/C++, ngoài cách dùng openCV thì có thể dùng cách nào khác không?.Hiện tại e tìm hiểu đc cấu trúc của một số định dạng ảnh, và đang gặp khó khăn trong việc biểu diễn ở chế độ màu, tức là vẽ ra màn hình những dữ liệu mình đọc được...Nếu a có biết về vấn đề này, mong a có thể giải đáp giúp em.

    ReplyDelete
  24. Chào anh, Thuật toán của bọn em là thế này; đầu tiên đọc video đầu vào từng frame một, sau đó trên mỗi frame bọn em phát hiện vùng mắt rồi xuất từng frame ra video. Như vậy khi chạy chương trình bọn em sẽ theo dõi được sự di chuyển của mắt. Bây giờ bọn em muốn xuất ra tín hiệu điều khiển (ví dụ như di chuyển mắt sang trái, thì chương trình sẽ thông báo là left, di chuyển mắt sang phải thì chương trình sẽ thông báo là right) thì phải làm thế nào anh? Mong ảnh gợi ý cho em hướng tìm hiểu.

    ReplyDelete
  25. chào bạn, bạn thử thuật toán Optical flow xem :) nó sẽ xác định hướng của véc tơ chuyển động trong video.

    ReplyDelete