chapter 3 - picprojects

advertisement
CHAPTER 3
Getting to know OpenCV
OpenCV Primitive Data Types
OpenCV có vài data type cơ sở. Những data type này không cơ sở từ góc nhìn của C, nhưng
chúng tất cả là các structure đơn giản, và ta sẽ xem chúng như nguyên tố. Bạn có thể xem xét
chi tiết các structure mô tả như sau (cũng như các structure khác) trong cxtypes.h header file, mà
ở .../OpenCV/cxcore/include directory của cài cặt OpenCV.
Đơn giản nhất của những types này là CvPoint. CvPoint là structure đơn giản với hai integer
members, x và y. CvPoint có hai anh em: CvPoint2D32f và CvPoint3D32f. Cái đầu có cùng hai
members x và y, mà cả hai là các số floating-point. Cái sau cũng chứa phần tử thứ ba, z.
CvSize giống anh họ của CvPoint. Các phần tử của nó là width và height, mà cả hai là integer. Nếu
bạn muốn các số floating-point, dùng anh học CvSize2D32f cousin của CvSize. CvRect một con
khác của CvPoint và CvSize; nó chứa bốn member: x, y, width, và height. (Trong trường hợp bạn lo
lắng, con cháu này được chấp nhận.)
Cuối cùng nhưng không kém là CvScalar, mà là một tập bốn số double-precision. Khi memory
không là vấn đề, CvScalar thường được dùng để biểu diễn một, hai, hay ba số thực (trong những
trường hợp này, các thành phần không cần đơn giản được bỏ qua). CvScalar có một member val,
mà là một pointer đến một array chứa bốn số double-precision floating-point.
Tất cả những data type này có các constructor method có nên như cvSize() (nhìn chung*
constructor có cùng tên như structure type nhưng với character đầu tiên không hoa). Nhớ rằng
đây là C và không phải C++, do đó những “constructors” này chỉ là các inline function mà nhận
một list các argument và return structure mong muốn với các value được đặt thích hợp.
Các inline constructor cho các data type kê trong Table 3-1—cvPointXXX(), cvSize(), cvRect(), và
cvScalar()—là cực kỳ hữu ích vì chúng làm code không chỉ dễ hơn để viết mà còn dễ đọc hơn.
Giả sử bạn muốn vễ một rectangle trắng giữa (5, 10) và (20, 30); bạn có thể đơn giản gọi:
cvRectangle(
myImg,
cvPoint(5,10),
cvPoint(20,30),
cvScalar(255,255,255)
);
Table 3-1. Structures for points, size, rectangles, và scalar tuples
Structure
chứa
Represents
CvPoint
int x, y
Point in image
CvPoint2D32f
float x, y
Points in ℜ2
CvPoint3D32f
float x, y, z
Points in ℜ3
CvSize
int width, height
Size of image
CvRect
int x, y, width, height
Portion of image
CvScalar
double val[4]
RGBA value
cvScalar() là trường hợp đặc biệt: nó có ba constructor. Đầu tiên, gọi là cvScalar(), lấy một, hai, ba,
hay bốn argument và gán những argument này cho các phần tử tương ứng của val[]. Constructor
thứ hai là cvRealScalar(); nó lấy một argument, mà nó gán vào val[0] trong khi setting các entry
khác thành 0. Variant cuối cùng là cvScalarAll(), mà lấy một argument đơn nhưng đặt tất cả bốn
phần tử của val[] thành cùng argument đó.
Matrix và Image Types
Hình 3-1 cho thấy thứ bậc class hay structure của ba image types. Khi dùng OpenCV, bạn sẽ
chạm trán lặp lại IplImage data type. Bạn hiện đã thấy nó nhiều lần trong chương trước. IplImage
là structure cơ sở được dùng để encode cái ta nhìn chung gọi là “các image”. Những image này
có thể là grayscale, color, four-channel (RGB+alpha), và mỗi channel có thể chứa bất kỳ trong
vài type của các số integer hay floatingpoint. Do đó, kiểu này là chung hơn image three-channel
8-bit RGB có khắp nơi mà tức thời đến trong đầu.*
OpenCV cung cấp lượng lớn các operator hữu ích mà hoạt động trên những images này, gồm
các tool để resize images, trích các channel riêng, tìm thấy các value lớn nhất hay nhỏ nhất của
một channel cụ thể, cộng hai images, threshold một image, và hơn nữa. In chương này ta sẽ
xem xét những kiểu này của các operator cẩn thận.
Hình 3-1. Ngay cả qua OpenCV được thực hiện trong C, các structure được dùng trong OpenCV có một thiết kế hướng
đối tượng; về hiệu quả, IplImage được dẫn từ CvMat, mà được dẫn từ CvArr
Trước khi ta có thể thảo luận các image chi tiết, ta cần quan sát một data type khác: CvMat,
OpenCV matrix structure. Dù OpenCV được thực hiện toàn bộ trong C, quan hệ giữa CvMat và
IplImage là hơi giống với thứ bậc trong C++. Cho tất cả các nghĩa và các mục đích, IplImage có thể
được nghĩ như được dẫn từ CvMat. Do đó, điều tốt nhất để hiểu là lớp cơ sở (nên là) trước khi cố
hiểu các phức tạp thêm của lớp dẫn xuất. Lớp thứ ba, gọi là CvArr, có thể được nghĩ như một
abstract base class mà từ đó CvMat tự nó được dẫn. Bạn sẽ thường thấy CvArr (hay, chính xác
hơn, CvArr*) trong các function prototypes. Khi nó xuất hiện, nó chấp nhận chuyển CvMat* hay
IplImage* cho routine.
CvMat Matrix Structure
Có hai điều bạn cần biết trước khi ta chìm vào matrix business. Đầu tiên, không có “vector”
construct trong OpenCV. Bất cứ khi nào ta muốn một vector, ta chỉ dùng một matrix với một cột
(hay một hàng, nếu ta muoons một hoán vị hay kết hợp vector).
Thứ hai, khái niệm của một matrix trong OpenCV là có phần trừu tượng hơn khái niệm bạn học
trong lớp đại số tuyền tính. Về cụ thể, các phần tử củmột matrix tự chúng không là các số đơn
giản. Ví dụ, routine mà mà tạo một two-dimensional matrix mới có prototype sau:
cvMat* cvCreateMat ( int rows, int cols, int type );
Ở đây type có thể là bất kỳ của một list dài các predefined type theo dạng: CV_<bit_depth>(S|U|F)
C<number_of_channels>. Do đó, matrix có thể gồm các 32-bit floats (CV_32FC1), của các unsigned
integer 8-bit triplets (CV_8UC3), hay vô số các element khác. Một element của CvMat không cần
thiết là một số đơn. Có thể biểu diễn nhiều giá trị cho một entry đơn trong matrix cho phép ta làm
nhiều thứ như biểu diễn nhiều channel màu trong một RGB image. Cho một image đơn giản
chứa các kênh red, green và blue, hầu hết các image operator sẽ được áp dụng cho mỗi channel
riêng (trừ phi ngược lại được lưu ý). Về bên trong, structure của CvMat tương đối đơn giản, như
được thấy trong Example 3-1 (bạn có thể thấy điều này bởi mở …/opencv/cxcore/include/cxtypes.h).
Các matrix có một width, height, type, step (chiều dài của một row theo các byte, không ints hay
floats), và một pointer đến một data array (và một vài thứ mà ta chưa thể nói). Bạn có thể truy cập
những members này trực tiếp bởi referencing lại một pointer đến CvMat hay, cho một vài element
phổ biến hơn, bởi dùng các accessor function được cung cấp. Ví dụ, để lấy size của một matrix,
bạn có thể lấy thông tin bạn muốn một trong bởi gọi cvGetSize(CvMat*), mà trả về một CvSize
structure, hay bởi truy cập height và width độc lập với các constructs như matrix->height và matrix>width.
Example 3-1. CvMat structure: the matrix “header”
typedef struct CvMat {
int type;
int step;
int* refcount; // cho dùng chỉ bên trong
union {
uchar* ptr;
short* s;
int* i;
float* fl;
double* db;
} data;
union {
int rows;
int height;
};
union {
int cols;
int width;
};
} CvMat;
Thông tin này nhìn chung được refer đến như matrix header. Nhiều routines phân biệt giữa
header và data, cái sau là memory mà data element trỏ đến.
Các matrix có thể được tạo theo một trong vài cách. Các hầu hết phổ biến là dùng cvCreateMat(),
mà is thực sự thuận tiện để sự kết hợp của nhiều atomic function cvCreateMatHeader() và
cvCreateData(). cvCreateMatHeader() tạo CvMat structure không cấp memory cho data, trong khi
cvCreateData() handles cấp phát data. Đôi khi chỉ cvCreateMatHeader() được đòi hỏi, một trong vì
bạn đã cấp data cho vài nguyên nhân khác hay vì bạn chưa thực sự cần cấp nó. Method thứ ba
để dùng cvCloneMat(CvMat*), mà tạo matrix mới từ một cái hiện có.* Khi matrix không còn cần
nữa, nó có thể được giải phóng bởi gọi cvReleaseMat(CvMat**).
List trong Example 3-2 tóm tắt các function ta vừa mô tả cũng những cái khác mà liên quan mật
thiết.
Example 3-2. Matrix creation và release
// tạo một matrix rows by cols của kiểu ‘type’.
//
CvMat* cvCreateMat( int rows, int cols, int type );
// tạo only matrix header without allocating data
//
CvMat* cvCreateMatHeader( int rows, int cols, int type );
// Khởi tạo header on existing CvMat structure
//
CvMat* cvInitMatHeader(
CvMat* mat,
int rows,
int cols,
int type,
void* data = NULL,
int step = CV_AUTOSTEP
);
// Like cvInitMatHeader() but allocates CvMat as well.
//
CvMat cvMat(
int rows,
int cols,
int type,
void* data = NULL
);
// Allocate a new matrix just like the matrix ‘mat’.
//
CvMat* cvCloneMat( const cvMat* mat );
// Free the matrix ‘mat’, both header và data.
//
void cvReleaseMat( CvMat** mat );
Tương tự với nhiều OpenCV structures, có một constructor gọi là cvMat() mà tạo một CvMat
structure. Routine này không thực sự cấp phát memory; nó chỉ tạo header (điều này tương tự với
cvInitMatHeader()). Những method này là cách tốt để lấy vài data bạn đã có nằm dưới, đóng gói
nó bởi trỏ matrix header đến nó như trong Example 3-3, và chạy nó qua các routine mà xử lý các
OpenCV matrice.
Example 3-3. Tạo một OpenCV matrix với data cố định
// tạo an OpenCV Matrix containing some fixed data.
//
float vals[] = { 0.866025, -0.500000, 0.500000, 0.866025 };
CvMat rotmat;
cvInitMatHeader(
&rotmat,
2,
2,
CV_32FC1,
vals
);
Một khi ta có một matrix, có nhiều thứ ta có thể làm với nó. Các tác vụ đơn giản nhất là query các
khía cạnh của định nghĩa array và truy cập data. Để query matrix, ta có cvGetElemType( const
CvArr* arr ), cvGetDims( const CvArr* arr, int* sizes=NULL ), và cvGetDimSize( const CvArr* arr, int index ).
Cái đầu tiên trả về một integer constant biểu diễn kiểu các phần tử được lưu trong array (điều
này sẽ bằng với cái giống CV_8UC1, CV_64FC4, …). Cái thứ hai lấy array và một pointer tùy chọn
đến một integer; nó trả về số các chiều (hai cho trường hợps ta đang quan tâm, nhưng về sau ta
sẽ trạm chán các N-dimensional matrix như các object). Nếu integer pointer là khác null thì nó sẽ
lưu height và width(hay N dimensions) của array được cung cấp.
Function cuối lấy một integer nhận biết chiều của cái quan tâm và đơn giản trả về extent của
matrix ở trong chiều đó.*
Accessing Data in Your Matrix
Có ba cách để truy cập data trong matrix: cách dễ, cách khó, và cách đúng.
The easy way
Cách dễ nhất để lấy một phần tử thành phần của một array là bằng CV_MAT_ELEM() macro.
Macro này (xem Example 3-4) lấy matrix, type của phần tử được nhận, và số row và column và
sau đó trả về phần tử.
Example 3-4. Trruy cập một matrix bằng CV_MAT_ELEM() macro
CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
float element_3_2 = CV_MAT_ELEM( *mat, float, 3, 2 );
“Under the hood” macro này chỉ gọi macro CV_MAT_ELEM_PTR(). CV_MAT_ELEM_PTR() (xem
Example 3-5) lấy các argument matrix và row và column của element được quan tâm và trả về
(không ngạc nhiên) một pointer đến phần tử được chỉ định. Một khác biệt quan trọng giữa
CV_MAT_ELEM() và CV_MAT_ELEM_PTR() đó là CV_MAT_ELEM() thực sự ép pointer thành kiểu chỉ
định trước khi de-referencing nó. Nếu bạn thích đặt giá trị hơn chỉ đọc nó, bạn có thể gọi
CV_MAT_ELEM_PTR() trực tiếp; trong trường hợp này, tuy nhiên, bạn phải cast pointer trả về
thành kiểu thích hợp.
Example 3-5. Đặt một giá trị đơn trong matrix dùng CV_MAT_ELEM_PTR() macro
CvMat* mat = cvCreateMat( 5, 5, CV_32FC1 );
float element_3_2 = 7.7;
*( (float*)CV_MAT_ELEM_PTR( *mat, 3, 2 ) ) = element_3_2;
Không may, những cái này macros tính lại pointer cần trong mỗi lời gọi. Điều này có nghĩa giám
sát pointer thành phần tử cơ sở của vùng data của matrix, tính một offset để lấy address của
thông tin bạn quan tâm, và sau đó thêm offset đó vào cơ sở được tính. Do đó, mặc dù những
macro này là dễ dùng, chúng có thể không là cách tốt nhất để truy cập một matrix. Điều này là
đặc biệt đúng khi bạn có kế hoạch truy cập tất cả các element trong một matrix tuần tự. Ta sẽ
đến ngay với cách tốt nhất để hoàn thành nhiệm vụ quan trọng này.
The hard way
Hai macros được thảo luận trong “The easy way” chỉ phù hợp để truy cập các mảng một hay hai
chiều (nhớ lại các mảng một chiều, hay “các vector”, thực sự chỉ là n-by-1 các matrix). OpenCV
cung cấp các cơ chế để làm việc với các mảng đa chiều. Thật ra OpenCV cho phép cho một Ndimensional matrix nói chung mà có thể có nhiều chiều như bạn thích.
Để truy cập data một matrix tổng quát, ta dùng họ các function cvPtr*D và cvGet*D… kê trong
Examples 3-6 và 3-7. cvPtr*D family chứa cvPtr1D(), cvPtr2D(), cvPtr3D(), và cvPtrND() . . . . Mỗi cái
trong ba cái đầu tiên lấy một tham số pointer matrix CvArr* theo sau bởi số thích hợp của các
integer cho các index, và một argument tùy chọn nhận diện kiểu của tham số output. Các routine
return một pointer đến element quan tâm. Bằng cvPtrND(), argument thứ hai là một pointer đến
một array các integer chứa số thích hợp của các index. Ta sẽ return function này sau. (Trong các
prototype mà theo sau, bạn sẽ cũng llwu ý các argument tùy chọn thích hợp; ta sẽ address
những cái này khi ta cần chúng.)
Example 3-6. Pointer truy cập vào các matrix structures
uchar* cvPtr1D(
const CvArr* arr,
int idx0,
int* type = NULL
);
uchar* cvPtr2D(
const CvArr* arr,
int idx0,
int idx1,
int* type = NULL
);
uchar* cvPtr3D(
const CvArr* arr,
int idx0,
int idx1,
int idx2,
int* type = NULL
);
uchar* cvPtrND(
const CvArr* arr,
int* idx,
int* type = NULL,
int create_node = 1,
unsigned* precalc_hashval = NULL
);
Để đọc hoàn toàn data, có một dòng khác của các function cvGet*D, kê trong Example 3-7, mà
tương tự với những cais trong Example 3-6 nhưng return giá trị thực của matrix element.
Example 3-7. CvMat và IplImage element functions
double cvGetReal1D( const CvArr* arr, int idx0 );
double cvGetReal2D( const CvArr* arr, int idx0, int idx1 );
double cvGetReal3D( const CvArr* arr, int idx0, int idx1, int idx2 );
double cvGetRealND( const CvArr* arr, int* idx );
CvScalar cvGet1D( const CvArr* arr, int idx0 );
CvScalar cvGet2D( const CvArr* arr, int idx0, int idx1 );
CvScalar cvGet3D( const CvArr* arr, int idx0, int idx1, int idx2 );
CvScalar cvGetND( const CvArr* arr, int* idx );
Return type của cvGet*D là double cho bốn của các routine và CvScalar cho bốn cái khác. Điều này
có nghĩa rằng có thể có vài lãng phí có ý nghĩa khi dùng những function này. Chúng nên được
dùng chỉ nơi thuận lợi và hiệu quả; ngược lại, tốt hơn là dùng cvPtr*D.
Một nguyên nhân tốt hơn để dùng cvPtr*D() là bạn có thể dùng những pointer functions này để có
lợi truy cập đến một điểm cụ thể trong matrix và sau đó dùng đại số pointer để di chuyển quanh
matrix từ đó. Điều quan trọng phải nhớ rằng các channel là liên tiếp trong multichannel matrix. Ví
dụ, trong matrix hai chiều ba channel biểu diễn các byte red, green, blue (RGB), matrix data
được lưu: rgbrgbrgb . . . . Do đó, để di chuyển một pointer của kiểu thích hợp đến channel tiếp
theo, ta cộng 1. Nếu ta muốn đi đến “pixel” tiếp theo hay tập các element, ta cộng và offset bằng
với số các channel (trong trường hợp này là 3).
Mẹo khác để biết phần tử bước trong matrix array (xem Examples 3-1 và 3-3) là chiều dài theo
bytes của một row trong matrix. Trong structure đó, các cột hay một mình độ rộng không đủ để di
chuyển giữa các hàng matrix vì, để hiện quả máy, matrix hay image allocation được làm thành
biên bốn byte gần nhất. Do đó một matrix của ba byte rộng sẽ được cấp thành bốn byte với cái
cuối được bỏ qua. Cho nguyên nhân này, nếu ta lấy một byte pointer đến data element thì ta
thêm bước vào pointer để chuyển nó đến row tiếp theo trực tiếp bên dưới điểm của tapoint. Nếu
ta có một matrix các số nguyên hay floating-point và tương ứng int hay float pointers cho một data
element, ta sẽ bước đến hàng tiếp theo bởi thêm step/4; cho các double, ta cộng step/8 (điều này
chỉ để lấy một lượng mà C sẽ tự động nhân các offset ta cộng bởi byte size của data type). Một ít
tương tự với cvGet*D là cvSet*D trong Example 3-8, mà đặt một matrix hay image element bằng
một lời gọi, và các functions cvSetReal*D() và cvSet*D(), mà có thể được dùng để set các value của
các elements của một matrix hay image.
Example 3-8. Set element functions for CvMat hay IplImage.
void cvSetReal1D( CvArr* arr, int idx0, double value );
void cvSetReal2D( CvArr* arr, int idx0, int idx1, double value );
void cvSetReal3D(
CvArr* arr,
int idx0,
int idx1,
int idx2,
double value
);
void cvSetRealND( CvArr* arr, int* idx, double value );
void cvSet1D( CvArr* arr, int idx0, CvScalar value );
void cvSet2D( CvArr* arr, int idx0, int idx1, CvScalar value );
void cvSet3D(
CvArr* arr,
int idx0,
int idx1,
int idx2,
CvScalar value
);
void cvSetND( CvArr* arr, int* idx, CvScalar value );
Như một thuận lợi đi kèm, ta cũng có cvmSet() và cvmGet(), mà được dùng khi làm việc với các
matrix một channel floating-point. Chúng là rất đơn giản:
double cvmGet( const CvMat* mat, int row, int col )
void cvmSet( CvMat* mat, int row, int col, double value )
Do đó lời gọi đến function tiện lợi cvmSet(),
cvmSet( mat, 2, 2, 0.5000 );
là giống với lời gọi đến cvSetReal2D function tương đương,
cvSetReal2D( mat, 2, 2, 0.5000 );
The right way
Với tất cả các accessor function này, bạn có thể nghĩ rằng không gì thêm để nói. Thật ra, bạn sẽ
hiếm khi dùng bất kỳ trong các hàm set và get. Hầu hết thời gian, vision là hoạt động đòi hỏi
processor, và bạn sẽ muốn làm các thứ trong hầu hết cách hiệu quả có thể. Không cần thiết để
nói, đi qua những interface functions này là không hiệu quả. Thay vào đó, bạn nên làm đại số
pointer riêng và đơn giản de-reference cách của bạn vào matrix. Quản lý các pointers tự bạn là
đặc biệt quan trọng khi bạn muốn làm gì đó với mọi element trong một array (giả thiết không có
OpenCV routine mà có thể thực hiện nhiệm vụ này cho bạn).
Để truy cập trực tiếp vào phần bên trong của một matrix, tất cả bạn thực sự cần biết là data được
lưu tuần tự theo thứ tự quét thô, trong đó các cột (“x”) là biến chạy nhanh nhất. Các channel
được chen vào, mà có nghĩa rằng, trong trường hợp của một multichannel matrix, chúng là thứ
tự chạy nhanh nhất. Example 3-9 cho thấy một ví dụ cách điều này có thể được làm.
Example 3-9. Cộng tất cả các elements trong một three-channel matrix
float sum( const CvMat* mat ) {
float s = 0.0f;
for(int row=0; row<mat->rows; row++ ) {
const float* ptr = (const float*)(mat->data.ptr + row * mat->step);
for( col=0; col<mat->cols; col++ ) {
s += *ptr++;
}
}
return( s );
}
Khi tính pointer trong matrix, nhớ rằng matrix element data là hợp nhất. Do đó, khi de-referencing
pointer này, bạn phải nhận diện đúng element của hợp nhất này để để lấy pointer type đúng. Sau
đó, offset pointer đó, bạn phải dùng step element của matrix. Như lưu ý trước đây, step element là
theo byte. Để an toàn, tốt nhất để làm đại số pointer theo byte và sau đó cast type thích hợp,
trong trường hợp này là float. Mặc dù CVMat structure có khái niêm height và width để tương thích
với IplImage structure cũ, ta dùng the các hàng và cột cập nhật hơn thay vào. Cuối cùng, lưu ý
rằng ta tính lại ptr cho mỗi hàng hơn là đơn giản bắt đầu ở lúc bắt đầu và sau đó tăng pointer đó
mỗi lần đọc. Điều này có thể dường như dư, nhưng vì CvMat data pointer có thể chỉ trỏ đến một
ROI với trong một array lớn hơn, không có đảm bảo data sẽ liên tiếp với các hàng.
Arrays of Points
Một vấn đề mà sẽ đến thường xuyên—và là quan trọng để hiểu—là khác nhau giữa một
multidimensional array (hay matrix) of multidimensional objects và một array của một chiều cao
hơn mà chứa chỉ các one-dimensional object. Giả thiết, ví dụ, bạn có n points theo ba chiều mà
bạn muốn chuyển qua OpenCV function mà nhận một argument kiểu CvMat* (hay, đúng hơn,
cvArr*). Có bốn cách hiển nhiên bạn có thể làm điều này, và nó tuyệt đối nghiêm túc để nhớ rằng
chúng không cần thiết tương đương. Một method sẽ là dùng một two-dimensional array của type
CV32FC1 với n rows và ba columns (n-by-3). Tương tự, bạn có thể dùng một two-dimensional
array của ba row và n columns (3-by-n). Bạn có thể cũng dùng một array với n rows và một
column (n-by-1) của type CV32FC3 hay một array với một row và n columns (3-by-1). Một vài trong
những trường hợp này có thể được chuyển tự do từ cái sang cái khác (có nghĩa bạn có thể chỉ
chuyển một nơi cái khác được mong) nhưng những cái khác không thể. Để hiểu lý do, quan sát
memory layout được thấy trong Hình 3-2.
Như bạn có thể thấy trong hình, các point được ánh xạ trong memory theo cùng cách cho ba
trong bốn trường hợp vừa được mô tả ở trên nhưng khác cho cái cuối.
Hình 3-2. Một tập mười point, mỗi cái được biểu diễn bởi ba số floating-point, đặt trong bốn array mà mỗi cái dùng
một structure hơi khác; trong ba trường hợp memory layout kết quả là đồng nhất, nhưng trường hợp một là khác
Trường hợp ngay cả phức tạp hơn cho trường hợp của một N-dimensional array của các cdimensional point. Điều cốt yếu là vị trí của bất kỳ point cho trước được cho bởi bởi công thức:
  (row)  N cols  N channels  (col )  N channels  (channel )
trong đó Ncols và Nchannels là số columns và channels, tương ứng.* Từ công thức này cái có thể thấy
rằng, nhìn chung, một N-dimensional array của các c-dimensional object là không giống như một
(N + c)-dimensional array của các one-dimensional object. Trong trường hợp đặc biệt N = 1
(chẳng hạn các vectors biểu diễn một trong như n-by-1 hay 1-by-n arrays), có một suy biến đặc
biệt (đặc biệt, các tương đương được thấy trong Hình 3-2) mà có thể đôi khi có được thuận lợi
về hiệu suất.
Chi tiết cuối liên qua các OpenCV data type chẳng hạn CvPoint2D và CvPoint2D32f. Những data
type này được định nghĩa như các C structure và do đó có memory layout được định nghĩa
nghiêm chỉnh. Về cụ thể, các số integer hay floating-point mà những structures này hợp thành là
thứ tự “channel”. Như một kết quả, một one-dimensional C-style array của các object này có
cùng memory layout như một n-by-1 hay một 1-by-n array của type CV32FC2. Lý do tương tự áp
dụng cho các array của các structure của type CvPoint3D32f.
IplImage Data Structure/*/
Với tất cả điều đó trong tay, bây giờ dễ dàng thảo luận IplImage data structure. Trong object thực
chất này là một CvMat nhưng với vài goodies thêm được đặt trong nó để làm matrix thích hợp
cho một image. Structure này ban đầu được định nghĩa như một phần của Image Processing
Library (IPL) của Intel.* Định nghĩa chính xác của IplImage structure được thấy trong Example 310.
Example 3-10. IplImage header structure
typedef struct _IplImage {
int nSize;
int ID;
int nChannels;
int alphaChannel;
int depth;
char colorModel[4];
char channelSeq[4];
int dataOrder;
int origin;
int align;
int width;
int height;
struct _IplROI* roi;
struct _IplImage* maskROI;
void* imageId;
struct _IplTileInfo* tileInfo;
int imageSize;
char* imageData;
int widthStep;
int BorderMode[4];
int BorderConst[4];
char* imageDataOrigin;
} IplImage;
Nghe thật hay, ta muốn thảo luận chức năng của vài trong những variable này. Vài là bình
thường, nhưng nhiều là rất quan trọng để hiểu cách OpenCV thông dịch và làm việc với các
image.
Sau width và height, depth và nChannels phổ biến là hầu hết chủ yếu tiếp theo. Biến depth lấy một
trong một tập các value định nghĩa trong ipl.h, mà (không may) không chính xác các value ta
trạm chán khi quan sát các matrix. Điều này vì cho các image ta hướng đến làm việc với depth
và số các channel riêng rẽ (bất cứ matrix routines ta hướng đến refer chúng đồng thời). Các
depth cth được kê trong Table 3-2.
Table 3-2. OpenCV image types
Macro
IPL_DEPTH_8U
IPL_DEPTH_8S
IPL_DEPTH_16S
IPL_DEPTH_32S
IPL_DEPTH_32F
IPL_DEPTH_64F
Image pixel type
Unsigned 8-bit integer (8u)
Signed 8-bit integer (8s)
Signed 16-bit integer (16s)
Signed 32-bit integer (32s)
32-bit fl oating-point single-precision (32f)
64-bit fl oating-point double-precision (64f)
Các value có thể cho nChannels là 1, 2, 3, hay 4.
Hai members quan trọng tiếp theo là origin và dataOrder. Origin variable có thể lấy một trong hai
value: IPL_ORIGIN_TL hay IPL_ORIGIN_BL, tương ứng bới gốc tọa độ được đặt một trong upperleft hay lower-left corners của image, tương ứng.
Việc thiếu origin chuẩn (upper với lower) là nguồn quan trọng của error trong các computer vision
routines. Về cụ thể, phụ thuộc vào nơi image đến, operating system, codec, storage format, và
hơn nữa có thể tất cả ảnh hưởng vị trí của tọa độ của image cụ thể. Ví dụ, bạn có thể nghĩ bạn
lấy mẫu các pixel từ một khuôn mặt trong phần tư đỉnh của một image trong khi bạn thực sự lấy
mẫu một cái áo trong phần tư đáy. Tốt nhất là kiểm tra system lần đầu tiên bởi vẽ nơi bạn nghĩ
bạn đang vận hành trên một image patch.
dataOrder có thể là một trong IPL_DATA_ORDER_PIXEL hay IPL_DATA_ORDER_PLANE.* Giá trị này
nhận biết có hay không data nên được gói bằng cái multiple channels sau khi cái khác cho mỗi
pixel (interleaved, trường hợp bình thường), hay hơn là tất cả channel được bó thành các image
plane với các plane đặt một cái sau khi cái khác.
Parameter widthStep chứa số các byte giữa các điểm trong cùng cột và các hàng liên tiếp (tương
tự với “step” parameter của CvMat được thảo luận trước). Variable width không đủ để tính khoảng
cách vì mỗi hàng có thể được gióng với một số nào đó các byte để có được xử lý nhanh hơn của
image; do đó có thể có vài kẽ hở giữa kết thúc của hàng i và bắt đầu hàng (i + 1). Parameter
imageData chứa một pointer đến hàng đầu tiên của image data. Nếu có vài plane riêng trong
image (như khi dataOrder = IPL_DATA_ORDER_PLANE) thì chúng được đặt liên tiếp như các
image riêng với các hàng height*nChannels theo tổng, nhưng thông thường chúng được
interleaved sao cho số hàng bằng với height và với mỗi hàng chứa các interleaved channel theo
thứ tự.
Cuối cùng có region of interest (ROI) thực tế và quan trọng, mà thực sự là một instance của một
IPL/IPP structure khác, IplROI. Một IplROI chứa một xOffset, a yOffset, height, width, và coi, nơi COI
viết tắt cho channel of interest.* Ý tưởng phía sau ROI là, một khi nó được đặt, các function mà
thông thường làm việc trên toàn bộ image sẽ thay vào đó hoạt động chỉ trên tập con của image
nhận diện bởi ROI. Tất cả OpenCV functions sẽ dùng ROI nếu được đặt. Nếu COI được đặt
thành một value khác không thì vài operator sẽ làm việc chỉ trên channel được chỉ định. Không
may, nhiều OpenCV functions bỏ qua parameter này.
Accessing Image Data
Khi làm việc với image data ta thường cần làm sao cho nhanh chóng và hiệu quả. Điều này gợi ý
rằng ta không nên tự khất phụ việc ở trên của gọi các accessor function như cvSet*D hay tương
đương của chúng. Thật ta, ta thích truy cập data bên trong của image theo hầu hết cách trực tiếp
như có thể. Bằng kiến thức về bên trong IplImage structure, ta bây giờ có thể hiểu cách tốt nhất
để làm điều này.
Ngay cả qua các well-optimized routines trong OpenCV mà hoàn thành nhiều trong các nhiệm vụ
ta cần thực hiện trên image, sẽ luôn luôn có các nhiệm vụ mà không có routine đóng gói sẵn
trong library. Xem xét trường hợp của một three-channel HSV [Smith78] image mà trong đó ta
muốn set saturation và value thành 255 (các value cực đại cho một 8-bit image) while để hue
không biến đổi. Ta có thể là tốt nhất điều này bởi tự handling các pointers vào image, như ta làm
với các matrix trong Example 3-9. Tuy nhiên, có vài khác biệt phụ mà nảy sinh từ khác biệt giữa
IplImage và CvMat structures. Example 3-11 cho thấy cách nhanh nhất.
Example 3-11. Maxing out (saturating) only the “S” và “V” parts of an HSV image
void saturate_sv( IplImage* img ) {
for( int y=0; y<img->height; y++ ) {
uchar* ptr = (uchar*) (
img->imageData + y * img->widthStep
);
for( int x=0; x<img->width; x++ ) {
ptr[3*x+1] = 255;
ptr[3*x+2] = 255;
}
}
}
Ta đơn giản tính pointer ptr trực tiếp như đầu của hàng y liên qua. Từ đó, ta de-reference
saturation và value của cột x. Vì đây là threechannel image, vị trí channel c trong column x là
3*x+c.
Một khác biệt quan trọng giữa trường hợp IplImage và the trường hợp CvMat là hành xử của
imageData, so với element data của CvMat. Data element của CvMat là hợp nhất, do đó bạn phải
nhận biết pointer type mà bạn muốn dùng. imageData pointer là byte pointer (uchar*). Ta hiện đã
biết rằng data được trỏ đến không nhất thiết type uchar, mà có nghĩa rằng—khi làm đại số pointer
trên images—bạn có thể đơn giản thêm widthStep (cũng được đo theo byte) không lo về data type
thực đến sau khi phép cộng, khi bạn cast pointer kết quả thành data type bạn cần. Để recap: khi
làm việc với các matrix, bạn phải scale down offset vì the data pointer có thể là nonbyte type; khi
làm việc với images, bạn có thể dùng offset “as is” vì data pointer luôn luôn là byte type, do đó
bạn có thể chỉ cast toàn bộ thứ khi bạn sẵn sàng dùng nó.
More on ROI và widthStep
ROI và widthStep có quan trọng thực hành lớn, vì trong nhiều trường hợp chúng tăng tốc các tác vụ
computer vision bởi cho phép code xử lý chỉ vùng nhỏ của image. Hỗ trợ cho ROI và widthStep là
phổ biến trong OpenCV:* Mọi function cho phép hoạt động được giới hạn trên một vùng phụ. Để
bật ROI bật hay tắt, dùng cvSetImageROI() và cvResetImageROI() functions. Cho trước một vùng
phụ chữ nhật mong muốn theo dạng của CvRect, bạn có thể chuyển image pointer và chữ nhật
đến cvSetImageROI() để “bật” ROI; “tắt” ROI bởi chuyển image pointer đến cvResetImageROI().
void cvSetImageROI( IplImage* image, CvRect rect );
void cvResetImageROI( IplImage* image );
Để thấy cách ROI được dùng, hãy giả sử ta muốn load một image và modify vài region của
image đó. Code trong Example 3-12 đọc một image và sau đó sets x, y, width, và height của ROI
mong muốn và cuối cùng một integer value thêm vào để tăng ROI region. Program sau đó đặt ROI
dùng thuận lợi của inline cvRect() constructor. Điều quan trọng là release ROI bằng
cvResetImageROI(), ngược lại display sẽ quan sát ROI và display nghiêm túc chỉ ROI region.
Example 3-12. dùng ImageROI to increment tất cả of the pixels in a region
// roi_add <image> <x> <y> <width> <height> <add>
#include <cv.h>
#include <highgui.h>
int main(int argc, char** argv)
{
IplImage* src;
if( argc == 7 && ((src=cvLoadImage(argv[1],1)) != 0 ))
{
int x = atoi(argv[2]);
int y = atoi(argv[3]);
int width= atoi(argv[4]);
int height = atoi(argv[5]);
int add = atoi(argv[6]);
cvSetImageROI(src, cvRect(x,y,width,height));
cvAddS(src, cvScalar(add),src);
cvResetImageROI(src);
cvNamedWindow( “Roi_Add”, 1 );
cvShowImage( “Roi_Add”, src );
cvWaitKey();
}
return 0;
}
Hình 3-3 cho thấy kết quả của thêm 150 vào blue channel của image của con mèo với một ROI
có tâm trên mặt nó, dùng code từ Example 3-12.
Hình 3-3. Kết quả cộng 150 và mặt ROI của một con mèo
Ta có thể có được cùng hiệu ứng bởi dùng thông minh widthStep. Để làm điều này, ta tạo một
image header khác và đặt width và height bằng interest_rect width và height. Ta cũng cần set
image origin (upper left hay lower left) để cùng với interest_img. Tiếp theo ta set widthStep của
subimage thành widthStep của interest_img lớn hơn; cách này, bước bởi các hàng theo các bước
subimage bạn đặt thích hợp ở bắt đầu của dòng tiếp theo của subregion bằng với image lớn
hơn. Ta cuối cùng set subimage imageData pointer thành bắt đầu của subregion mong muốn, như
được thấy trong Example 3-13.
Example 3-13. dùng alternate widthStep method to increment tất cả of the pixels of interest_img by 1
// Assuming IplImage *interest_img; and
// CvRect interest_rect;
// dùng widthStep to get a region of interest
//
// (Alternate method)
//
IplImage *sub_img = cvCreateImageHeader(
cvSize(
interest_rect.width,
interest_rect.height
),
interest_img->depth,
interest_img->nChannels
);
sub_img->origin = interest_img->origin;
sub_img->widthStep = interest_img->widthStep;
sub_img->imageData = interest_img->imageData +
interest_rect.y * interest_img->widthStep +
interest_rect.x * interest_img->nChannels;
cvAddS( sub_img, cvScalar(1), sub_img );
cvReleaseImageHeader(&sub_img);
Do đó, vì sao bạn nên muốn dùng mẹo widthStep khi setting và resetting ROI dường như là qua
thuận lợi? Nguyên nhân là có những lần khi bạn muốn set và chẳng hạn giữ nhiều subregions
của một image tích cực trong khi xử lý, but ROI có thể chỉ được làm nối tiếp và phải được đặt và
reset không đổi.
Cuối cùng, một lời nên được nói ở đây về các mask. cvAddS() function dùng trong các code
example cho phép dùng của một argument thứ tư mà mặc định là NULL: const CvArr*mask=NULL.
Đây là một 8-bit single-channel array mà cho phép bạn hạn chế xử lý cho một vùng mặt nạ hình
dáng bất kỳ nhận dện bởi các nonzero pixel trong mặt nạ. Nếu ROI được đặt cùng bới mask, xử
lý sẽ được giới hạn với giao của ROI và mask. Các Mask có thể được dùng chỉ trong các
functions mà chỉ định dùng chúng.
Matrix và Image Operators
Table 3-3 kê một lượng các routine để thao tác matrix, hầu hết mà làm việc tốt cho images.
Chúng làm tất cả những việc “bình thường”, chẳng hạn diagonalizing hay transposing một matrix,
cũng như vài tác vụ phức tạp hơn, chẳng hạn tính thống kê image.
Table 3-3. Basic matrix và image operators
Function
Mô tả
cvAbs
Trị tuyệt đối tất cả elements trong array
cvAbsDiff
Trị tuyệt đối các khác biệt giữa hai arrays
cvAbsDiffS
Trị tuyệt đối các khác biệt giữa array và scalar
cvAdd
Cộng elementwise của hai array
cvAddS
Elementwise addition of an array và a scalar
cvAddWeighted
Elementwise weighted addition of hai arrays (alpha
blending)
cvAvg
Average value of tất cả elements in an array
cvAvgSdv
Absolute value và standard deviation of tất cả
elements in an array
cvCalcCovarMatrix
Compute covariance of a set of n-dimensional vectors
cvCmp
Apply selected comparison operator to tất cả
elements in hai arrays
cvCmpS
Apply selected comparison operator to an array
relative to a scalar
cvConvertScale
Convert array type with optional rescaling of the value
cvConvertScaleAbs
Convert array type sau khi absolute value with
optional rescaling
cvCopy
Copy elements of one array to another
cvCountNonZero
Count nonzero elements in an array
cvCrossProduct
Compute cross product of hai three-dimensional
vectors
cvCvtColor
Convert channels of an array from one color space to
another
cvDet
Compute determinant of a square matrix
cvDiv
Elementwise division of one array by another
cvDotProduct
Compute dot product of hai vectors
cvEigenVV
Compute eigenvalues và eigenvectors of a square
matrix
cvFlip
cvGEMM
cvGetCol
cvGetCols
cvGetDiag
cvGetDims
cvGetDimSize
cvGetRow
cvGetRows
cvGetSize
cvGetSubRect
cvInRange
cvInRangeS
cvInvert
cvMahalonobis
cvMax
cvMaxS
cvMerge
cvMin
cvMinS
cvMinMaxLoc
cvMul
cvNot
cvNorm
cvNormalize
cvOr
cvOrS
cvReduce
cvRepeat
cvSet
cvSetZero
cvSetIdentity
cvSolve
cvSplit
cvSub
cvSubS
cvSubRS
cvSum
cvSVD
cvSVBkSb
cvTrace
cvTranspose
cvXor
cvXorS
cvZero
Flip an array about a selected axis
Generalized matrix multiplication
Copy elements from column slice of an array
Copy elements from multiple adjacent columns of an
array
Copy elements from an array diagonal
Return the number of dimensions of an array
Return the sizes of tất cả dimensions of an array
Copy elements from row slice of an array
Copy elements from multiple adjacent rows of an
array
Get size of a two-dimensional array và return as
CvSize
Copy elements from subregion of an array
Test if elements of an array are with in values of hai
other arrays
Test if elements of an array are in range giữa hai
scalars
Invert a square matrix
Compute Mahalonobis khoảng cách giữa hai vectors
Elementwise max operation on hai arrays
Elementwise max operation giữa an array và a scalar
Merge vài single-channel images into one
multichannel image
Elementwise min operation on hai arrays
Elementwise min operation giữa an array và a scalar
tìm thấy minimum và maximum values in an array
Elementwise multiplication of hai arrays
Bitwise inversion of every element of an array
Compute normalized correlations giữa hai arrays
Normalize elements in an array to some value
Elementwise bit-level hay of hai arrays
Elementwise bit-level hay of an array và a scalar
Reduce a two-dimensional array to a vector by a
given operation
Tile the contents of one array into another
Set tất cả elements of an array to a given value
Set tất cả elements of an array to 0
Set tất cả elements of an array to 1 for the diagonal
và 0 otherwise
Solve a system of linear equations
Split a multichannel array into multiple single-channel
arrays
Elementwise subtraction of one array from another
Elementwise subtraction of a scalar from an array
Elementwise subtraction of an array from a scalar
Sum tất cả elements of an array
Compute singular value decomposition of a twodimensional array
Compute singular value back-substitution
Compute the trace of an array
Transpose tất cả elements of an array across the
diagonal
Elementwise bit-level XOR giữa hai arrays
Elementwise bit-level XOR giữa an array và a scalar
Set tất cả elements of an array to 0
cvAbs, cvAbsDiff, và cvAbsDiffS
void cvAbs(
const CvArr* src,
const dst
);
void cvAbsDiff(
const CvArr* src1,
const CvArr* src2,
const dst
);
void cvAbsDiffS(
const CvArr* src,
CvScalar value,
const dst
);
Các function này tính trị tuyệt đối của array hay khác biệt giữa array và vài reference. cvAbs()
function đơn giản tính trị tuyệt đối của các element trong src và viết kết quả vào dst; cvAbsDiff() đầu
tiên trừ src2 với src1 và sau đó viết trị tuyệt đối hiệu vào dst. Lưu ý rằng cvAbsDiffS() thực sự là
giống như cvAbsDiff() ngoại trừ giá trị được trừ với tất cả element của src là giá trị scalar hằng.
cvAdd, cvAddS, cvAddWeighted, và alpha blending
void cvAdd(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask = NULL
);
void cvAddS(
const CvArr* src,
CvScalar value,
CvArr* dst,
const CvArr* mask = NULL
);
void cvAddWeighted(
const CvArr* src1,
double alpha,
const CvArr* src2,
double beta,
double gamma,
CvArr* dst
);
cvAdd() kà function cộng đơn giản: nó cộng tất cả elements trong src1 với các element tương ứng
trong src2 và đựt các kết quả vào dst. Nếu mask không đặt thành NULL, thì bất kỳ element của dst
mà tương ứng với zero element của mask vẫn không đổi bởi tác vụ này.
Function tương đối quen là cvAddS() là điều tương tự ngoại trừ scalar value hằng được thêm vào mỗi
element của src.
Function cvAddWeighted() giống với cvAdd() ngoại trừ kết quả viết vào dst được tính tỉ lệ theo công
thức sau:
Function này có thể được dùng để thực hiện alpha blending [Smith79; Porter84]; mà, nó có thể
được dùng trộn một image với cái khác. Dạng của function này là:
void cvAddWeighted(
const CvArr* src1,
double alpha,
const CvArr* src2,
double beta,
double gamma,
CvArr* dst
);
Trong cvAddWeighted() ta có hai source image, src1 và src2. những images này có thể là bất kỳ
pixel type và cả hai cùng type. They có thể cũng là một hay ba channels (grayscale hay color),
lần nữa chúng phải giống nhau. Image kết quả, dst, phải cũng có cùng pixel type như src1 và src2.
những images này có thể khác nhau size, nhưng các ROI của chúng phải thống nhất theo size
hay OpenCV sẽ phát ra một error. Parameter alpha là blending strength của src1, và beta là
blending strength của src2. Phương trình alpha blending là:
Bạn có thể chuyển thành phương trình alpha blend chuẩn bởi chọn α giữa 0 và 1, setting β = 1 –
α, và setting γ thành 0; điều này tạo ra:
Tuy nhiên, cvAddWeighted() cho ta sự linh hoạt hơn —cả trong cách ta đặt nặng các blended image
và trong parameter phụ γ, mà cho phép một additive offset vào image đích. Từ dạng chung, bạn
sẽ có thể muốn giữ alpha và beta ở không nhỏ hơn 0 và tổng của chúng không lớn hơn 1; gamma
có thể được đặt phụ thuộc vào trung bình hay image value cực đại để tỉ lệ các pixel. Một program
cho thấy việc dùng alpha blending được thấy trong Example 3-14.
Example 3-14. Program hoàn chỉnh để alpha blend ROI bắt đầu ở (0,0) trong src2 với ROI bắt đầu ở (x,y) trong src1
// alphablend <imageA> <image B> <x> <y> <width> <height>
// <alpha> <beta>
#include <cv.h>
#include <highgui.h>
int main(int argc, char** argv)
{
IplImage *src1, *src2;
if( argc == 9 && ((src1=cvLoadImage(argv[1],1)) != 0
)&&((src2=cvLoadImage(argv[2],1)) != 0 ))
{
int x = atoi(argv[3]);
int y = atoi(argv[4]);
int width= atoi(argv[5]);
int height = atoi(argv[6]);
double alpha = (double)atof(argv[7]);
double beta = (double)atof(argv[8]);
cvSetImageROI(src1, cvRect(x,y,width,height));
cvSetImageROI(src2, cvRect(0,0,width,height));
cvAddWeighted(src1, alpha, src2, beta,0.0,src1);
cvResetImageROI(src1);
cvNamedWindow( “Alpha_blend”, 1 );
cvShowImage( “Alpha_blend”, src1 );
cvWaitKey();
}
return 0;
}
Code trong Example 3-14 lấy hai source images: cái chính (src1) và cái để blend (src2). Nó đọc
một rectangle ROI cho src1 và áp dụng một ROI cùng size cho src2, lần này được cấp ở gốc. Nó
đọc alpha và beta levels nhưng đặt gamma thành 0. Alpha blending được áp dụng dùng
cvAddWeighted(), và các kết quả được đặt vào src1 và hiển thị. Example output được thấy trong
Hình 3-4, nơi mặt em bé được blend vào mặt và người của mèo. Lưu ý rằng code lấy cùng ROI
như trong ví dụ cộng ROI trong Hình 3-3. Lần này ta dùng ROI như vùng blending đích.
cvAnd và cvAndS
void cvAnd(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask = NULL
);
void cvAndS(
const CvArr* src1,
CvScalar value,
CvArr* dst,
const CvArr* mask = NULL
);
Hai functions này tính bitwise và hoạt động trên array src1. Trong trường hợp của cvAnd(), mỗi
element của dst được tính với bitwise và với hai element tương ứng của src1 và src2. Trong trường
hợp của cvAndS(), bitwise và được tính với scalar value không đổi. Như luôn luôn, nếu mask là
không NULL thì chỉ các element của dst tương ứng với các nonzero entry trong mask được tính.
Dù tất cả data types được hỗ trợ, src1 và src2 phải có cùng data type cho cvAnd(). Nếu các
elements là floating-point type, thì biểu diễn bitwise của floating-point number đó được dùng.
Hình 3-4. Mặt em bé được alpha blended vào mặt con mèo
cvAvg
CvScalar cvAvg(
const CvArr* arr,
const CvArr* mask = NULL
);
cvAvg() tính giá trị trung bình của các pixel trong arr. Nếu mask là khác NULL thì trung bình sẽ được
tính trên các pixel đó mà cho giá trị tường ứng của mask là khác không.
Function này has the bây giờ deprecated alias cvMean().
cvAvgSdv
cvAvgSdv(
const CvArr* arr,
CvScalar* mean,
CvScalar* std_dev,
const CvArr* mask = NULL
);
Function này giống cvAvg(), nhưng thêm vào trung bình nó cũng tính vi phân chuẩn của các pixel.
Function này bây giờ đã mất giá so với cvMean_StdDev().
cvCalcCovarMatrix
void cvAdd(
const CvArr** vects,
int count,
CvArr* cov_mat,
CvArr* avg,
int flags
);
Cho trước bất kỳ số vector, cvCalcCovarMatrix() sẽ tính trung bình và covariance matrix cho xấp xỉ
Gaussian cho phân phối của các điểm này. Điều này có thể được dùng theo nhiều cách, dĩ
nhiên, và OpenCV có vài flag phụ mà that sẽ giúp cụ thể theo ngữ cảnh (xem Table 3-4). Những
flags này có thể được kết hợp bởi việc dùng chuẩn của Boolean hay operator.
Table 3-4. Các thành phần có thể của flags argument cho cvCalcCovarMatrix()
Flag trong flags
ý nghĩa
argument
CV_COVAR_NORMAL
Tính mean và covariance
PCA “scrambled” covariance nhanh
Dùng avg như input thay vì tính nó
Rescale output covariance matrix
Trong tất cả trường hợp, các vectors được cung cấp theo các vect như một array của các
CV_COVAR_SCRAMBLED
CV_COVAR_USE_AVERAGE
CV_COVAR_SCALE
OpenCV array (chẳng hạn một pointer đến một list các pointer đến các array), với argument count
nhận biết bao nhiêu array đang được cung cấp. Các kết quả sẽ được đặt trong cov_mat trong tất cả
trường hợp, nhưng ý nghĩa chính xác của avg phụ thuộc vào các giá trị flag (xem Table 3-4).
Các flags CV_COVAR_NORMAL và CV_COVAR_SCRAMBLED loại trừ nhau; bạn nên dùng cái này
hay cái kia nhưng không cả hai. Trong trường hợp của CV_COVAR_NORMAL, function sẽ đơn
giản tính mean và covariance các điểm được cung cấp.
–v
n được định nghĩa như phần tử thứ n của average vector v –. Covariance matrix kết quả là một nby-n matrix. Factor z là một scale factor tùy chọn; nó sẽ được đặt thành 1 trừ phi
CV_COVAR_SCALE flag được dùng.
Trong trường hợp của CV_COVAR_SCRAMBLED, cvCalcCovarMatrix() sẽ tính như sau:
Matrix này không là covariance matrix bình thường (lưu ý vị trí của transpose operator). Matrix
này được tính từ cùng các m vector chiều dài n, nhưng scrambled covariance matrix kết quả là một
m-by-m matrix. Matrix này được dùng trong vài thuật toán đặc biệt chẳng hạn PCA nhanh các
vector rất lớn (như trong kỹ thuật eigenfaces cho face recognition).
Flag CV_COVAR_USE_AVG được dùng khi mean của các input vector hiện đã biết. Trong trường
hợp này, argument avg được dùng như input hơn một output, mà giảm thời gian tính toán.
Cuối cùng, flag CV_COVAR_SCALE được dùng để áp uniform scale cho covariance matrix được
tính. Đây là factor z trong các phương trình trước. Khi được dùng kết hợp với
CV_COVAR_NORMAL flag, scale factor được áp sẽ là 1.0/m (hay, tương đương, 1.0/ count). Nếu
thay vì CV_COVAR_SCRAMBLED được dùng, thì giá trị z sẽ là 1.0/n (nghịch đảo chiều dài các
vector).
Các input và output array cho cvCalcCovarMatrix() nên tất cả là cùng floating-point type. Size của
matrix cov_mat kết quả sẽ là một trong n-by-n hay m-by-m phụ thuộc vào có hay không covariance
chuẩn hay scrambled đang được tính. nên được lưu ý rằng các “vectors” input trong vects không
thực sự phải là một chiều; chúng có thể là các two-dimensional object (chẳng hạn các image).
cvCmp và cvCmpS
void cvCmp(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
int cmp_op
);
void cvCmpS(
const CvArr* src,
double value,
CvArr* dst,
int cmp_op
);
Cả hai function này làm các so sánh, một trong giữa pixels tương ứng trong hai images hay giữa
pixels trong một image và một scalar value không đổi. Cả cvCmp() và cvCmpS() lấy argument cuối
của chúng là một comparison operator, mà có thể là bất kỳ types kê trong Table 3-5.
Table 3-5. Các giá trị của cmp_op được dùng bởi cvCmp() và cvCmpS() và comparison operation kết quả được thực
hiện
Value of cmp_op
Comparison
CV_CMP_EQ
CV_CMP_GT
(src1i == src2i)
(src1i > src2i)
CV_CMP_GE
CV_CMP_LT
CV_CMP_LE
CV_CMP_NE
(src1i >= src2i)
(src1i < src2i)
(src1i <= src2i)
(src1i != src2i)
Tất cả so sánh được kê được làm với cùng các function; bạn chỉ chuyển vào argument thích hợp
để nhận diện cái bạn muốn làm. Những functions cụ thể này làm việc chỉ trên các single-channel
image.
Các function so sánh này hữai ích trong các application nơi bạn làm vài phiên bản của
background subtraction và muốn mask các kết quả (chẳng hạn quan sát video stream từ một
security camera) mà chỉ những thông tin mới được đẩy ra khỏi image.
cvConvertScale
void cvConvertScale(
const CvArr* src,
CvArr* dst,
double scale = 1.0,
double shift= 0.0
);
cvConvertScale() function thực sự là vài function được gom thành một cái; ms sẽ thực hiện bất kỳ
trong vài functions hay, nếu cần, tất cả chúng cùng nhau. Function đầu tiên là chuyển data type
trong source image thành data type của destination image. Ví dụ, nếu ta have một 8-bit RGB
grayscale image và mà muốn chuyển thành 16-bit signed image, ta có thể làm bởi gọi
cvConvertScale().
Function thứ hai của cvConvertScale() là thực hiện biến đổi tuyến tính trên image data. Sau khi
chuyển thành data type mới, mỗi pixel value sẽ được nhận bởi value scale và sau đó cộng nó vào
value shift.
Điều quan trọng phải nhớ, ngay cả qua “Convert” đi trước “Scale” trong function name, thứ tự
thực mà trong những operations này được thực hiện là ngược lại. Đặc biệt, việc nhân bởi scale
và cộng shift xảy ra trước khi chuyển type được làm.
Khi bạn đơn giản chuyển các value mặc định (scale = 1.0 và shift= 0.0), bạn không cần có các sợ
hãi hiệu suất; OpenCV đủ thông minh để nhận biết trường hợp này và không lãng phí processor
time cho các tác vụ vô ích. Để rõ ràng (nếu bạn nghĩ nó cộng tùy ý), OpenCV cũng cung cấp
macro cvConvert(), mà là giống như cvConvertScale() nhưng được dùng theo thói quen khi các tham
số scale và shift sẽ được để ở giá trị mặc định của chúng.
cvConvertScale() sẽ làm việc trên tất cả data types và bất kỳ số channel, nhưng số channel trong
source và destination images phải giống nhau. (nếu bạn muốn chuyển từ color thành grayscale
hay ngược lại, xem cvCvtColor(), mà đến ngay sau.)
cvConvertScaleAbs
void cvConvertScaleAbs(
const CvArr* src,
CvArr* dst,
double scale = 1.0,
double shift= 0.0
);
cvConvertScaleAbs() thực sự là đúng cho cvConvertScale() ngoại trừ rằng dst image chứa giá trị tuyệt
đối của data kết quả. Đặc biệt, cvConvertScaleAbs() các scales và shifts đầu tiên, sau đó tính giá trị
tuyệt đối, và cuối cùng thực hiện chuyển datatype.
cvCopy
void cvCopy(
const CvArr* src,
CvArr* dst,
const CvArr* mask = NULL
);
Đây là cách bạn copy một image thành cái khác. cvCopy() function mong cả hai array phải có
cùng kiểu, cùng size, và cùng số các dimension. Bạn có thể dùng nó để copy các array thưa,
nhưng cho điều này việc dùng của mask không được hỗ trợ. Cho các arrays không thưa và
images, hiệu ứng của mask (nếu khác NULL) chỉ là các pixels trong dst mà tương ứng với các
entry khác không trong mask sẽ được thay đổi.
cvCountNonZero
int cvCountNonZero( const CvArr* arr );
cvCountNonZero() trả về số các pixel khác không trong array arr.
cvCrossProduct
void cvCrossProduct(
const CvArr* src1,
const CvArr* src2,
CvArr* dst
);
Function này tính vector cross product [Lagrange1773] của hai three-dimensional vectors. Nó
không quan tâm các vectors ở dạng hàng hay cột (một ít phản xạ tiết lộ rằng, cho các singlechannel objects, hai cái này thực sự là giống nhau bên trong). Cả src1 và src2 nên là các singlechannel arrays, và dst sẽ là single-channel và chiều dài chính xác là 3.Tất cả ba arrays sẽ là của
cùng data type.
cvCvtColor
void cvCvtColor(
const CvArr* src,
CvArr* dst,
int code
);
Các function trước là để chuyển từ một data type thành cái khác, và chúng muốn số các channel
là giống nhau trong cả các source và destination image. Function hoàn chỉnh là cvCvtColor(), mà
chuyển từ một color space (number of channels) thành một cái khác [Wharton71] trong khi muốn
data type là giống nhau. Tác vụ chuyển đổi chính xác được làm được chỉ định bởi argument
code, mà các giá trị có thể được kê trong Table 3-6.*
Table 3-6. Conversions available by means of cvCvtColor()
Conversion code
Meaning
CV_BGR2RGB
Chuyển giữa RGB và BGR color spaces (có
CV_RGB2BGR
hay không có alpha channel)
CV_RGBA2BGRA
CV_BGRA2RGBA
CV_RGB2RGBA
CV_BGR2BGRA
CV_RGBA2RGB
CV_BGRA2BGR
CV_RGB2BGRA
CV_RGBA2BGR
CV_BGRA2RGB
CV_BGR2RGBA
CV_RGB2GRAY
CV_BGR2GRAY
CV_GRAY2RGB
CV_GRAY2BGR
CV_RGBA2GRAY
CV_BGRA2GRAY
CV_GRAY2RGBA
CV_GRAY2BGRA
CV_RGB2BGR565
CV_BGR2BGR565
CV_BGR5652RGB
CV_BGR5652BGR
CV_RGBA2BGR565
CV_BGRA2BGR565
CV_BGR5652RGBA
CV_BGR5652BGRA
Thêm alpha channel vào RGB hay BGR image
CV_GRAY2BGR565
CV_BGR5652GRAY
CV_RGB2BGR555
CV_BGR2BGR555
CV_BGR5552RGB
CV_BGR5552BGR
CV_RGBA2BGR555
CV_BGRA2BGR555
CV_BGR5552RGBA
CV_BGR5552BGRA
Convert grayscale to BGR565 color
representation hay vice versa (16-bit images)
Convert from RGB hay BGR color space to
BGR555 color representation with optional
addition hay removal of alpha channel (16-bit
images)
Xóa alpha channel khỏi RGB hay BGR image
Chuyển RGB thành BGR color spaces trong
khi thêm hay xóa alpha channel
Chuyển RGB hay BGR color spaces thành
grayscale
Chuyển grayscale thành RGB hay BGR color
spaces (tùy chọn xóa alpha channel trong xử
lý)
Convert grayscale to RGB hay BGR color
spaces và add alpha channel
Convert from RGB hay BGR color space to
BGR565 color representation with optional
addition hay removal of alpha channel (16-bit
images)
CV_GRAY2BGR555
CV_BGR5552GRAY
CV_RGB2XYZ
CV_BGR2XYZ
CV_XYZ2RGB
CV_XYZ2BGR
CV_RGB2YCrCb
CV_BGR2YCrCb
CV_YCrCb2RGB
CV_YCrCb2BGR
CV_RGB2HSV
CV_BGR2HSV
CV_HSV2RGB
CV_HSV2BGR
CV_RGB2HLS
CV_BGR2HLS
CV_HLS2RGB
CV_HLS2BGR
CV_RGB2Lab
CV_BGR2Lab
CV_Lab2RGB
CV_Lab2BGR
CV_RGB2Luv
CV_BGR2Luv
CV_Luv2RGB
CV_Luv2BGR
CV_BayerBG2RGB
CV_BayerGB2RGB
CV_BayerRG2RGB
CV_BayerGR2RGB
CV_BayerBG2BGR
CV_BayerGB2BGR
CV_BayerRG2BGR
CV_BayerGR2BGR
Convert grayscale to BGR555 color
representation hay vice versa (16-bit images)
Convert RGB hay BGR image to CIE XYZ
representation hay vice versa (Rec 709 with
D65 white point)
Convert RGB hay BGR image to luma-chroma
(aka YCC) color representation
Convert RGB hay BGR image to HSV (hue
saturation value) color representation or vice
versa
Convert RGB hay BGR image to HLS (hue
lightness saturation) color representation or
vice versa
Convert RGB hay BGR image to CIE Lab color
representation hay vice versa
Convert RGB hay BGR image to CIE Luv color
representation
Convert from Bayer pattern (single-channel) to
RGB hay BGR image
Các chi tiết của nhiều trong những chuyển đổi này là quan trọng, và ta sẽ không đi vào chủ đề
của biểu diễn Bayer của các CIE color spaces ở đây. Cho các mục đích của ta, là đủ để lưu ý
rằng OpenCV chứa các tool để chuyển thành và từ những color space khác nhau, mà là quan
trọng với các lớp khác nhau người dùng.
Chuyển đổi color-space tất cả dùng các chuyển đổi: 8-bit images ở trong dải 0–255, 16-bit
images ở trong dải 0–65536, và các số floating-point ở trong dải 0.0–1.0. Khi grayscale images
được chuyển thành color images, tất cả các thành phần của image kết quả được lấy là bằng;
nhưng cho biến đổi ngược (chẳng hạn RGB hay BGR thành grayscale), gray value được tính bởi
dùng công thức trọng số cảm giác sau:
Y(0.299)R(0.587)G(0.114)B
Trong trường hợp của biểu diễn HSV hay HLS, hue thông thường được biểu diễn như value từ 0
đến 360.* Điều này có thể gây ra rắc rối trong các biểu diễn 8-bit và do đó, khi chuyển thành
HSV, hue được chia bởi 2 khi output image là một 8-bit image.
cvDet
double cvDet(
const CvArr* mat
);
cvDet() tính determinant (Det) crr một array vuông. Array có thể là bất kỳ data type, nhưng nó phải
là single-channel. Nếu matrix là nhỏ thì determinant trực tiếp được tính bởi công thức chuẩn.
Cho các các matrix lớn, điều này đặc biệt không hiệu quả và do đó determinant được tính bởi
Gaussian elimination.
Nếu đáng để biết bạn hiện biết rằng một matrix là đối xứng và có một determinant dương, bạn có
thể cũng dùng mẹo để giải qua singular value decomposition (SVD). Cho nhiều thông tin hơn xem
Phần “cvSVD” theo sau, nhưng mẹo là đặt cả U và V thành NULL và sau đó lấy các product của
matrix W để lấy determinant.
cvDiv
void cvDiv(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
double scale = 1
);
cvDiv() là function chia đơn giản; nó chia tất cả elements trong src1 bởi các elements tương ứng
trong src2 và đặt các kết quả vào dst. Nếu mask là khác NULL, thì bất kỳ element của dst mà tương
ứng với một zero element của mask không được thay đổi bởi operation này. Nếu bạn chỉ muốn
nghịch đảo tất cả elements trong một array, bạn có thể chuyển NULL ở vị trí của src1; routine sẽ
làm việc với điều này như một array đủ của cái số một.
cvDotProduct
double cvDotProduct(
const CvArr* src1,
const CvArr* src2
);
Function này tính vector dot product [Lagrange1773] của hai N-dimensional vectors.* Như với
cross product (và cho cùng nguyên nhân), nó không gì nếu các vector ở dạng hàng hay cột. Cả
src1 và src2 nên là các single-channel arrays, và cả hai array nên là cùng kiểu data.
cvEigenVV
double cvEigenVV(
CvArr* mat,
CvArr* evects,
CvArr* evals,
double eps = 0
);
Cho trước một matrix đối xứng mat, cvEigenVV() sẽ tính eigenvectors và eigenvalues tương ứng của
matrix đó. Điều này được làm dùng Jacobi’s method [Bronshtein97], do đó nó hiệu quả cho các
matrix nhỏ hơn.† Jacobi’s method đòi hỏi một stopping parameter, mà là kích thước cực đại của
các off-diagonal elements trong matrix cuối.‡ Optional argument eps đặt giá trị kết thúc. Trong
tiến trình của tính toán, matrix được cung cấp mat được dùng để tính, do đó các value của nó sẽ
bị thay đổi bởi function. Khi function returns, bạn sẽ tìm thấy các eigenvector của bạn trong evects
ở dạng của các hàng tiếp theo. Các giá trị eigen tương ứng được lưu trong evals. Thứ tự các
eigenvector sẽ luôn luôn là ngược theo biên độ của các eigenvalues tương ứng.
cvEigenVV() function đòi hỏi tất cả ba arrays cho floating-point type. Như với cvDet() (mô tả trước
đây), nếu matrix đang yêu cầu được biết là đối xứng và positive definite § thì tốt hơn là dùng
SVD để tìm eigenvalues và eigenvectors của mat.
cvFlip
void cvFlip(
const CvArr* src,
CvArr* dst = NULL,
int flip_mode = 0
);
Function này lập một image quanh x-axis, the y-axis, hay cả hai. Về cụ thể, nếu argument
flip_mode được đặt thành 0 thì image sẽ được lập quanh x-axis. Nếu flip_mode được đặt tành một
giá trị dương (chẳng hạn +1) image sẽ được lập quanh yaxis, và nếu đặt thành âm (chẳng hạn –
1) image sẽ được lật cả hai trục. Khi xử lý video trên các Win32 systems, bạn sẽ tự tìm thấy việc
dùng function này thường để chuyển giữa các image format vơiis gốc của chúng ở upper-left và
lower-left của image.
cvGEMM
double cvGEMM(
const CvArr* src1,
const CvArr* src2,
double alpha,
const CvArr* src3,
double beta,
CvArr* dst,
int tABC = 0
);
Generalized matrix multiplication (GEMM) trong OpenCV được thực hiện bởi cvGEMM(), mà thực
hiện nhân matrix, nhân bởi một transpose, scaled multiplication, et cetera. Trong dạng chuẩn hầu
hết của nó, cvGEMM() tính như sau:
Trong đó A, B, và C là (tương ứng) các matrix src1, src2, và src3, α và β là các hệ số, và op() là
một transposition tùy chọn của matrix bao quanh. Argument src3 có thể được đặt thành NULL, mà
trong trường hợp nó sẽ không được cộng. Các transpositions được khiển bởi optional argument
tABC, mà có thể là 0 hay bất kỳ sự kết hợp (bởi các phương tiện của Boolean OR) của
CV_GEMM_A_T, CV_GEMM_B_T, và CV_GEMM_C_T (với mỗi flag nhận diện một transposition của
matrix tương ứng).
Trong quá khứ xa OpenCV chứa các methods cvMatMul() và cvMatMulAdd(), nhưng những cái này
thường quá lộn xộn với cvMul(), mà mà gì đó khác hoàn toàn (chẳng hạn nhân phần tử với phần
tử của hai array). những functions này tiếp tục tồn tại như các macro cho các lời gọi đến
cvGEMM(). Về cụ thể, ta có hai tương đương kê trong Table 3-7.
Table 3-7. Macro aliases for common usages of cvGEMM()
cvMatMul(A, B, D)
cvMatMulAdd(A, B, C, D)
cvGEMM(A, A, 1, NULL, 0, D, 0)
cvGEMM(A, A, 1, C, 1, D, 0)
Tất cả các matrix phải ở size thích hợp để nhân, và tất cả nên là kiểu floating-point. cvGEMM()
function hỗ trợ các matrix two-channel, mà trong trường hợp nó sẽ làm hai channels như hai
components của một single complex number.
cvGetCol và cvGetCols
CvMat* cvGetCol(
const CvArr* arr,
CvMat* submat,
int col
);
CvMat* cvGetCols(
const CvArr* arr,
CvMat* submat,
int start_col,
int end_col
);
Function cvGetCol() được dùng để lâyys một column đơn ra khỏi một matrix và return nó như một
vector (chẳng hạn như một matrix với chỉ một column). Trong trường hợp này matrix header
submat sẽ được biến đổi để trỏ đến một column cụ thể trong arr. Điều quan trọng cần lưu ý rằng
biến đổi header như thế không include allocation of memory hay copying của data. Nội dung của
submat sẽ đơn giản được thay đổi sao cho nó nhận biết đúng column được chọn trong arr. Tất cả
data types được hỗ trợ. cvGetCols() làm việc chính xác theo cùng cách, ngoại trừ rằng tất cả columns
từ start_col đến end_col được chọn. Với cả hai function, return value là một pointer đến một
header tương ứng với column hay column span được chỉ đinh cụ thể (chẳng hạn submat) được
chọn bởi trình gọi.
cvGetDiag
CvMat* cvGetDiag(
const CvArr* arr,
CvMat* submat,
int diag = 0
);
cvGetDiag() tương tưj với cvGetCol(); nó được dùng để lấy một single diagonal từ một matrix và
return nó như một vector. Argument submat là một matrix header. Function cvGetDiag() sẽ điền các
component của header này sao cho nó trỏ đến thông tin đúng trong arr. Lưu ý rằng kết quả của
gọi cvGetDiag() mà là header bạn cung cấp là được cấu hình đúng để trỏ đênns diagonal data
trong arr, nhưng data từ arr không được copy. Optional argument diag chỉ định diagonal mà được
trỏ đến bởi submat. Nếu diag được đặt thành default value 0, diagonal chính sẽ được chọn. Nếu
diag lớn hơn 0, thì diagonal bắt đầu ở (diag,0) sẽ được chọn; nếu diag nhỏ hơn 0, thì diagonal bắt
đầu ở (0,-diag) sẽ được chọn thay. cvGetDiag() function không đòi hỏi matrix arr là vuông, nhưng
array submat phải có chiều dài đúng cho size của input array. Returned value cuối là giống như
value của submat chuyển vào khi the function được gọi.
cvGetDims và cvGetDimSize
int cvGetDims(
const CvArr* arr,
int* sizes=NULL
);
int cvGetDimSize(
const CvArr* arr,
int index
);
Nhớ rằn các arrays trong OpenCV có thể có chiều lớn hơn hai. Function cvGetDims() trả về số các
chiều array của một array cụ thể và (optionally) các size của mỗi dimension này. Các size sẽ
được báo cáo nếu các array size là không NULL. Nếu sizes được dùng, nó nên là một một pointer
đến n integers, trong đó n là số các dimension. Nếu bạn không biết số dimension trước, bạn có
thể cấp các sizes cho CV_MAX_DIM integers chỉ để an toàn.
Function cvGetDimSize() trả về size của dimension đơn được chỉ định bởi index. Nếu array là một
trong một matrix hay một image, số các dimension được return sẽ luôn luôn là hai.* Cho các các
matrix và images, thứ tự của size trả về bởi cvGetDims() sẽ luôn luôn là số các hàng đầu tiên theo
sau bởi số các cột.
cvGetRow và cvGetRows
CvMat* cvGetRow(
const CvArr* arr,
CvMat* submat,
int row
);
CvMat* cvGetRows(
const CvArr* arr,
CvMat* submat,
int start_row,
int end_row
);
cvGetRow() lấy một single row ra khỏi một matrix và trả về nó như một vector (một matrix với chỉ
một hàng). Như với cvGetRow(),matrix header submat sẽ được biến đổi thành trỏ đến một hàng cụ
thể trong arr, và các biên đổi của header này không bao gồm việc cấp memory hay copying data;
nội dung của submat sẽ đơn giản được thay đổi như nó nhận biết đúng cột được chọn trong arr. Tất
cả data types được hỗ trợ. Function cvGetRows() làm việc chính xác giống cách, ngoại trừ rằng tất
cả các hàng từ start_row đến end_row được chọn. Cới cả hai function, return value là một pointer
đến một header tương ứng với hàng hay row span chỉ định cụ thể chọn bởi trình gọi.
cvGetSize
CvSize cvGetSize( const CvArr* arr );
Liên quan gần với cvGetDims(), cvGetSize() trả về size của một array. Khác biệt chính là cvGetSize()
được thiết kế để được dùng trên các matrix và images, mà luôn luôn có dimension là hai. Size có
thể sau được trả về theo dạng của một CvSize structure, mà phù hợp để dùng khi (ví dụ) xây
dựng matrix hay image mới của cùng size.
cvGetSubRect
CvSize cvGetSubRect(
const CvArr* arr,
CvArr* submat,
CvRect rect
);
cvGetSubRect() là tương tự với cvGetColumns() hay cvGetRows() ngoại trừ rằng nó chọn vài hình chữ
nhật con tùy ý trong array chỉ định bởi argument rect. Như với các routine khác mà chọn các
phần phụ của các array, submat đơn giản là một header mà sẽ được điền bởi cvGetSubRect() theo
một cách mà nó trỏ đúng đến submatrix mong muốn (chẳng hạn không có memory được cấp và
không data được copy).
cvInRange và cvInRangeS
void cvInRange(
const CvArr* src,
const CvArr* lower,
const CvArr* upper,
CvArr* dst
);
void cvInRangeS(
const CvArr* src,
CvScalar lower,
CvScalar upper,
CvArr* dst
);
Hai functions này có thể được dùng để kiểm tra có các pixels trong một image rơi vào một dải
được chỉ định cụ thể. Trong trường hợp của cvInRange(), mỗi pixel của src được so với value
tương ứng trong các images lower và upper. Nếu value trong src là lớn hơn hay bằng value trong
lower và cũng nhỏ hơn value trong upper, sau đó value tương ứng trong dst sẽ được đặt thành 0xff;
ngược lại, value trong dst sẽ được đặt thành 0.
Function cvInRangeS() làm việc chính xác cùng cách ngoại trừ rằng image src được so với các giá
trị hằng (CvScalar) theo lower và upper. Cho cả hai function, image src có thể là bất kỳ type; nếu nó
có nhiều channels thì mỗi channel sẽ được handle riêng. Lưu ý rằng dst phải là cùng size và số
channels và cũng phải một 8-bit image.
cvInvert
double cvInvert(
const CvArr* src,
CvArr* dst,
Int method = CV_LU
);
cvInvert() nghịch đảo matrix trong src và đặt kết quả trong dst. Function này hỗ trợ vài methods của
tính inverse matrix (xem Table 3-8), nhưng mặc định là Gaussian elimination. Return value phụ
thuộc vào method được dùng.
Table 3-8. Possible values of method argument to cvInvert()
Value of method argument Meaning
CV_LU
Gaussian elimination (LU Decomposition)
CV_SVD
Singular value decomposition (SVD)
CV_SVD_SYM
SVD for symmetric các matrix
Trong trường hợp của Gaussian elimination (method=CV_LU), determinant của src được trả về khi
function hoàn thành. Nếu determinant là 0, thì nghịch đảo không thực sự được thực hiện và array
dst đơn giản được đặt tất cả thành 0.
Trong trường hợp của CV_SVD hay CV_SVD_SYM, return value là số điều kiện nghịch đảo cho
matrix (ratio của eigenvalue nhỏ nhất đến lớn nhất). Nếu matrix src là singular, thì cvInvert() trong
SVD mode sẽ thay vào tính pseudo-inverse.
cvMahalonobis
CvSize cvMahalonobis(
const CvArr* vec1,
const CvArr* vec2,
CvArr* mat
);
Khoảng cách Mahalonobis (Mahal) được định nghĩa như vector khoảng cách đo giữa một điểm và
tâm của phân phối Gaussian ; nó được tính dùng inverse covariance mà phân phối đó như một
metric. Xem Figure 3-5. Về trực giác, điều này tương tự với z-score trong thông kê cơ bản, trong
đó khoảng cách từ tâm của phân phối được đo theo đơn vị thay đổi của phân phối đó. Khoảng
cách Mahalonobis chỉ là tổng quát hóa đa biến của cùng ý tưởng. cvMahalonobis() tính giá trị này:
rMahalonobis  ( x   )T  ( x   )
1
Vector vec1 được cộng trước để là điểm x, và vector vec2 được lấy để là hiệu dụng của phân
phối.* Matrix mat là inverse covariance.
Trong thực tế, covariance matrix này sẽ thường được tính bằng cvCalcCovar Matrix() (được mô tả
trước đây) và sau đó nghịch đảo bằng cvInvert(). Thực tế lập trình tốt là dùng SV_SVD method cho
nghịch đảo này vì một ngày bạn sẽ trạm chán một phân phối mà là một trong các eigenvalue là 0!
cvMax và cvMaxS
void cvMax(
const CvArr* src1,
const CvArr* src2,
);
void cvMaxS(
const CvArr* src,
double value,
CvArr* dst
);
Hình 3-5. Một phân phối các điểm trong hai chiều với các ellipsoids xếp chồng biểu diễn khoảng cách Mahalonobis
của 1.0, 2.0, và 3.0 từ hiệu dụng của phân phối
cvMax() tính giá trị cực đại của cặp tương ứng các pixel trong các array src1 và src2. Với cvMaxS(),
src array được so với scalar value hằng. Như luôn luôn, nếu mask là khác NULL thì chỉ các element
của dst tương ứng với các entry khác không trong mask được tính.
cvMerge
void cvMerge(
const CvArr* src0,
const CvArr* src1,
const CvArr* src2,
const CvArr* src3,
CvArr* dst
);
cvMerge() là tác vị nghịch đảo của cvSplit(). Arrays trong src0, src1, src2, và src3 được kết hợp vào
array dst. Dĩ nhiên, dst nên có cùng data type và size như tất cả trong các source array, nhưng có
thể có hai, ba, hay bốn channel. Các source image không được dùng có thể được leftset thành
NULL.
cvMin và cvMinS
void cvMin(
const CvArr* src1,
const CvArr* src2,
CvArr* dst
);
void cvMinS(
const CvArr* src,
double value,
CvArr* dst
);
cvMin() tính giá trị cực tiểu mỗi cặp tương ứng của các pixels trong arrays src1 và src2. Với
cvMinS(), các src arrays được so với scalar value hằng. Lần nữa, nếu mask khác NULL thì chỉ các
elements của dst tương ứng với các entry khác không trong mask được tính.
cvMinMaxLoc
void cvMinMaxLoc(
const CvArr* arr,
double* min_val,
double* max_val,
CvPoint* min_loc = NULL,
CvPoint* max_loc = NULL,
const CvArr* mask = NULL
);
Routine này tìm các giá trị cự đại và cực tiểu trong array arr và (tùy chọn) trả về vị trí của chúng.
Các giá trị cực đại và cực tiểu được tính được đặt trong min_val và max_val. Tùy chọn, các vị trí
của các cực trị này sẽ cũng được viết vào các địa chỉ cho bởi min_loc và max_loc nếu các hía trị
này là không NULL.
Như bình thường, nếu mask là khác NULL vì chỉ các phần của image arr mà tương ứng với các
nonzero pixels trong mask được quan tâm. cvMinMaxLoc() routine handles chỉ single-channel
arrays, tuy nhiên, do đó nếu bạn có một multichannel array thì bạn nên dùng cvSetCOI() để set
một channel cụ thể để quan tâm.
cvMul
void cvMul(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
double scale=1
);
cvMul() là multiplication function đơn giản. Nó nhân tất cả các elements trong src1 bởi các
elements tương ứng trong src2 và sau đó đặt các kết quả trong dst. Nếu mask là non-NULL, thì bất
kỳ element của dst mà tương ứng với một zero element của mask không được thay đổi bởi
operation này. Không có function cvMulS() vì chức năng đó hiện được cấp bởi cvScale() hay
cvCvtScale().
Một điều xa hơn cần nhớ: cvMul() thực hiện nhân phần từ với phần tử. Một ngày, khi bạn đang
nhân vài matrix, bạn có thể bị lôi cuốn để đến với cvMul(). Điều này sẽ làm việc; nhớ rằng nhân
matrix được làm với cvGEMM(), không với cvMul().
cvNot
void(
const CvArr* src,
CvArr* dst
);
Function cvNot() nghịch đảo mọi bit trong mọi element của src và sau đó đặt kết quả trong dst. Do
đó, cho một 8-bit image value 0x00 sẽ được ánh xạ thành 0xff và value 0x83 sẽ được chiếu
thành 0x7c.
cvNorm
double cvNorm(
const CvArr* arr1,
const CvArr* arr2 = NULL,
int norm_type = CV_L2,
const CvArr* mask = NULL
);
Function này có thể được dùng để tính total norm của một array và cũng một lượng các norm
khoảng cách tương đối nếu hai arrays được cung cấp. Trong trường hợp trước, norm được tính
như được thấy trong Table 3-9.
Table 3-9. Norm được tính bởi cvNorm() cho các giá trị khác nhau của khi arr2=NULL
Norm_type
CV_C
Kết quả
CV_L1
|| arr1 || L1   abs(arr1x , y )
|| arr1 ||c  max x , y abs (arr1x , y )
x, y
CV_L2
|| arr1 || L 2   arr12x , y )
x, y
Nếu array argument thứ hai arr2 là non-NULL, thì norm được tính là một norm khác—mà là, gì đó
giống khoảng cách giữa hai arrays.* Trong ba trường hợp đầu tiên được thấy trong Table 3-10,
norm là tuyệt đối; trong hai trường hợp sau nó được rescale bởi biên độ của array thứ hai arr2.
Table 3-10. Norm được tính bởi cvNorm() cho các giá trị khác nhau của norm_type khi arr2 là non-NULL
Norm_type
Kết quả
CV_C
|| arr1  arr 2 ||c  max x , y abs(arr1x , y  arr 2 x , y )
CV_L1
|| arr1  arr 2 || L1   abs (arr1x , y  arr 2 x , y )
x, y
|| arr1  arr 2 || x , y   (arr1x , y  arr 2 x , y ) 2
CV_L2
x, y
CV_RELATIVE_C
CV_RELATIVE_L1
|| arr1  arr 2 ||C
|| arr 2 ||C
|| arr1  arr 2 || L1
|| arr 2 || L 2
Trong tất cả trường hợp, arr1 và arr2 phải có cùng size và số channel. Khi có nhiều hơn một
channel, norm được tính trên tất cả các channel cùng nhau (chẳng hạn các sum trong Tables 3-9
và 3-10 là không chỉ trên x và y mà còn trên các channel).
cvNormalize
cvNormalize(
const CvArr* src,
CvArr* dst,
double a = 1.0,
double b = 0.0,
int norm_type = CV_L2,
const CvArr* mask = NULL
);
Như với nhiều OpenCV function, cvNormalize() làm nhiều hơn nó có thể ở đầu tiên xuất hiện. Phụ
thuộc vào value của norm_type, image src được normalized hay ngược lại được chiếu vào dải cụ
thể trong dst. Các giá trị có thể của norm_type được thấy trong Table 3-11.
Table 3-11. Các giá trị có thể của norm_type argument cho cvNormalize()
Norm_type
CV_C
Kết quả
CV_L1
|| arr1 || L1   abs( I x , y )  a
|| arr1 ||C  max dst abs ( I x , y )  a
dst
CV_L2
|| arr1 || L 2   I x2, y  a
CV_MINMAX
Chiếu vào dải [a,b]
dst
Trong trường hợp của C norm, array src được rescaled thành biên độ của giá trị tuyệt đối của
entry lớn nhất bằng với a. Trong trường hợp L1 hay L2 norm, array được rescaled sao cho norm
được cho bằng với giá trị của a. Nếu norm_type được đặt thành CV_MINMAX, thì các giá trị của
array được rescaled và translated sao cho chúng được chiếu tuyến tính vào khoảng giữa a và b
(bao gồm).
Như trước, nếu mask là non-NULL thì chỉ các pixels mà tương ứng với các nonzero values của
mask image sẽ cấu thành tính toán của norm—và chỉ các pixels này sẽ được thay đổi bởi
cvNormalize().
cvOr và cvOrS
void cvOr(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask=NULL
);
void cvOrS(
const CvArr* src,
CvScalar value,
CvArr* dst,
const CvArr* mask = NULL
);
Hai functions này tính bitwise OR operation trên array src1. Trong trường hợp của cvOr(), mỗi
element của dst được tính như bitwise OR của hai elements tương ứng của src1 và src2. Trong
trường hợp của cvOrS(), bitwise OR được tính với scalar value hằng. Như thường, nếu mask là
non-NULL thì chỉ các elements của dst tương ứng với các nonzero entry trong mask được tính.
Tất của data types được hỗ trợ, nhưng src1 và src2 phải có cùng data type cho cvOr(). Nếu các
elements là floating-point type, thì biểu diễn bitwise của floating-point number đó được dùng.
cvReduce
CvSize cvReduce(
const CvArr* src,
CvArr* dst,
int dim,
int op = CV_REDUCE_SUM
);
Reduction là biến đổi systematic của input matrix src vào một vector dst bởi áp dụng vài luật kết
hợp op trên mỗi hàng (hay cột) và láng giêngf của nó đến khi chỉ một hàng (hay cột) vẫn thế (xem
Table 3-12).* Argument op điều khiển cách reduction được làm, như tổng kết trong Table 3-13.
Table 3-12. Argument op trong cvReduce() chọn reduction operator
Value of op
Result
CV_REDUCE_SUM
Compute sum across vectors
CV_REDUCE_AVG
Compute average across vectors
CV_REDUCE_MAX
Compute maximum across vectors
CV_REDUCE_MIN
Compute minimum across vectors
Table 3-13. Argument dim trong cvReduce() điều khiển hướng của reduction
Value of dim
Result
+1
Collapse to a single row
0
Collapse to a single column
–1
Collapse as appropriate for dst
cvReduce() hỗ trợ multichannel arrays của floating-point type. Nó cũng cho phép dùng một
precision type cao hơn trong dst hơn xuất hiện trong src. Điều này thích hợp chính cho
CV_REDUCE_SUM và CV_REDUCE_AVG, nơi overflows và các vấn đề cộng là có thể.
cvRepeat
void cvRepeat(
const CvArr* src,
CvArr* dst
);
Function này chép nội dung của src vào dst, lặp lại bao nhiêu lần như cần thiết để điền dst. Về cụ
thể, dst có thể là bất kỳ size tương ứng với src. Nó có thể là lớn hơn hay nhỏ hơn, và nó không
cần có một số nguyên quan hệ giữa bất kỳ các chiều của nó và các chiều tương ứng của src.
cvScale
void cvScale(
const CvArr* src,
CvArr* dst,
double scale
);
Function cvScale() thực sự là một macro cho cvConvertScale() mà đặt shift argument thành 0.0. Do
đó, nó có thể được dùng để rescale nội dung của một array và chuyển từ một kiểu của data type
thành kiểu khác.
cvSet và cvSetZero
void cvSet(
CvArr* arr,
CvScalar value,
const CvArr* mask = NULL
);
Các function này set tất cả values trong tất cả channel của array thành giá trị cụ thể. cvSet()
function nhận một mask argument tùy chọn: nếu mask được cung cấp, thì chỉ các pixels này trong
image arr mà tương ứng với các nonzero values của mask image sẽ được đặt thành value chỉ định.
Function cvSetZero() chỉ là một đồng nghĩa cho cvSet(0.0).
cvSetIdentity
void cvSetIdentity( CvArr* arr );
cvSetIdentity() đặt tất cả elements của array thành 0 ngoại trừ cho các element mà hàng và cột là
bằng; các elements này được đặt thành 1. cvSetIdentity() hỗ trợ tất cả data types và không ngay cả
đòi hỏi array là vuông.
cvSolve
int cvSolve(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
int method = CV_LU
);
Function cvSolve() cung cấp một cách nhanh để giải các hệ tuyến tính dựa trên cvInvert(). Nó tính
lời giải với
C  arg min x || A. X  B ||
Trong đó A là square matrix cho bởi src1, B là vector src2, và C là lời giải tính bởi cvSolve() cho
vector tốt nhất X nó có thể tìm thấy. Vector tốt nhất X đó được trả về trong dst. Cùng các method
được hỗ trợ như bởi cvInvert() (mô tả trước đây); chỉ các floating-point data type được hỗ trợ.
Function trả về một integer value trong đó một nonzero return nhận biết mà nó có thể tìm thấy
một đáp án.
Nên được lưu ý rằng cvSolve() có thể được dùng để giải overdetermined các hệ tuyến tính.
Overdetermined systems sẽ được giải dùng gì đó gọi là pseudo-inverse, mà dùng các SVD method
để tìm thấy đáp án least-squares cho hệ phương trình.
cvSplit
void cvSplit(
const CvArr* src,
CvArr* dst0,
CvArr* dst1,
CvArr* dst2,
CvArr* dst3
);
Có các lần khi không thuận lợi để làm việc với một multichannel image. Trong các trường hợp
này, ta có thể dùng cvSplit() để copy mỗi channel riêng thành một trrg vài single-channel images
được cung cấp. cvSplit() function sẽ copy các channels trong src vào images dst0, dst1, dst2, và
dst3 như cần thiết. Các destination images phải thỏa mãn source image theo size và data type
nhưng, dĩ nhiên, nên là các single-channel images. Nếu source image có ít hơn bốn channels
(như nó sẽ thường), thì các destination argument không cần thiết cho cvSplit() có thể được đặt
thành NULL.
cvSub
void cvSub(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask = NULL
);
Function này thực hiện một phép trừ phần tử phần tử cơ sở của một array src2 với một cái khác
src1 và đặt kết quả trong dst. Nếu array mask là non-NULL, thì chỉ các elements này của dst tương
ứng với các nonzero elements của mask được tính. Lưu ý rằng src1, src2, và dst phải tất cả có
cùng type, size, và số các channels; mask, nếu được dùng, nên là một 8-bit array của cùng size
và số các channels như dst.
cvSub, cvSubS, và cvSubRS
void cvSub(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask = NULL
);
void cvSubS(
const CvArr* src,
CvScalar value,
CvArr* dst,
const CvArr* mask = NULL
);
void cvSubRS(
const CvArr* src,
CvScalar value,
CvArr* dst,
const CvArr* mask = NULL
);
cvSub() là một hàm trừ đơn giản; nó trừ tất cả các elements trong src2 với các elements tương
ứng trong src1 và đặt các kết quả trong dst. Nếu mask là non-NULL, thì bất kỳ element của dst mà
tương ứng với một zero element của mask không được thay đổi bởi tác vụ này. Function liên
quan gần cvSubS() làm cùng việc ngoại trừ rằng một constant scalar value được thêm cho mỗi
element của src. Function cvSubRS() là giống như cvSubS() ngoại trừ rằng, hơn việc trừ một hằng
với mỗi element của src, nó trừ mọi element của src với value hằng.
cvSum
CvScalar cvSum(
CvArr* arr
);
cvSum() cộng tất cả pixels trong tất cả các channels của array arr. Nhận thấy rằng return value là
kiểu CvScalar, mà có nghĩa rằng cvSum() có thể thích hợp các multichannel arrays. Trong trường
hợp đó, tổng của mỗi channel được đặt trong thành phần tương ứng của CvScalar return value.
cvSVD
void cvSVD(
CvArr* A,
CvArr* W,
CvArr* U = NULL,
CvArr* V = NULL,
int flags = 0
);
Singular value decomposition (SVD) là phân rã của một m-by-m matrix A thành dạng:
A  U W V T
Trong đó W is a diagonal matrix và U và V là các matrix hợp nhất m-by-m và n-by-n. Dĩ nhiên
matrix W cũng là m-by-n matrix, do đó ở đây “diagonal” có nghĩa rằng bất kỳ element mà số hàng
và cottj là không bằng nhau thì nhất thiết bằng 0. Vì W nhất thiết là diagonal, OpenCV cho phép
nó được biểu diễn một trong bởi một m-by-n matrix hay bởi một n-by-1 vector (mà trong trường
hợp đó vector sẽ chứa chỉ các giá trị diagonal “singular”).
Các matrix U và V là tùy chọn với cvSVD(), và nếu chúng được đặt thành NULL thì không có giá trị
sẽ được trả về. Các argument flags cuối có thể là bất kỳ hay tất cả trong ba option được mô tả
trong Table 3-14 (được kết hợp như thích hợp bằng Boolean hay operator).
Table 3-14. Các flag có thể cho flags argument với cvSVD()
Flag
Result
CV_SVD_MODIFY_A
cho phép modification of matrix A
CV_SVD_U_T
Return UT thay vì U
CV_SVD_V_T
Return VT thay vì V
cvSVBkSb
void cvSVBkSb(
const CvArr* W,
const CvArr* U,
const CvArr* V,
const CvArr* B,
CvArr* X,
int flags = 0
);
Đây là một function mà bạn không thích gọi trực tiếp. Trong kết hợp với cvSVD() (vừa được mô
tả), nó nằm dưới các SVD-based methods của cvInvert() và cvSolve(). Mà được bảo, bạn có thể
muốn cut out người trung gian và làm các nghịch đảo matrix riêng (phụ thuộc vào data source,
điều này có thể tiết kiệm cho bạn khỏi một chùm memory allocations cho các các matrix tạm bên
trong của cvInvert() hay cvSolve()).
Function cvSVBkSb() tính thay thế phía sau cho một matrix A mà được biểu diễn theo dạng của
một phân rã của các matrix U, W, và V (chẳng hạn một SVD). Kết quả matrix X được cho bởi
công thức:
X  V W * U T  B
Matrix B laf optional, và nếu được đặt thành NULL nó sẽ được bỏ qua. Matrix W* là một matrix
mà các diagonal elements được định nghĩa bởi
*i  i1
cho λi ≥ ε. Giá trị ε này là singularity
threshold, một số rất nhỏ mà điển hình tỉ lệ với tổng của các diagonal elements của W (chẳng hạn
 ii )
cvTrace
CvScalar cvTrace( const CvArr* mat );
Trace của một matrix (Trace) là tổng tất cả các diagonal element. Trace trong OpenCV được
thực hiện trên đinht của cvGetDiag() function, do đó nó không đòi hỏi array chuyển vào là vuông.
Các Multichannel arrays được hỗ trợ, nhưng array mat nên là floating-point type.
cvTranspose và cvT
void cvTranspose(
const CvArr* src,
CvArr* dst
);
cvTranspose() chép mọi element của src và vị trí trong dst nhận biết bởi row và column index.
Function này hỗ trợ multichannel array; tuy nhiên, nếu bạn đang dùng multiple channels để biểu
diễn các complex number, nhớ rằng cvTranspose() không thực hiện complex conjugation (một
cách nhanh để hoàn thành nhiệm vụ này bởi các phương tiện của cvXorS() function, mà có thể
được dùng để trực tiếp lật dấu các bit trong phần ảo của array). Macro cvT() đơn giản tiện tay cho
cvTranspose().
cvXor và cvXorS
void cvXor(
const CvArr* src1,
const CvArr* src2,
CvArr* dst,
const CvArr* mask=NULL
);
void cvXorS(
const CvArr* src,
CvScalar value,
CvArr* dst,
const CvArr* mask=NULL
);
Hai functions này tính bitwise XOR operation trên array src1. Trong trường hợp của cvXor(), mỗi
element của dst được tính như bitwise XOR của hai elements tương ứng của src1 và src2. Trong
trường hợp của cvXorS(), bitwise XOR được tính với constant scalar value. Lặp lại, nếu mask là
non-NULL thì chỉ các elements của dst tương ứng với các nonzero entry trong mask được tính.
Tất cả data types được hỗ trợ, nhưng src1 và src2 phải có cùng data type cho cvXor(). Cho các
floating-point element, biểu diễn bitwise của floating-point number đó được dùng.
cvZero
void cvZero( CvArr* arr );
Function này đặt tất cả values trong tất cả channels của array thành 0.
Drawing Things
Điều mà thường xuyên xảy ra là cần vẽ vài thứ của picture hay vẽ gì đó lên đỉnh của một image
có được từ đâu đó. Toward this end, OpenCV cung cấp một đám các functions mà sẽ cho phép
ta làm các lines, squares, circles, và khác.
Lines
Đơn giản nhất trong những routines này là vẽ draws line bởi Bresenham algorithm
[Bresenham65]:
void cvLine(
CvArr* array,
CvPoint pt1,
CvPoint pt2,
CvScalar color,
int thickness = 1,
int connectivity = 8
);
Argument đầu tiên cho cvLine() là CvArr* bình thường, mà trong ngữ cảnh này điển hình có nghĩa
một IplImage* image pointer. Hai arguments tiếp thep là CvPoints. Như một gợi nhớ nhanh, CvPoint
là một structure đơn giản chứa chỉ các integer members x và y. Ta có thể tạo một CvPoint “trực
tiếp” bằng routine cvPoint(int x, int y), mà gòi thuận tiện hai integers vào một CvPoint structure cho
ta.
Argument tiếp, color, là kiểu CvScalar. CvScalars cũng là các structure, mà (bạn có thể nhớ lại)
được định nghĩa như sau:
typdef struct {
double val[4];
} CvScalar;
Như bạn có thể thấy, structure này chỉ là tập bốn số doubles. Trong trường hợp này, ba cái đầu
tiên biểu diễn red, green, và blue channels; cái thứ tư không dùng (nó có thể được dùng cho một
alpha channel khi thích hợp). Một điển hình làm việc dùng của handy macro CV_RGB(r, g, b).
Macro này lấy ba số và gói chúng vào một CvScalar. Hai arguments tiếp theo là optional. Độ dày
là độ dày của line (theo pixels), và sự kết đặt anti-aliasing mode. Mặc định là “8 connected”, mà sẽ
cho một nice, smooth, anti-aliased line. Bạn có thể cũng đặt cái này thành “4 connected” line; các
diagonal sẽ là thô và chắc, nhưng chúng sẽ được vẽ nhanh hơn nhiều.
Dễ như cvLine() is cvRectangle(). Có lẽ không cần thiết nói với bạn rằng cvRectangle() vẽ một
rectangle. Nó có cùng các argument như cvLine() ngoại trừ rằng không có connectivity argument.
Đây là vì các rectangle kết quả luôn luôn được hướng bằng các cạnh song song với các trục xvà y-axes. Bằng cvRectangle(), ta đơn giản cho hai điểm cho các cạnh đối diện và OpenCV sẽ vẽ
rectangle.
void cvRectangle(
CvArr* array,
CvPoint pt1,
CvPoint pt2,
CvScalar color,
int thickness = 1
);
Circles và Ellipses
Dễ tương tự là method để vẽ các circle, mà có cùng các argument.
void cvCircle (
CvArr* array,
CvPoint center,
int radius,
CvScalar color,
int thickness = 1,
int connectivity = 8
);
Cho các circle, các rectangle, và tất cả các đường kín khác, thickness argument có thể cũng được
đặt thành CV_FILL, mà là một alias cho –1; kết quả là hình được vẽ sẽ được tô cùng màu như
các cạnh.
Chỉ phức tạp hơn một ít với cvCircle() là routine để vẽ các generalized ellipses:
void cvEllipse(
CvArr* img,
CvPoint center,
CvSize axes,
double angle,
double start_angle,
double end_angle,
CvScalar color,
int thickness = 1,
int line_type = 8
);
Trong trường hợp này, thành phần chính mới là axes argument, mà là type CvSize. Function
CvSize rất giống CvPoint và CvScalar; nó là một structure đơn giản, trong trường hợp này chứa chỉ
các member width và height. Như CvPoint và CvScalar, có một helper function thuận tiện cvSize(int
height, int width) mà sẽ return một CvSize structure khi ta cần một cái. Trong trường hợp này, các
height và width arguments biểu diễn length của các trục major và minor của ellipse.
Angle là góc (theo độ) của major axis, mà được đo theo counterclockwise từ trục hoành (chẳng
hạn từ x-axis). Tương tự start_angle và end_angle nhận diện (cũng theo độ) góc cho cung bắt đầu
và cho nó hoàn thành. Do đó, cho một ellipse hoàn chỉnh bạn phải đặt những values này thành 0
và 360, tương ứng. Một cách lhacs để chỉ định vẽ một ellipse là to dùng một bounding box:
void cvEllipseBox(
CvArr* img,
CvBox2D box,
CvScalar color,
int thickness = 1,
int line_type = 8,
int shift= 0
);
Ở đây lần nữa ta thấy một cái khác của các helper structures của OpenCV, CvBox2D:
typdef struct {
CvPoint2D32f center;
CvSize2D32f size;
float angle;
} CvBox2D;
CvPoint2D32f là tương tự floating-point của CvPoint, và CvSize2D32f là tương tự floatingpoint của
CvSize. Nhưng cái này, cùng với góc nghiêng, chỉ định hiệu quả bounding box cho ellipse.
Polygons
Cuối cùng, ta có một tập các functions để vẽ các polygon:
void cvFillPoly(
CvArr* img,
CvPoint** pts,
int* npts,
int contours,
CvScalar color,
int line_type = 8
);
void cvFillConvexPoly(
CvArr* img,
CvPoint* pts,
int npts,
CvScalar color,
int line_type = 8
);
void cvPolyLine(
CvArr* img,
CvPoint** pts,
int* npts,
int contours,
int is_closed,
CvScalar color,
int thickness = 1,
int line_type = 8
);
Tất cẩ ba cái này là các biến đổi ít trên cùng ý tưởng, với khác biệt chính là cách các điểm được
chỉ định.
Trong cvFillPoly(), các điểm được cung cấp như một array của các CvPoint structure. Điều này cho
phép cvFillPoly() vẽ nhiều polygons trong một lời gọi đơn. Tương tự npts là một array của point
counts, một cho mỗi polygon được vẽ. Nếu biến is_closed được đặt thành true, thì một segment
thêm sẽ được vẽ từ điểm cuối đến điểm đầu cho mỗi polygon. cvFillPoly() là rất mạnh và sẽ
handle các polygon tự cắt, polygons với các lỗ, và các phức tạp khác như thế. Không may, điều
này có nghĩa routine tương đối chậm.
cvFillConvexPoly() làm việc giống cvFillPoly() ngoại trừ rằng nó vẽ một polygon một lúc và có thể
draw chỉ các convex polygon.* Thuận lợi mà là cvFillConvexPoly() chạy nhanh hơn.
Function thứ ba, cvPolyLine(), lấy cùng arguments như cvFillPoly(); tuy nhiên, do chỉ các cạnh
polygon được vẽ, tự giao biểu diễn không sự phức tạp cụ thể. Do đó function này nhanh hơn nf
so với cvFillPoly().
Fonts và Text
Một dạng cuối của vẽ là ái có thể cần là vẽ text. Dĩ nhiên, text tạo tập riêng các phức tạp,
nhưng—như luôn luôn với kiểu này của việc—OpenCV được liên quan nhiều hơn bằng cung cấp
một giải pháp đơn giản “thấp và dirty” mà sẽ làm cho các trường hợp đơn giản cases hơn là một
giải pháp mạnh, phức tạp (mà sẽ là dữ được cho bởi các khả năng của các thư viện khác).
OpenCV có một routine chính, gọi là cvPutText() mà chỉ ném vaiif text lên một image. Text nhận
diện bởi text được in bằng lower-leftcorner của nó của text box ở gốn và theo màu chỉ định bởi
color.
void cvPutText(
CvArr* img,
const char* text,
CvPoint origin,
const CvFont* font,
CvScalar color
);
Luôn luôn có vài thứ mà làm việc của ta phức tạp thêm một tí hơn ta muốn, và trong trường hợp
này việc có mặt của pointer đến CvFont.
Trong một nutshell, cách để lấy CvFont* pointer hợp lệ để gọi function cvInitFont(). Function này
lấy một nhóm các arguments mà câuus hình vài font cụ thể để dùng trên screen. Những cái bạn
quen với GUI programming trong các môi trường khác sẽ tìm thấy cvInitFont() là nhớ lại các
device tương tự nhưng với vài option. Để tạo một CvFont mà ta có thể chuyển vào cvPutText(), ta
phải đầu tiên khai báo một CvFont variable; thì ta có thể chuyển nó vào cvInitFont().
void cvInitFont(
CvFont* font,
int font_face,
double hscale,
double vscale,
double shear = 0,
int thickness = 1,
int line_type = 8
);
Quan sát rằng đây là một khác biệt nhỏ về cách các function dường như tương tự, chẳng hạn
cvCreateImage(), làm việc trong OpenCV. Lời gọi đến cvInitFont() khởi tạo CvFont structure hiện có
(mà có nghĩa rằng bạn tạo variable và chuyển cho cvInitFont() một pointer vào variable bạn tạo).
Đây không giống cvCreateImage(), mà tạo structure cho bạn và trả về một pointer.
Argument font_face là một trong những cái được kê trong Table 3-15 (và mô tả trong Figure 3-6), và
nó có thể tùy chọn được kết hợp (bởi Boolean OR) bằng CV_FONT_ITALIC.
Table 3-15. Available fonts (all are variations of Hershey)
Identifier
Mô tả
CV_FONT_HERSHEY_SIMPLEX
Normal size sanserif
CV_FONT_HERSHEY_PLAIN
Small size sanserif
CV_FONT_HERSHEY_DUPLEX
Normal size sanserif, more complex than
CV_FONT_HERSHEY_SIMPLEX
CV_FONT_HERSHEY_COMPLEX
Normal
CV_FONT_HERSHEY_DUPLEX
CV_FONT_HERSHEY_TRIPLEX
Normal
CV_FONT_HERSHEY_COMPLEX
CV_FONT_HERSHEY_COMPLEX_SMALL
CV_FONT_HERSHEY_COMPLEX
CV_FONT_HERSHEY_SCRIPT_SIMPLEX
CV_FONT_HERSHEY_SCRIPT_COMPLEX
CV_FONT_HERSHEY_SCRIPT_SIMPLEX
size serif, more complex than
size serif, more complex than
Smaller version of
Handwriting style
More complex variant of
Figure 3-6. Tám fonts của Table 3-15 vẽ với hscale = vscale = 1.0, vói gốc của mỗi line được chia theo chiều đứng 30
pixels
Cả hscale và vscale có thể được đặt thành một trong 1.0 hay 0.5 only. Điều này làm font được vẽ ở
height (và width) đủ hay nửa tương ứng với định nghĩa cơ bản của font cụ thể.
Shear function tạo nghiêng cho font; nếu đặt thành 0.0, font không nghiêng. Nó có thể được đặt
lớn bằng 1.0, mà đặt slope của các characters xấp xỉ 45 độ.
Cả thickness và line_type là giống như được định nghĩa cho tất cả các drawing function khác.
Data Persistence
OpenCV cung cấp một cơ chế để serializing và de-serializing các data types khác nhau vào và ra
disk theo một trong YAML hay XML format. Trong chương về HighGUI, mà chuyên về các user
interface function, ta sẽ đề cập các function cụ thể mà lưu và nhớ lại hầu hết object phổ biến:
IplImages (các functions này là cvSaveImage() và cvLoadImage()).
Thêm vào đó, chương HighGUI sẽ thảo luận các read và write functions cụ thể cho movies:
cvGrabFrame(), mà đọc từ file hay từ camera; và cvCreateVideoWriter() và cvWriteFrame(). Trong
phần này, ta sẽ tập tring vào lưu object chung: reading và writing các matrix, OpenCV structures,
và configuration và log files. Đầu tiên ta bắt đầu với các functions cụ thể và thuận lợi mà save và
load các matrix OpenCV. những functions này là cvSave() và cvLoad(). Giả thiết bạn có một 5-by-5
identity matrix (0 mọi nơi ngoại trừ cái đầu trên diagonal). Example 3-15 cho thấy cách hoàn
thành điều này.
Example 3-15. Saving và loading a CvMat
CvMat A = cvMat( 5, 5, CV_32F, the_matrix_data );
cvSave( “my_matrix.xml”, &A );
...
// to load it then in some other program dùng …
CvMat* A1 = (CvMat*) cvLoad( “my_matrix.xml” );
CxCore reference manual chứa toàn bộ phần về data persistence. Điều bạn thực sự cần biết là
data persistence chung trong OpenCV gồm tạo một CvFileStorage structure, như trong Example 316, mà lưu các memory objects trong một tree structure. Bạn có thể tạo và điều structure này bởi
reading từ disk qua cvOpenFileStorage() với CV_STORAGE_READ, hay bạn có thể tạo và open
CvFileStorage qua cvOpenFileStorage() với CV_STORAGE_WRITE để writing và sau đó điền nó dùng
các data persistence function thích hợp. Trên disk, data được lưu theo một XML hay YAML
format.
Example 3-16. CvFileStorage structure; data is accessed by CxCore data persistence functions
typedef struct CvFileStorage
{
... // hidden fields
} CvFileStorage;
Data nội bên trong CvFileStorage tree có thể gồm một tập thứ bâc các scalars, các CxCore objects
(các matrix, sequences, và graphs) và/hoặc user-defined objects. Hãy nói bạn có một
configuration hay logging file. Ví dụ, xem trường hợp của một movie configuration file mà nói cho
ta bao nhiêu frames ta muốn (10), size của chúng là (320 by 240) và mmm 3-by-3 color
conversion matrix mà sẽ được áp dụng. Ta muốn gọi file “cfg.xml” trên disk. Example 3-17 cho
thấy cách làm điều này.
Example 3-17. Writing a configuration file “cfg.xml” to disk
CvFileStorage* fs = cvOpenFileStorage(
“cfg.xml”,
0,
CV_STORAGE_WRITE
);
cvWriteInt( fs, “frame_count”, 10 );
cvStartWriteStruct( fs, “frame_size”, CV_NODE_SEQ );
cvWriteInt( fs, 0, 320 );
cvWriteInt( fs, 0, 200 );
cvEndWriteStruct(fs);
cvWrite( fs, “color_cvt_matrix”, cmatrix );
cvReleaseFileStorage( &fs );
Lưuys vào key function trong ví dụ này. Ta có thể cho tên thành các integer mà ta viết vào
structure dùng cvWriteInt(). Ta có thể tạo một structure tùy ý, dùng cvStartWriteStruct(), mà cũng
được cho một optional name (chuyển 0 hay NULL nếu không có tên). Structure này có hai int mà
không có tên và do đó ta chuyển 0 cho chúng trong name field, sau khi ta dùng cvEndWriteStruct()
để kết thúc viết structure đó. Nếu có nhiều structure hơn, ta Start và End từng cái tương tự; các
structures có thể được kết lại theo độ sâu tùy ý. Ta sau đó dùng cvWrite() để viết ra color
conversion matrix. Trái với thủ tục viết matrix hoàn toàn phức tạp này bằng đơn giản hơn
cvSave() trong Example 3-15. cvSave() function chỉ là một shortcut thuận lợi cho cvWrite() khi bạn
có chỉ một matrix để viết. Khi ta hoàn thành viết data, CvFileStorage handle được giải phóng trong
cvReleaseFileStorage(). Output (ở đây, theo dạng XML) sẽ trong như trong Example 3-18.
Example 3-18. XML version of cfg.xml on disk
<?xml version=“1.0”?>
<opencv_storage>
<frame_count>10</frame_count>
<frame_size>320 200</frame_size>
<color_cvt_matrix type_id=“opencv-matrix”>
<rows>3</rows> <cols>3</cols>
<dt>f</dt>
<data>…</data></color_cvt_matrix>
</opencv_storage>
Ta có thể sau đó đọc configuration file này như được thấy trong Example 3-19.
Example 3-19. Reading cfg.xml from disk
CvFileStorage* fs = cvOpenFileStorage(
“cfg.xml”,
0,
CV_STORAGE_READ
);
int frame_count = cvReadIntByName(
fs,
0,
“frame_count”,
5 /* default value */
);
CvSeq* s = cvGetFileNodeByName(fs,0,“frame_size”)->data.seq;
int frame_width= cvReadInt(
(CvFileNode*)cvGetSeqElem(s,0)
);
int frame_height = cvReadInt(
(CvFileNode*)cvGetSeqElem(s,1)
);
CvMat* color_cvt_matrix = (CvMat*) cvReadByName(
fs,
0,
“color_cvt_matrix”
);
cvReleaseFileStorage( &fs );
Khi đọc, ta mở XML configuration file bằng cvOpenFileStorage() như trong Example 3-19. Ta sau
đó đọc frame_count dùng cvReadIntByName(), mà cho phép một value mặc định được cho nếu
không có số được đọc. Trong trường hợp này mặc định là 5. Ta sau đó lấy structure mà ta đặt
tên “frame_size” dùng cvGetFileNodeByName(). Từ đây, ta đọc hai integer không có tên dùng
cvReadInt(). Tiếp theo ta đọc color conversion matrix có tên dùng cvReadByName().* Lần nữa, trái
với điều này dạng ngắn short cvLoad() trong Example 3-15. Ta có thể dùng cvLoad() nếu ta chỉ có
một matrix để đọc, nhưng ta phải dùng cvRead() nếu matrix được nhúng vào trong một structure
lớn hơn. Cuối cùng, ta release CvFileStorage structure.
List các data persistence function thích hợp kết hợp với CvFileStorage structure được thấy trong
Table 3-16. Xem CxCore manual cho chi tiết hơn.
Table 3-16. Data persistence functions
Function Mô tả
Open và Release
cvOpenFileStorage Opens file storage for reading hay writing
cvReleaseFileStorage Releases data storage
Writing
cvStartWriteStruct Starts writing a new structure
cvEndWriteStruct Ends writing a structure
cvWriteInt Writes integer
cvWriteReal Writes float
cvWriteString Writes text string
cvWriteComment Writes an XML hay YAML comment string
cvWrite Writes một object chẳng hạn a CvMat
cvWriteRawData Writes multiple numbers
cvWriteFileNode Writes file node to một cái khác file storage
Table 3-16. Data persistence functions (continued)
Function Mô tả
Reading
cvGetRootFileNode Gets the top-level nodes of the file storage
cvGetFileNodeByName Finds node in the map hay file storage
cvGetHashedKey trả về a unique pointer for given name
cvGetFileNode Finds node in the map hay file storage
cvGetFileNodeName trả về name of file node
cvReadInt Reads unnamed int
cvReadIntByName Reads named int
cvReadReal Reads unnamed float
cvReadRealByName Reads named float
cvReadString Retrieves text string from file node
cvReadStringByName Finds named file node và trả về its value
cvRead Decodes object và trả về pointer to it
cvReadByName Finds object và decodes it
cvReadRawData Reads multiple numbers
cvStartReadRawData Initializes file node sequence reader
cvReadRawDataSlice Reads data from sequence reader above
Function Mô tả
Reading
cvGetRootFileNode Gets the top-level nodes of the file storage
cvGetFileNodeByName Finds node in the map hay file storage
cvGetHashedKey trả về a unique pointer for given name
cvGetFileNode Finds node in the map hay file storage
cvGetFileNodeName trả về name of file node
cvReadInt Reads unnamed int
cvReadIntByName Reads named int
cvReadReal Reads unnamed float
cvReadRealByName Reads named float
cvReadString Retrieves text string from file node
cvReadStringByName Finds named file node và trả về its value
cvRead Decodes object và trả về pointer to it
cvReadByName Finds object và decodes it
cvReadRawData Reads multiple numbers
cvStartReadRawData Initializes file node sequence reader
cvReadRawDataSlice Reads data from sequence reader above
Integrated Performance Primitives
Intel có một sản phẩm gọi là Integrated Performance Primitives (IPP) library (IPP). Thư viện này
thực sự là một toolbox của các lõi hiệu suất cao để handling multimedia và các tác vụ tiêu thụ
processor khác theo cách mà làm mở rộng việc dùng kiến trúc chi tiết của các processor của họ
(và, một thỏa thuận tệ hại, các processors của các nhà sản xuất khác mà có cùng kiến trúc).
Như được thảo luận trong chương 1, OpenCV thích quan hệ gần với IPP, cả hai ở mức phần
mềm và ở một mức tổ chức bên trong của công ty này. Như một kết quả, OpenCV được thiết kế
để tự động nhận ra sự có mặt của IPP library và tự động “hoàn đổi” các thực hiện hiệu suất thấp
của nhiều chức năng lõi thành các higher-performance counterparts trong IPP. IPP library cho
phép OpenCV lấy các thuận lợi của tối ưu hiệu suất mà đến từ các lệnh SIMD trong một single
processor cũng như từ các kiến trúc multicore hiện đại.
Với những cơ bản này trong tay, ta có thể thực hiện lượng lớn các nhiệm vụ cơ bản. Tiến lên
qua text này, ta sẽ quan sát nhiều khả năng phức tạp hơn của OpenCV, hầu hết tất cả mà được
tạo trên những routines này. Nó sẽ không ngạc nhiên rằng image processing—mà thường đòi hỏi
làm cùng thứ trên lượng lớn data, mà phần lớn là hoàn toàn parallel—mà hiện thực một thuận lợi
lớn từ bất kỳ code mà cho phép nó có thuận lợi của cac đơn vị thực thi song song của bất kỳ
dạng nào (MMX, SSE, SSE2, etc.).
Verifying Installation
Cách kiểm tra và đảm bảo rằng IPP được cài và làm việc thích hợp bằng function
cvGetModuleInfo(), được thấy trong Example 3-20. Function này sẽ nhận cả version của OpenCV
bạn hiện đang chạy và version và nhận bất kỳ add-in modules.
Example 3-20. dùng cvGetModuleInfo() to check for IPP
char* libraries;
char* modules;
cvGetModuleInfo( 0, &libraries, &modules );
printf(“Libraries: %s/nModules: %s/n”, libraries, modules );
Code trong Example 3-20 sẽ tạo các text string mà mô tả các installed libraries và modules.
Output có thể trông như thế này:
Libraries cxcore: 1.0.0
Modules: ippcv20.dll, ippi20.dll, ipps20.dll, ippvm20.dll
Các modules kê trong output này là các IPP modules dùng bởi OpenCV. Các modules này tự
chúng thực sự xử lý cho ngay cả các lower-level CPU-specific libraries. Chi tiết cách nó tất cả
làm việc nằm ngoài phạm vi sách này, nhưng nếu bạn thấy các IPP libraries trong Modules string
thì bạn có thể be pretty confident that mọi thứ is working như mong đợi. Dĩ nhiên, bạn có thể
dùng thông tin này to kiểm tra that IPP is running correctly on your own system. Bạn có thể cũng
dùng it to check for IPP on a machine on which your finished software is installed, perhaps then
making some dynamic adjustments phụ thuộc vào có hay không IPP is available.
Tóm tắt
In chương này ta introduced some basic data structures that ta sẽ often trạm chán. Về cụ thể, ta
met the OpenCV matrix structure và the all-important OpenCV image structure, IplImage. ta
considered both in some detail và found that the matrix và image structures are rất similar: the
functions used for primitive manipulations in one work equally well in the other.
Exercises
In the following exercises, bạn có thể need to refer to the CxCore manual that ships with OpenCV
hay to the OpenCV Wiki on the Web for details of the functions outlined in chương này.
Find và 1. d open .../opencv/cxcore/include/cxtypes.h. Read through và tìm thấy the many
conversion helper functions.
a. , and
then take its ceiling và floor.
b. Generate some random numbers.
c. tạo a floating point CvPoint2D32f và convert it to an integer CvPoint.
d. Convert a CvPointer đến a CvPoint2D32f.
2. This exercise sẽ accustom bạn to the idea of nhiều functions taking matrix types.
Create a two-dimensional matrix with three channels of type byte with data size
100-by-100. Set tất cả the values to 0.
a. Draw a circle in the matrix dùng void cvCircle( CvArr* img, CvPoint center,
intradius, CvScalar color, int thickness=1, int line_type=8, int shift=0 ).
b. Display this image dùng methods described in chương 2.
3. tạo a two-dimensional matrix with three channels of type byte with data
size 100-by-100, và set tất cả the values to 0. dùng the pointer element truy cập function
cvPtr2D to trỏ đến the middle (“green”) channel. Draw a green rectangle between
(20, 5) và (40, 20).
4. tạo a three-channel RGB image of size 100-by-100. Clear it. dùng pointer arithmetic
to draw a green square giữa (20, 5) và (40, 20).
5. Practice dùng region of interest (ROI). tạo a 210-by-210 single-channel byte image
and zero it. With in the image, build a pyramid of increasing values dùng ROI
and cvSet(). That is: the outer border should be 0, the next inner border should be
20, the next inner border should be 40, và so on until the final innermost square is
set to value 200; tất cả borders should be 10 pixels wide. Display the image.
6. dùng multiple image headers for one image. Load một image that is tối thiểu 100-by-100.
Create hai additional image headers và set their origin, depth, number of channels,
and widthstep to be the same as the loaded image. In the new image headers,
set the widthat 20 và the height at 30. Cuối cùng, set their imageData pointers to point
to the pixel at (5, 10) và (50, 60), respectively. Pass những cái này new image subheaders
to cvNot(). Display the loaded image, mà should have hai inverted rectangles
with in the larger image.
7. tạo a mask dùng cvCmp(). Load a real image. dùng cvSplit() to split the image
into red, green, và blue images.
a. tìm thấy và display the green image.
b. Clone this green plane image twice (call những cái này clone1 và clone2).
c. tìm thấy the green plane’s minimum và maximum value.
d. Set clone1’s values to thresh = (unsigned char)((maximum - minimum)/2.0).
e. Set clone2 to 0 và dùng cvCmp(green_image, clone1, clone2, CV_CMP_GE). Now
clone2 sẽ have a mask of where the value exceeds thresh in the green image.
f. e cvSubS(green_image,thresh/2, green_image, clone2) và display the
results.
8. tạo a structure of an integer, a CvPoint và a CvRect; call it “my_struct”.
a. Write hai functions: void write_my_struct( CvFileStorage * fs, const char *
name, my_struct *ms) và void read_my_struct( CvFileStorage* fs, CvFileNode*
ms_node, my_struct* ms ). dùng them to write và read my_struct.
b. Write và read an array of 10 my_struct structures.
Download