Hiển thị các bài đăng có nhãn OpenCV. Hiển thị tất cả bài đăng
Hiển thị các bài đăng có nhãn OpenCV. Hiển thị tất cả bài đăng

20/4/13

Phát hiện mặt người dựa trên các đặc trưng Haar-like


Phát hiện mặt người dựa trên các đặc trưng Haar-like

Phát hiện mặt người là bài toán cơ bản được xây dựng từ nhiều năm nay, có nhiều phương pháp được đưa ra như sử dụng template matching, neuron network ...Cho tới nay bài toán này hầu như được giải quyết dựa trên phương pháp sử dụng các đặc trưng haar like. Phương pháp này được cho là đơn giản và kết quả phát hiện là tương đối cao, lên tới trên 98%, Các hãng sản xuất máy ảnh như Canon, Samsung ... cũng đã tích hợp nó vào trong các sản phẩm của mình. Trong bài này tôi giới thiệu sơ qua về phương pháp này và chương trình để phát hiện mặt người.
Các đặc trưng Haar-Like(Haar-Like feature) là những hình chữ nhật được phân thành các vùng khác nhau như hình sau :
Các đặc trưng Haar Like
Chi tiết về các đặc trưng Haar Like các bạn có thể tham khảo tạihttp://en.wikipedia.org/wiki/Haar-like_features hoặc trên rất nhiều trang web khác nhau.

Vậy làm sao dựa vào các đặc trưng này để phát hiện ra mặt người? Câu trả lời như sau:
Đầu tiên, ta phải huấn luyện dựa vào các đặc trưng haar like sau đó dựa vào tập huấn luyện này để phát hiện khuôn mặt. Để làm như được vậy, ảnh đầu vào trước hết phải được xám hóa, sau đó cho một hoặc nhiều hình chữ nhật chứa đặc trưng haar-like chạy khắp bức ảnh, những chỗ khác biệt sẽ được lưu trữ lại. Tập hợp hàng nghìn điểm khác biệt của hàng trăm khuôn mặt khác nhau sẽ cho ta một tập dữ liệu dùng cho việc phat hiện mặt người ... Hơi khó hiểu phải không nào :D, các bạn hãy xem hình sau:


Đặc trưng haar-like trên vùng mắt
Trên hình là một đặc trưng haar-like trên vùng mắt, bây giờ nếu có hàng trăm đặc trưng như vậy trên hàng trăm bức ảnh huấn luyện thì sao? ta sẽ được một phân lớp, tập hợp của rất nhiều phân lớp này sẽ cho ta xác định vùng chứa khuôn mặt.
Các bạn có thể xem bài hướng dẫn cách huấn luyện các đặc trưng haar-like cho việc nhận dạng đối tượng để biết thêm thông tin, giả sử ở đây ta đã huấn luyện thành công và có file kết quả huấn luyện *.xml. Ta sẽ tiến hành phát hiện mặt người ngay sau đây:
Trên là những kiến thức cơ bản, trong Opencv người ta đã có bộ huấn luyện cho việc phát hiện mặt người, kết quả này lấy từ hàng nghìn mẫu của những khuân mặt khác nhau và phân loại dựa trên mô hình phân loại cascade.Sau khi cài đặt xong Opencv các bạn hãy vào mục C:\opencv\data\haarcascades để lấy ra filehaarcascade_frontalface_alt.xml file này sẽ được đặt cùng với file chạy exe của chương trình sau:
Chương trình phát hiện mặt người:
Tạo một project cho nó hoạt động được vơi thư viện opencv. Chương trình phát hiện mặt người như sau:

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;

int main() {
    cout << "Face detection written by CVA" << endl;
    cv::Mat src = cv::imread("D:\\faces.jpg");
    cv::CascadeClassifier c;
    c.load("haarcascade_frontalface_alt.xml");
    std::vector<cv::Rect> faces;
    c.detectMultiScale(src, faces, 1.1 , 2, CV_HAAR_SCALE_IMAGE, cv::Size(30, 30));
    for(std::vector<cv::Rect>::const_iterator r = faces.begin(); r < faces.end(); r++)
    {
        cv::Point center;
        center.x = r->x + (int)r->width/2;
        center.y = r->y + (int)r->height/2;
        cv::circle(src, center, 35, CV_RGB(255, 0, 0),2, 8, 0 );
    }
    cvNamedWindow("FaceDetection CVA", 1);
    cv::imshow("FaceDetection CVA", src);
    cvWaitKey(0);
    return 0;
}

Kết quả như sau:
Phát hiện mặt người trong ảnh
Trong chương trình trên có sử dụng giao diện của C++ khác với dao diện C thông thường. Trong hàm detectMultiScale, Tham số cuối cùng cv::Size(width, height) chính là kích thước đầu vào của các đặc trưng Haar-Like, các các ảnh gần các bạn chỉnh tham số này lớn lên, và với những ảnh kích thước bé, khuôn mặt trong ảnh bé các bạn nên chỉnh kích thước này nhỏ lại.
Chương trình với giao diện đầy đủ, cơ sở lý thuyết và xử lý trên video các bạn có thể tham khảo tại đây.

Hiển thị ảnh xử lý bằng OpenCV lên Picturebox của Visual Studio MFC


Hiển thị ảnh xử lý bằng OpenCV lên Picturebox của Visual Studio MFC

Việc sử dụng OpenCV để lập trình cho xử lý ảnh và dùng chung cùng dao diện của MFC không phải là một chuyện đơn giản và thường có nhiều vướng mắc đối với những ai mới bước đầu làm quen. Trong bài viết này tôi sẽ hướng dẫn các newbi làm một bài thực hành đơn giản để gải tỏa các vướng mắc và làm quen dần với thư viện này. Bài thực hành đơn giản là load một ảnh rồi hiển thị ảnh đó lên picture box của MFC, sau đó dùng các hàm của OpenCV chuyển ảnh này thành ảnh xám và hiển thị lên một picture box khác. Giao diện chương trình như sau:

OK, bắt đầu nhé!
Đầu tiên ta khởi tạo một project sử dụng MFC, các bạn vào  File->New->Project. Chọn loại ứng dụng là MFC Application, đặt tên cho Project rồi click OK.
Môt hộp thoại hiện ra, các bạn chọn Next, đến hộp thoại tiếp theo các bạn chọn vào  như hình sau:
Chọn Next một vài lần nữa cho tới khi cửa sổ cuối cùng như sau hiện ra:
 Khi đó trong mục Generate Classes hãy chọn lớp thứ hai (có dạng ...Dlg) để chương trình khởi động tự động lớp Dialog.
 Nhấn Finish để kết thúc, như vậy ta đã có một project bằng MFC dựa trên giao diện Dialog. Để config cho Project có thể chạy được với OpenCV các bạn hãy vào Project Properties hoặc nhấn Alt + F7 để tuy chỉnh. Cách tùy chỉnh cũng giống như đối với Project dạng console mà tôi đã giới thiệu trước đó, các bạn có thểtham khảo thêm ở đây.

Ok, bây giờ ta hãy thiết kế một giao diện như hình đầu tiên. Giao diện gồm một button có nhãn Load Image, một button Convert to Scale Image. 2 Picture Control, và các Group box. Để lôi được các item này ra các bạn hãy vào ToolBox và kéo thả bình thường.

Để thay đổi các thuộc tính của các Item này hãy click chuột phải vào Item cần tùy chỉnh, chọn Properties (ví dụ như thay đổi tên hiển thị thì tìm mục caption mà đổi ...). Riêng đối với 2 Picture Control để hiển thị ảnh được và giúp lập trình đơn giản thì ta cần làm theo các một số việc sau:
- Thứ nhất, mặc định của Picture Control là dạng Frame. Để hiển thị ảnh Bitmap ta cần chỉnh thành Bitmap, để chỉnh sữa, click chuột phải chọn Properties, trong bảng Properties, chọn mục Type, sau đó đổi Frame thành Bitmap.
- Thứ hai, tất cả các Item trong MFC đều được định sẵn một ID. Để tiện lợi cho việc thay lập trình ta sẽ truy xuất các ID này bằng các biến. Như vậy ta cần gắn cho các Picture Control một biến truy cập. Để gắn được biến cho Picture Control, trược hết phải đổi thuộc tính STATIC của chúng. Để đổi thuộc tính này, trong mục Properties của Picture Control, chon ID, chỉnh sữa IDC_STATIC thành bất kì cái gì bạn muốn, thông thường nên chỉnh thành IDC_STATIC1 chẳn hạn. Picture Control thứ hai là IDC_STATIC2. Sau khi đổi được thuộc tính STATIC, hãy click chuột phải vào biểu tượng Picture Control và chọn Add Variable... Một bảng hiện ra, hãy đặt tên lần lượt cho các Picture Control là pic1 và pic2 như sau:


Và bây giờ ta có thể truy xuất tới các Picture Control bày bằng các biến pic1, pic2. Để đặt một ảnh bitmap lên control này ta có thể dùng lệnhpic1.SetBitmap(bitmap);  
Vấn đề lớn nhất ở đây là hàm SetBitmap của MFC chỉ nhận duy nhất tham số đầu vào là một ảnh Bitmap (Trong khi có nhất nhiều định dạng ảnh khác nữa: Jpeg, png, ...) và với định dạng IplImage của OpenCV thì làm thế nào?
Có hai giải pháp được đưa ra (có thể có nhiều nhưng tui mới biết có hai ... :D). Thứ nhất, chuyển đổi định dạng IplImage của OpenCV ra thành định dạng bitmap bằng cách dùng các hàm chuyển đồi. Thứ hai, lưu ảnh xử lý bằng OpenCV thành Bitmap sau đó load ảnh bằng hàm LoadImage của MFC rồi hiển thị ảnh. Tôi sẽ giới thiệu cả hai cách.

♥ Chuyển đổi IplImage của OpenCV sang định dạng Bitmap
Các thông tin về định dạng của một bức ảnh được lưu ở header của ảnh. Do đó để chuyển đổi định dạng của ảnh ta chỉ cần can thiệp vào header của nó. Đoạn code sau sẽ làm việc đó.

inline HBITMAP IplImage2DIB(const IplImage *Image)    // Convert IPl Image to Bitmap
{
  int bpp = Image->nChannels * 8;
        assert(Image->width >= 0 && Image->height >= 0 &&(bpp == 8 || bpp == 24 || bpp == 32));
        CvMat dst;
        void* dst_ptr = 0;
        HBITMAP hbmp = NULL;
        unsigned char buffer[sizeof(BITMAPINFO) + 255*sizeof(RGBQUAD)];
        BITMAPINFO* bmi = (BITMAPINFO*)buffer;
        BITMAPINFOHEADER* bmih = &(bmi->bmiHeader);
        
        ZeroMemory(bmih, sizeof(BITMAPINFOHEADER));
        bmih->biSize = sizeof(BITMAPINFOHEADER);
        bmih->biWidth = Image->width;
        bmih->biHeight = Image->origin ? abs(Image->height) :
        -abs(Image->height);
        bmih->biPlanes = 1;
        bmih->biBitCount = bpp;
        bmih->biCompression = BI_RGB;
        
        if (bpp == 8) {
                RGBQUAD* palette = bmi->bmiColors;
                int i;
                for (i = 0; i < 256; i++) {
                        palette[i].rgbRed = palette[i].rgbGreen = palette[i].rgbBlue
                                = (BYTE)i;
                        palette[i].rgbReserved = 0;
                }
        }
        
        hbmp = CreateDIBSection(NULL, bmi, DIB_RGB_COLORS, &dst_ptr, 0, 0);
        cvInitMatHeader(&dst, Image->height, Image->width, CV_8UC3,
                dst_ptr, (Image->width * Image->nChannels + 3) & -4);
        cvConvertImage(Image, &dst, Image->origin ? CV_CVTIMG_FLIP : 0);
        
        return hbmp;      
}


♥ Bằng cách lưu ảnh lại dưới dạng bitmap, sau đó load và hiển thị ảnh:
Sử dụng hàm của OpenCV để lưu ảnh: cvSaveImage(ten_anh.bmp, du_lieu_anh); sau đó dùng hàm LoadImage(); của MFC để load ảnh bitmap và hiển thị lên Picture box.

Quay lại Project, hãy copy đoạn code trên pass vào file ten_projectDlg.cpp ngay dưới phần include các header (Nhớ là include opencv.hpp vào đã nhé.). sau đó hãy ta xử lý sự kiện click chuột ở các button Load Image và Convert to Scale Image như sau:
Double click vào button Load Image và thêm vào đoạn code như hình sau:

 Ở button Convert to Gray Image ta viết đoạn code sau:
Chú ý là các ảnh src, gray, resize và file_name có thể được khai báo trong header ten_projectDlg.h
Để kích thước của ảnh vừa khít với khung của dao diện thiết kế, ta có thể chỉnh tham số của ảnh resize trong hàm cvCreateImage(). (trong bài là 400x300 pixel). Một số trường hợp báo lỗi do khai báo file_name là kiểu CString còn tham sốcvLoadImage lại là const char*. Để khắc phục lỗi này hãy tắt chế độ unicode của chương trình đi bằng cách vào Project Properties (Alt + F7) -> General. Ở trang bên của General, mục Character Set chỉnh Use Unicode Character Set thành Use Multi-Byte Character Set.
Sau đây là kết quả chạy chương trình:


PS: Một số khái niệm nhỏ hoặc các bước đơn giản trung gian khác nếu các bạn chưa rõ có thể tìm hiểu thêm, trong bài có đôi lúc gọi Picture Control là Picture Box ...

Toàn bộ project các bạn có thể download tại đây

                                                                      Chúc các bạn học tập tốt!

Hướng dẫn sử dụng thư viện OpenCV Eclipse


Hướng dẫn sử dụng thư viện OpenCV

OpenCV là một thư viện mã nguỗn mở rất mạnh hỗ trợ cho việc xử lý ảnh cũng như các vấn đề liên quan đến thị giác máy tính (Computer Vision).
Tron bài này tôi sẽ hướng dẫn cách sử dụng thư viện OpenCV cho việc lập trình trên ngôn ngữ C/C++ trong môi trường Visual Studio và Eclipse.
Trước hết các bạn download thư viện OpenCV về. Hiện có nhiều phiên OpenCV khác nhau, các bạn nên download bản mới nhất về dùng, Ở đây tôi giả sử đang dùng bản 2.3.1. Tiếp theo các bạn cài đặt thư viện lên, việc cài đặt ở đây thực chất là giải nén thư viện OpenCV vào một thư mục nào đó, các bạn cài đặt như bình thường, ở đây tôi giả sử là cài vào thư mục C:\. Sau khi cài đặt ta sẽ có thư mục C:\opencv và bên trong có rất nhiều thư mục con.
Sử dụng OpenCV với Eclipse CDT và trình dich MinGW GCC.
Từ cửa sổ Eclipse chọn New -> C++ Project , hộp thoại C++ Project xuất hiện, trong hộp thoại ta chọn Project name là opencv, Project type là Hello World C++ Project, Toolchains là MinGW GCC.



Chọn Finish và ta có một Project mới. OK, bây giờ configure cho project này hoạt động được với OpenCV
Trong cửa sổ của Eclipse chọn Project ->Properties, cửa sổ Properties hiện ra. Tron cửa sổ Properties chọn C/C++ Build->Settings. Trong tab Tool Settings. Ở phần GCC C++ Compiller chọn Include rồi dẫn đường dẫn tới mục Include của OpenCV là C:\opencv\build\include. Trong phần MinGW C++ Linker chọn Library và chọn các mục như sau: click vào dấu cộng ở Library search path (-L) và dẫn tới thư mục lib: C:\opencv\build\x86\mingw\lib đối với Windows 32 bit hoặcC:\opencv\build\x64\mingw\lib đối với Windows 64 bit. Tiếp đó click vào dấu "cộng" để thêm Library(-I) vào, các library cần thêm lần lượt là: opencv_core231, opencv_highgui231, opencv_imgproc231 ... nói chung là tùy vào nhu cầu sử dụng có thể thêm một hoặc nhiều lib vào.


Nhấn OK để hoàn thành, như vậy là configure xong, bây giờ là viết một chương trình đơn giản là load và hiển thị một ảnh từ máy tính.
Trong file opencv.cpp thêm vào header : #include <opencv2/opencv.hpp> . Việc thêm header này vào sẽ tự động thêm tất cả các thư viện của opencv vào trong project. Tuy nhiên các bạn cũng có thể thêm thủ công vào project của mình bằng cách thêm các thư viện #include <opencv2/core/core.hpp>#include <opencv2/imgproc/imgproc.hpp> ...
Toàn bộ chương trình đơn giản như sau : 

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;

int main() {
    cout << "The first program in opencv" << endl;
    IplImage *img = cvLoadImage("D:\\Test\\test.jpg", CV_LOAD_IMAGE_COLOR);
    cvNamedWindow("Test", CV_WINDOW_NORMAL);
    cvShowImage("Test", img);
    cvReleaseImage(&img);
    return 0;
}


Chương trình đã hoàn tất, tuy nhiên lúc này để chạy được ta cần copy các file DLL trong thư mục C:\opencv\build\x86\mingw\bin vào thư mục chứa file chạy (opencv.exe) hoặc vào trong thư mục C:\WINDOWS\system32. 
Giờ thị build và chạy chương trình, ta sẽ thấy một cửa sổ ảnh Test hiện ra. Ok, xong phần với eclipse. 

Sử dụng OpenCV với Visual Studio

Phiên bản Visual studio sử dụng ở đây là phiên bản Visual studio 2010, các version trước các bạn có thể cấu hình một cách tương tự.
Tạo một project mới: New > Project, trong cửa sổ New Project chọn Visual C++, Win32 console application. Đặt tên project là opencv


Nhấn OK, Next, Finish và ta có một project mới. Bây giờ vào Project > Properties hoặc nhấn Alt + F7 để mở hộp thoại property. Hộp thoại opencv property page hiện ra, Trong mục Visual C++ Directories, chọn Include Directories, dẫn tới đường dẫn chứa thư mục include của opencv, ở đây là C:\opencv\build\include

Tương tự như vậy, trong mục Lybrary Directories chọn đường dẫn tớiC:\opencv\build\x86\vc9\lib
Trong mục Liker > Input > Additional Dependence ta thêm vào các lib:opencv_core231.lib,
opencv_imgproc231.lib, opencv_highgui231.lib
 ... Tùy vào mục đích chương trình mà ta có thể thêm nhiều hoặc tất cả các lib vào.

Để máy tính có thể chạy được chương trình ta cần phải chỉnh phần PATHenvironment variable, tuy nhiên để đơn giản tôi thường copy hêt các file *.dll trong thư mục C:\opencv\build\x86\vc9\bin vào thư mục C:\Windows\System32 hoặc vào thư mục chứa file chạy (opencv.exe) trong project để có thể chạy được trên máy khác khi copy thư mục bin của project từ máy mình sang máy khác.
Cài đặt xong, bây giở viết chương trình đơn giản load ảnh và hiển thị ảnh:

#include "stdafx.h"
#include <opencv2/opencv.hpp>

int _tmain(int argc, _TCHAR* argv[])
{
    IplImage *src = cvLoadImage("D:\\Temp_work\\opencvgiude2.jpg", CV_LOAD_IMAGE_COLOR);
    cvNamedWindow("a", 1);
    cvShowImage("a", src);
    cvWaitKey(10);
    return 0;
}
OK, giờ thì build và chạy chương trình, ...

17/12/12

Motion Analysis and Object Tracking


Acc

void cvAcc(const CvArr* image, CvArr* sum, const CvArr* mask=NULL)
Adds a frame to an accumulator.
Parameters:
  • image – Input image, 1- or 3-channel, 8-bit or 32-bit floating point. (each channel of multi-channel image is processed independently)
  • sum – Accumulator with the same number of channels as input image, 32-bit or 64-bit floating-point
  • mask – Optional operation mask
The function adds the whole image image or its selected region to the accumulator sum:
\texttt{sum}(x,y) \leftarrow \texttt{sum}(x,y) + \texttt{image}(x,y) \quad \text {if} \quad \texttt{mask}(x,y) \ne 0

CalcGlobalOrientation

double cvCalcGlobalOrientation(const CvArr* orientation, const CvArr* mask, const CvArr* mhi, double timestamp, double duration)
Calculates the global motion orientation of some selected region.
Parameters:
  • orientation – Motion gradient orientation image; calculated by the function CalcMotionGradient
  • mask – Mask image. It may be a conjunction of a valid gradient mask, obtained with CalcMotionGradient and the mask of the region, whose direction needs to be calculated
  • mhi – Motion history image
  • timestamp – Current time in milliseconds or other units, it is better to store time passed to UpdateMotionHistory before and reuse it here, because running UpdateMotionHistory and CalcMotionGradient on large images may take some time
  • duration – Maximal duration of motion track in milliseconds, the same as UpdateMotionHistory
The function calculates the general motion direction in the selected region and returns the angle between 0 degrees and 360 degrees . At first the function builds the orientation histogram and finds the basic orientation as a coordinate of the histogram maximum. After that the function calculates the shift relative to the basic orientation as a weighted sum of all of the orientation vectors: the more recent the motion, the greater the weight. The resultant angle is a circular sum of the basic orientation and the shift.

CalcMotionGradient

void cvCalcMotionGradient(const CvArr* mhi, CvArr* mask, CvArr* orientation, double delta1, double delta2, int apertureSize=3)
Calculates the gradient orientation of a motion history image.
Parameters:
  • mhi – Motion history image
  • mask – Mask image; marks pixels where the motion gradient data is correct; output parameter
  • orientation – Motion gradient orientation image; contains angles from 0 to 360 degrees
  • delta1 – See below
  • delta2 – See below
  • apertureSize – Aperture size of derivative operators used by the function: CV_SCHARR, 1, 3, 5 or 7 (see Sobel)
The function calculates the derivatives $Dx$ and $Dy$ of mhi and then calculates gradient orientation as:
\texttt{orientation}(x,y)=\arctan {\frac{Dy(x,y)}{Dx(x,y)}}
where both $Dx(x,y)$ and $Dy(x,y)$ signs are taken into account (as in the CartToPolar function). After that mask is filled to indicate where the orientation is valid (see the delta1 and delta2 description). The function finds the minimum ($m(x,y)$) and maximum ($M(x,y)$) mhi values over each pixel $(x,y)$ neighborhood and assumes the gradient is valid only if
\min (\texttt{delta1} , \texttt{delta2} ) \le M(x,y)-m(x,y) \le \max (\texttt{delta1} ,\texttt{delta2} ).

CalcOpticalFlowBM

void cvCalcOpticalFlowBM(const CvArr* prev, const CvArr* curr, CvSize blockSize, CvSize shiftSize, CvSize max_range, int usePrevious, CvArr* velx, CvArr* vely)
Calculates the optical flow for two images by using the block matching method.
param prev:
First image, 8-bit, single-channel
param curr:
Second image, 8-bit, single-channel
param blockSize:
Size of basic blocks that are compared
param shiftSize:
Block coordinate increments
param max_range:
Size of the scanned neighborhood in pixels around the block
param usePrevious:
Uses the previous (input) velocity field
param velx:
Horizontal component of the optical flow of
\left\lfloor \frac{\texttt{prev->width} - \texttt{blockSize.width}}{\texttt{shiftSize.width}} \right\rfloor \times \left\lfloor \frac{\texttt{prev->height} - \texttt{blockSize.height}}{\texttt{shiftSize.height}} \right\rfloor
size, 32-bit floating-point, single-channel
param vely:Vertical component of the optical flow of the same size velx, 32-bit floating-point, single-channel
The function calculates the optical flow for overlapped blocks $\texttt{blockSize.width} \times \texttt{blockSize.height}$ pixels each, thus the velocity fields are smaller than the original images. For every block in prev the functions tries to find a similar block in curr in some neighborhood of the original block or shifted by (velx(x0,y0),vely(x0,y0)) block as has been calculated by previous function call (if usePrevious=1)

CalcOpticalFlowHS

void cvCalcOpticalFlowHS(const CvArr* prev, const CvArr* curr, int usePrevious, CvArr* velx, CvArr* vely, double lambda, CvTermCriteria criteria)
Calculates the optical flow for two images.
Parameters:
  • prev – First image, 8-bit, single-channel
  • curr – Second image, 8-bit, single-channel
  • usePrevious – Uses the previous (input) velocity field
  • velx – Horizontal component of the optical flow of the same size as input images, 32-bit floating-point, single-channel
  • vely – Vertical component of the optical flow of the same size as input images, 32-bit floating-point, single-channel
  • lambda – Lagrangian multiplier
  • criteria – Criteria of termination of velocity computing
The function computes the flow for every pixel of the first input image using the Horn and Schunck algorithm .

CalcOpticalFlowLK

void cvCalcOpticalFlowLK(const CvArr* prev, const CvArr* curr, CvSize winSize, CvArr* velx, CvArr* vely)
Calculates the optical flow for two images.
Parameters:
  • prev – First image, 8-bit, single-channel
  • curr – Second image, 8-bit, single-channel
  • winSize – Size of the averaging window used for grouping pixels
  • velx – Horizontal component of the optical flow of the same size as input images, 32-bit floating-point, single-channel
  • vely – Vertical component of the optical flow of the same size as input images, 32-bit floating-point, single-channel
The function computes the flow for every pixel of the first input image using the Lucas and Kanade algorithm .

CalcOpticalFlowPyrLK

void cvCalcOpticalFlowPyrLK(const CvArr* prev, const CvArr* curr, CvArr* prevPyr, CvArr* currPyr, const CvPoint2D32f* prevFeatures, CvPoint2D32f* currFeatures, int count, CvSize winSize, int level, char* status, float* track_error, CvTermCriteria criteria, int flags)
Calculates the optical flow for a sparse feature set using the iterative Lucas-Kanade method with pyramids.
Parameters:
  • prev – First frame, at time t
  • curr – Second frame, at time t + dt
  • prevPyr – Buffer for the pyramid for the first frame. If the pointer is not NULL , the buffer must have a sufficient size to store the pyramid from level 1 to level level ; the total size of (image_width+8)*image_height/3 bytes is sufficient
  • currPyr – Similar to prevPyr, used for the second frame
  • prevFeatures – Array of points for which the flow needs to be found
  • currFeatures – Array of 2D points containing the calculated new positions of the input features in the second image
  • count – Number of feature points
  • winSize – Size of the search window of each pyramid level
  • level – Maximal pyramid level number. If 0 , pyramids are not used (single level), if 1 , two levels are used, etc
  • status – Array. Every element of the array is set to 1 if the flow for the corresponding feature has been found, 0 otherwise
  • track_error – Array of double numbers containing the difference between patches around the original and moved points. Optional parameter; can be NULL
  • criteria – Specifies when the iteration process of finding the flow for each point on each pyramid level should be stopped
  • flags – Miscellaneous flags:
    • CV_LKFLOWPyr_A_READY - pyramid for the first frame is precalculated before the call
    • CV_LKFLOWPyr_B_READY - pyramid for the second frame is precalculated before the call
    • CV_LKFLOW_INITIAL_GUESSES - array B contains initial coordinates of features before the function call
The function implements the sparse iterative version of the Lucas-Kanade optical flow in pyramids . It calculates the coordinates of the feature points on the current video frame given their coordinates on the previous frame. The function finds the coordinates with sub-pixel accuracy. Both parameters prevPyr and currPyr comply with the following rules: if the image pointer is 0, the function allocates the buffer internally, calculates the pyramid, and releases the buffer after processing. Otherwise, the function calculates the pyramid and stores it in the buffer unless the flag CV_LKFLOWPyr_A[B]_READY is set. The image should be large enough to fit the Gaussian pyramid data. After the function call both pyramids are calculated and the readiness flag for the corresponding image can be set in the next call (i.e., typically, for all the image pairs except the very first one CV_LKFLOWPyr_A_READY is set).

CamShift

int cvCamShift(const CvArr* prob_image, CvRect window, CvTermCriteria criteria, CvConnectedComp* comp, CvBox2D* box=NULL)
Finds the object center, size, and orientation.
Parameters:
  • prob_image – Back projection of object histogram (see CalcBackProject)
  • window – Initial search window
  • criteria – Criteria applied to determine when the window search should be finished
  • comp – Resultant structure that contains the converged search window coordinates (comp->rect field) and the sum of all of the pixels inside the window (comp->area field)
  • box – Circumscribed box for the object.
The function implements the CAMSHIFT object tracking algrorithm . First, it finds an object center using MeanShift and, after that, calculates the object size and orientation. The function returns number of iterations made within MeanShift. The CamShiftTracker class declared in cv.hpp implements the color object tracker that uses the function.

CvKalman

Kalman filter state.
typedef struct CvKalman
{
    int MP;                     /* number of measurement vector dimensions */
    int DP;                     /* number of state vector dimensions */
    int CP;                     /* number of control vector dimensions */

    /* backward compatibility fields */
#if 1
    float* PosterState;         /* =state_pre->data.fl */
    float* PriorState;          /* =state_post->data.fl */
    float* DynamMatr;           /* =transition_matrix->data.fl */
    float* MeasurementMatr;     /* =measurement_matrix->data.fl */
    float* MNCovariance;        /* =measurement_noise_cov->data.fl */
    float* PNCovariance;        /* =process_noise_cov->data.fl */
    float* KalmGainMatr;        /* =gain->data.fl */
    float* PriorErrorCovariance;/* =error_cov_pre->data.fl */
    float* PosterErrorCovariance;/* =error_cov_post->data.fl */
    float* Temp1;               /* temp1->data.fl */
    float* Temp2;               /* temp2->data.fl */
#endif

    CvMat* state_pre;           /* predicted state (x'(k)):
                                    x(k)=A*x(k-1)+B*u(k) */
    CvMat* state_post;          /* corrected state (x(k)):
                                    x(k)=x'(k)+K(k)*(z(k)-H*x'(k)) */
    CvMat* transition_matrix;   /* state transition matrix (A) */
    CvMat* control_matrix;      /* control matrix (B)
                                   (it is not used if there is no control)*/
    CvMat* measurement_matrix;  /* measurement matrix (H) */
    CvMat* process_noise_cov;   /* process noise covariance matrix (Q) */
    CvMat* measurement_noise_cov; /* measurement noise covariance matrix (R) */
    CvMat* error_cov_pre;       /* priori error estimate covariance matrix (P'(k)):
                                    P'(k)=A*P(k-1)*At + Q*/
    CvMat* gain;                /* Kalman gain matrix (K(k)):
                                    K(k)=P'(k)*Ht*inv(H*P'(k)*Ht+R)*/
    CvMat* error_cov_post;      /* posteriori error estimate covariance matrix (P(k)):
                                    P(k)=(I-K(k)*H)*P'(k) */
    CvMat* temp1;               /* temporary matrices */
    CvMat* temp2;
    CvMat* temp3;
    CvMat* temp4;
    CvMat* temp5;
}
CvKalman;
The structure CvKalman is used to keep the Kalman filter state. It is created by the CreateKalman function, updated by the KalmanPredict and KalmanCorrect functions and released by the ReleaseKalman function. Normally, the structure is used for the standard Kalman filter (notation and the formulas below are borrowed from the excellent Kalman tutorial )
\begin{array}{l} x_ k=A \cdot x_{k-1}+B \cdot u_ k+w_ k\\ z_ k=H \cdot x_ k+v_ k \end{array}
where:
\begin{array}{l l} x_ k\; (x_{k-1})& \text {state of the system at the moment \emph{k} (\emph{k-1})}\\ z_ k & \text {measurement of the system state at the moment \emph{k}}\\ u_ k & \text {external control applied at the moment \emph{k}} \end{array}
$w_ k$ and $v_ k$ are normally-distributed process and measurement noise, respectively:
\begin{array}{l} p(w) \sim N(0,Q)\\ p(v) \sim N(0,R) \end{array}
that is,
$Q$ process noise covariance matrix, constant or variable,
$R$ measurement noise covariance matrix, constant or variable
In the case of the standard Kalman filter, all of the matrices: A, B, H, Q and R are initialized once after the CvKalman structure is allocated via CreateKalman. However, the same structure and the same functions may be used to simulate the extended Kalman filter by linearizing the extended Kalman filter equation in the current system state neighborhood, in this case A, B, H (and, probably, Q and R) should be updated on every step.

CreateKalman

CvKalman* cvCreateKalman(int dynam_params, int measure_params, int control_params=0)
Allocates the Kalman filter structure.
Parameters:
  • dynam_params – dimensionality of the state vector
  • measure_params – dimensionality of the measurement vector
  • control_params – dimensionality of the control vector
The function allocates CvKalman and all its matrices and initializes them somehow.

KalmanCorrect

const CvMat* cvKalmanCorrect(CvKalman* kalman, const CvMat* measurement)
Adjusts the model state.
Parameters:
  • kalman – Pointer to the structure to be updated
  • measurement – CvMat containing the measurement vector
    #define cvKalmanUpdateByMeasurement cvKalmanCorrect
    
The function adjusts the stochastic model state on the basis of the given measurement of the model state:
\begin{array}{l} K_ k=P'_ k \cdot H^ T \cdot (H \cdot P'_ k \cdot H^ T+R)^{-1}\\ x_ k=x'_ k+K_ k \cdot (z_ k-H \cdot x'_ k)\\ P_ k=(I-K_ k \cdot H) \cdot P'_ k \end{array}
where
$z_ k$ given measurement (mesurement parameter)
$K_ k$ Kalman “gain” matrix.
The function stores the adjusted state at kalman->state_post and returns it on output. Example. Using Kalman filter to track a rotating point
#include "cv.h"
#include "highgui.h"
#include <math.h>

int main(int argc, char** argv)
{
    /* A matrix data */
    const float A[] = { 1, 1, 0, 1 };

    IplImage* img = cvCreateImage( cvSize(500,500), 8, 3 );
    CvKalman* kalman = cvCreateKalman( 2, 1, 0 );
    /* state is (phi, delta_phi) - angle and angle increment */
    CvMat* state = cvCreateMat( 2, 1, CV_32FC1 );
    CvMat* process_noise = cvCreateMat( 2, 1, CV_32FC1 );
    /* only phi (angle) is measured */
    CvMat* measurement = cvCreateMat( 1, 1, CV_32FC1 );
    CvRandState rng;
    int code = -1;

    cvRandInit( &rng, 0, 1, -1, CV_RAND_UNI );

    cvZero( measurement );
    cvNamedWindow( "Kalman", 1 );

    for(;;)
    {
        cvRandSetRange( &rng, 0, 0.1, 0 );
        rng.disttype = CV_RAND_NORMAL;

        cvRand( &rng, state );

        memcpy( kalman->transition_matrix->data.fl, A, sizeof(A));
        cvSetIdentity( kalman->measurement_matrix, cvRealScalar(1) );
        cvSetIdentity( kalman->process_noise_cov, cvRealScalar(1e-5) );
        cvSetIdentity( kalman->measurement_noise_cov, cvRealScalar(1e-1) );
        cvSetIdentity( kalman->error_cov_post, cvRealScalar(1));
        /* choose random initial state */
        cvRand( &rng, kalman->state_post );

        rng.disttype = CV_RAND_NORMAL;

        for(;;)
        {
            #define calc_point(angle)                                      \
                cvPoint( cvRound(img->width/2 + img->width/3*cos(angle)),  \
                         cvRound(img->height/2 - img->width/3*sin(angle)))

            float state_angle = state->data.fl[0];
            CvPoint state_pt = calc_point(state_angle);

            /* predict point position */
            const CvMat* prediction = cvKalmanPredict( kalman, 0 );
            float predict_angle = prediction->data.fl[0];
            CvPoint predict_pt = calc_point(predict_angle);
            float measurement_angle;
            CvPoint measurement_pt;

            cvRandSetRange( &rng,
                            0,
                            sqrt(kalman->measurement_noise_cov->data.fl[0]),
                            0 );
            cvRand( &rng, measurement );

            /* generate measurement */
            cvMatMulAdd( kalman->measurement_matrix, state, measurement, measurement );

            measurement_angle = measurement->data.fl[0];
            measurement_pt = calc_point(measurement_angle);

            /* plot points */
            #define draw_cross( center, color, d )                        \
                cvLine( img, cvPoint( center.x - d, center.y - d ),       \
                             cvPoint( center.x + d, center.y + d ),       \
                             color, 1, 0 );                               \
                cvLine( img, cvPoint( center.x + d, center.y - d ),       \
                             cvPoint( center.x - d, center.y + d ),       \
                             color, 1, 0 )

            cvZero( img );
            draw_cross( state_pt, CV_RGB(255,255,255), 3 );
            draw_cross( measurement_pt, CV_RGB(255,0,0), 3 );
            draw_cross( predict_pt, CV_RGB(0,255,0), 3 );
            cvLine( img, state_pt, predict_pt, CV_RGB(255,255,0), 3, 0 );

            /* adjust Kalman filter state */
            cvKalmanCorrect( kalman, measurement );

            cvRandSetRange( &rng,
                            0,
                            sqrt(kalman->process_noise_cov->data.fl[0]),
                            0 );
            cvRand( &rng, process_noise );
            cvMatMulAdd( kalman->transition_matrix,
                         state,
                         process_noise,
                         state );

            cvShowImage( "Kalman", img );
            code = cvWaitKey( 100 );

            if( code > 0 ) /* break current simulation by pressing a key */
                break;
        }
        if( code == 27 ) /* exit by ESCAPE */
            break;
    }

    return 0;
}

KalmanPredict

const CvMat* cvKalmanPredict(CvKalman* kalman, const CvMat* control=NULL)
Estimates the subsequent model state.
#define cvKalmanUpdateByTime cvKalmanPredict
Parameters:
  • kalman – Kalman filter state
  • control – Control vector $u_ k$, should be NULL iff there is no external control (control_params =0)
The function estimates the subsequent stochastic model state by its current state and stores it at kalman->state_pre:
\begin{array}{l} x'_ k=A \cdot x_{k-1}+B \cdot u_ k\\ P'_ k=A \cdot P_{k-1}+A^ T + Q \end{array}
where
$x’_ k$ is predicted state kalman->state_pre,
$x_{k-1}$ is corrected state on the previous step kalman->state_post (should be initialized somehow in the beginning, zero vector by default),
$u_ k$ is external control (control parameter),
$P’_ k$ is priori error covariance matrix kalman->error_cov_pre
$P_{k-1}$ is posteriori error covariance matrix on the previous step kalman->error_cov_post (should be initialized somehow in the beginning, identity matrix by default),
The function returns the estimated state.

MeanShift

int cvMeanShift(const CvArr* prob_image, CvRect window, CvTermCriteria criteria, CvConnectedComp* comp)
Finds the object center on back projection.
Parameters:
  • prob_image – Back projection of the object histogram (see CalcBackProject)
  • window – Initial search window
  • criteria – Criteria applied to determine when the window search should be finished
  • comp – Resultant structure that contains the converged search window coordinates (comp->rect field) and the sum of all of the pixels inside the window (comp->area field)
The function iterates to find the object center given its back projection and initial position of search window. The iterations are made until the search window center moves by less than the given value and/or until the function has done the maximum number of iterations. The function returns the number of iterations made.

MultiplyAcc

void cvMultiplyAcc(const CvArr* image1, const CvArr* image2, CvArr* acc, const CvArr* mask=NULL)
Adds the product of two input images to the accumulator.
Parameters:
  • image1 – First input image, 1- or 3-channel, 8-bit or 32-bit floating point (each channel of multi-channel image is processed independently)
  • image2 – Second input image, the same format as the first one
  • acc – Accumulator with the same number of channels as input images, 32-bit or 64-bit floating-point
  • mask – Optional operation mask
The function adds the product of 2 images or their selected regions to the accumulator acc:
\texttt{acc}(x,y) \leftarrow \texttt{acc}(x,y) + \texttt{image1}(x,y) \cdot \texttt{image2}(x,y) \quad \text {if} \quad \texttt{mask}(x,y) \ne 0

RunningAvg

void cvRunningAvg(const CvArr* image, CvArr* acc, double alpha, const CvArr* mask=NULL)
Updates the running average.
Parameters:
  • image – Input image, 1- or 3-channel, 8-bit or 32-bit floating point (each channel of multi-channel image is processed independently)
  • acc – Accumulator with the same number of channels as input image, 32-bit or 64-bit floating-point
  • alpha – Weight of input image
  • mask – Optional operation mask
The function calculates the weighted sum of the input image image and the accumulator acc so that acc becomes a running average of frame sequence:
\texttt{acc}(x,y) \leftarrow (1-\alpha ) \cdot \texttt{acc}(x,y) + \alpha \cdot \texttt{image}(x,y) \quad \text {if} \quad \texttt{mask}(x,y) \ne 0
where $\alpha $ regulates the update speed (how fast the accumulator forgets about previous frames).

SegmentMotion

CvSeq* cvSegmentMotion(const CvArr* mhi, CvArr* seg_mask, CvMemStorage* storage, double timestamp, double seg_thresh)
Segments a whole motion into separate moving parts.
Parameters:
  • mhi – Motion history image
  • seg_mask – Image where the mask found should be stored, single-channel, 32-bit floating-point
  • storage – Memory storage that will contain a sequence of motion connected components
  • timestamp – Current time in milliseconds or other units
  • seg_thresh – Segmentation threshold; recommended to be equal to the interval between motion history “steps” or greater
The function finds all of the motion segments and marks them in seg_mask with individual values (1,2,...). It also returns a sequence of CvConnectedComp structures, one for each motion component. After that the motion direction for every component can be calculated with CalcGlobalOrientation using the extracted mask of the particular component Cmp.

SnakeImage

void cvSnakeImage(const IplImage* image, CvPoint* points, int length, float* alpha, float* beta, float* gamma, int coeff_usage, CvSize win, CvTermCriteria criteria, int calc_gradient=1)
Changes the contour position to minimize its energy.
Parameters:
  • image – The source image or external energy field
  • points – Contour points (snake)
  • length – Number of points in the contour
  • alpha – Weight[s] of continuity energy, single float or array of length floats, one for each contour point
  • beta – Weight[s] of curvature energy, similar to alpha
  • gamma – Weight[s] of image energy, similar to alpha
  • coeff_usage – Different uses of the previous three parameters:
    • CV_VALUE - indicates that each of alpha, beta, gamma is a pointer to a single value to be used for all points;
    • CV_ARRAY - indicates that each of alpha, beta, gamma is a pointer to an array of coefficients different for all the points of the snake. All the arrays must have the size equal to the contour size.
  • win – Size of neighborhood of every point used to search the minimum, both win.width and win.height must be odd
  • criteria – Termination criteria
  • calc_gradient – Gradient flag; if not 0, the function calculates the gradient magnitude for every image pixel and consideres it as the energy field, otherwise the input image itself is considered
The function updates the snake in order to minimize its total energy that is a sum of internal energy that depends on the contour shape (the smoother contour is, the smaller internal energy is) and external energy that depends on the energy field and reaches minimum at the local energy extremums that correspond to the image edges in the case of using an image gradient. The parameter criteria.epsilon is used to define the minimal number of points that must be moved during any iteration to keep the iteration process running. If at some iteration the number of moved points is less than criteria.epsilon or the function performed criteria.max_iter iterations, the function terminates.

SquareAcc

void cvSquareAcc(const CvArr* image, CvArr* sqsum, const CvArr* mask=NULL)
Adds the square of the source image to the accumulator.
Parameters:
  • image – Input image, 1- or 3-channel, 8-bit or 32-bit floating point (each channel of multi-channel image is processed independently)
  • sqsum – Accumulator with the same number of channels as input image, 32-bit or 64-bit floating-point
  • mask – Optional operation mask
The function adds the input image image or its selected region, raised to power 2, to the accumulator sqsum:
\texttt{sqsum}(x,y) \leftarrow \texttt{sqsum}(x,y) + \texttt{image}(x,y)^2 \quad \text {if} \quad \texttt{mask}(x,y) \ne 0

UpdateMotionHistory

void cvUpdateMotionHistory(const CvArr* silhouette, CvArr* mhi, double timestamp, double duration)
Updates the motion history image by a moving silhouette.
Parameters:
  • silhouette – Silhouette mask that has non-zero pixels where the motion occurs
  • mhi – Motion history image, that is updated by the function (single-channel, 32-bit floating-point)
  • timestamp – Current time in milliseconds or other units
  • duration – Maximal duration of the motion track in the same units as timestamp
The function updates the motion history image as following:
\texttt{mhi}(x,y)=\forkthree {\texttt{timestamp}}{if $\texttt{silhouette}(x,y) \ne 0$} {0}{if $\texttt{silhouette}(x,y) = 0$ and $\texttt{mhi} < (\texttt{timestamp} - \texttt{duration})$} {\texttt{mhi}(x,y)}{otherwise}
That is, MHI pixels where motion occurs are set to the current timestamp, while the pixels where motion happened far ago are cleared.

Bài đăng phổ biến