2.2.1 Dò tìm cực trị cục bộ
Như đã nêu ở trên, bước đầu tiên sẽ tìm các điểm tiềm năng có thể trở thành điểm đặc trưng bằng phương pháp lọc theo tầng dựa vào việc thay đổi tham số bộ lọc Gaussisan. Trong bước này, ta cần dò tìm các vị trí và các số đo (kích cỡ) mà chúng bất biến trong các khung nhìn khác nhau của cùng một đối tượng. Các vị trí đó bất biến về số đo có thể được dò tìm bằng cách tìm kiềm các đặc trưng ổn định trên toàn bộ các số đo có thể, sử dụng một hàm liên tục về số đo vốn rất nổi tiếng có tên là hàm không gian đo (Witkin 1983).
Theo các công bố của Koenderink (1984) và Lindeberg(1994) thì hàm Gaussian là hàm tốt nhất để biễu diễn không gian đo của ảnh 2 chiều. Vì vậy, không gian đo của một ảnh sẽ được định nghĩa như là một làm L(x,y,ó) được tạo ra bằng cách nhân chập ảnh gốc I(x,y) với môt hàm Gaussian G(x,y,ó) có tham số về số đo ó thay đổi.
Trong đó toán hạng * là phép nhân chập các ma trận 2 chiều x,y. Và G(x,y, ó) hàm Gaussian:
Để tìm những điểm đặc trưng có tính bất biến cao, thuật toán được sử dụng là tìm cực trị cục bộ của hàm sai khác DoG (Difference-of-Gaussian), kí hiệu là D(x,y,ó ). Hàm này được tính toán từ sự sai khác giữa 2 không gian đo cạnh nhau của một ảnh với tham số đo lệch nhau một hằng số k.
Các lý do lựa chọn hàm Gaussian là vì nó là kỹ thuật rất hiệu quả để tính toán L (cũng như làm tăng độ mịn của ảnh), mà L thì luôn phải được tính rất nhiều để mô tả đặc trưng trong không gian đo, và sau đó, D sẽ được tính một cách đơn giản chỉ với phép trừ ma trận điểm ảnh với chi phí thực hiện thấp.
Hình 2.2. Quá trình tính không gian đo (L) và hàm sai khác D
Hơn nữa, hàm sai khác DoG có thể được sử dụng để tạo ra một sự xấp xỉ gần với đạo hàm bậc hai Laplace có kích thước chuẩn của hàm Gaussian (ó2V2G) do tác giả Lindeberg đề xuất năm 1994. Ông đã chỉ ra rằng việc chuẩn hóa đạo hàm bậc hai với hệ số ó2 là cần thiết cho bất biến đo trở nên đúng. Cụ thể, ông đã công bố rằng các giá trị cực đại và cực tiểu của ó2V2G chính là những giá trị có tính ổn định nhất (bất biến cao) so với một loạt các hàm đánh giá khác như:gradient, Hessian hay Harris.
Do đó:
Từ công thức này, ta thấy khi mà hàm sai khác DoG được tính toán tại các tham số đo lệch nhau một hằng số k, thì ta có thể sử dụng DoG để xấp xỉ đạo hàm bậc hai Laplace của Gaussian. Vì hệ số (k-1) trong phương trình trên là hằng số trong mọi không gian đo nên nó sẽ không ảnh hưởng đến việc tìm các vị trí cực trị. Sai số trong việc xấp xỉ đạo hàm bậc 2 tiến về 0 khi k gần với 1. Tuy nhiên, các kết quả thử nghiệmcủa tác giả cho thấy quá trình xấp xỉ đạo hàm không ảnh hưởng đến việc dò tìm các vị trí cực trị thậm chí ngay cả khi chọn k khá xa, ví dụ
Sau khi áp dụng hàm DoG ta thu được các lớp kết quả khác nhau (scale) từ ảnh gốc, bước tiếp theo là tìm các cực trị trong các lớp kết quả theo từng miền cục bộ. Cụ thể là tại mỗi điểm trên các lớp kết quả sẽ được so sánh với 8 điểm lân cận trên cùng lớp và 9 điểm lân cận trên mỗi lớp khác (hình dưới).
Hình 2.3 Quá trình tìm điểm cực trị trong các hàm sai khác DoG
Trong hình trên: điểm đánh dẫu x sẽ được so sánh với 26 điểm lân cận (đánh dấu vòng tròn xanh). Điểm này sẽ được lấy làm điểm tiềm năng (điểm có thể làm điểm đặc biệt - candidate keypoint) nếu nó có giá trị lớn nh ất hoặc nhỏ nhất so với 26 điểm lân cận như trên. Giải pháp cho việc tìm các điểm tiềm năng này là sử dụng thuật toán blob detection (dò tìm điểm) do Lindeberg đề xuất.
Vì số lượng các cực trị là rất lớn, vì vậy để tăng sự hiệu quả khi dò tìm các điểm cực trị (dò các điểm cực trị tốt nhất thay vì phải dò hết), ta cần xác định tần số lấy mẫu trong không gian đo và tần số lấy mẫu trong không gian quan sát (không gian ảnh). Thật không may là ta không thể xác định cả 2 loại tần số này một cách động trong mỗi tiến trình dò tìm. Thay vì vậy, các tần số này sẽ được xác định offline thông qua phương pháp thử nghiệm. Sau khi thử nghiệm với nhiều nguồn dữ liệu ảnh khác nhau, tác giả đã chỉ ra tần số lấy mẫu trong không gian đo tốt nhất là 3 (giữ lại 3 lớp Như vậy, V2G có thể được tính thông qua việc xấp xỉ sự sai khác hữu hạn ỒG, dơtạj các tham số đo gần nhau kó và ó:
trong mỗi bộ 8 lớp), và tần số lấy mẫu ó = 1.6.
2.2.2 Trích xuất keypoint
Sau bước 1 sẽ thu được rất nhiều điểm tiề m năng có thể làm điểm đặc biệt, tuy nhiên một số trong chúng là không cần thiết. ở bước tiếp theo này sẽ loại bỏ các điểm có độ tương phản kém (nhạy cảm với nhiễu) hoặc tính đặc trưng cục bộ ít hơn các điểm khác hoặc có xu hướng là đường biên đối tượng. Bước thực hiện này gồm 3 công đoạn:
a. Phép nội suy lân cận cho vị trí đúng của điểm tiềm năng:
Phép nội suy lân cận () sử dụng mở rộng Taylor (Taylor expansion) cho hàm Difference-of-Gaussian D(x,y,ó):
Trong đó: D và đạo hàm của nó được tính tại một điểm tiềm năng và X = (x,y,ó) là khoảng cách từ điểm đó. Vị trí của điểm cực trị Xđược xác định bằng cách lấy đạo hàm của hàm trên với đối số X và tiến dần đến 0:
Hình 2.4: Mô phỏng sử dụng công thức mở rộng của Taylor cho hàm DoG
Nếu X > 0.5 theo một chiều nào đó thì nó có chỉ số cực trị không gần với các điểm tiềm năng khác, nó sẽ bị thay đổi và phép nội suy sẽ thay thế vai trò của nó bằng điểm khác gần nó.
Hình 2.5. Minh họa các bước của quá trình lựa chọn các điểm keypoints. (a) là ảnh gốc. (b) mô tả 832 điểm keypoints tìm được, các điểm keypoints được vẽ ở dạng một vector thể hiện 3 thông tin: vị trí, hướng và độ dài. (c) sau khi đặt ngưỡng tương phản tổi thiểu, ta giữ lại được 729 điểm. (d) Giữ lại 536 điểm sau khi áp một ngưỡng nữa về hệ số độ cong.
b. Loại trừ các điểm có tính tương phản kém:
Các điểm nhạy cảm với độ sáng và nhiễu thì không được trở thành điểm đặc biệt và cần loại bỏ khỏi danh sách điểm tiềm năng. Trong khai triển Taylor mở rộng ở trên, nếu điểm tiềm năng nào có giá trị X < 0.03 thì điểm đó sẽ bị loại, ngược lại thì nó được giữ lại theo vị trí mới (y+x) và tùy biến ó, với y là vị trí cũ của nó cùng giá trị biến ó.
c. Loại bỏ các điểm dư thừa theo biên:
Sử dụng hàm DoG sẽ cho tác động mạnh đến biên khi vị trí của biên là khó xác định và vì vậy các điểm tiềm năng trên biên sẽ không bất biến và bị nhiễu. Và để tăng sự ổn định cho các điểm sẽ được chọn làm điểm đặc biệt ta sẽ loại trừ các điểm tiềm năng khó định vị (tức là vị trí dễ thay đổi khi có nhiễu do nằm ở biên).
Sau khi áp dụng hàm DoG sẽ làm đường biên ảnh không rõ ràng và độ cong chính sẽ có giá trị lớn hơn nhiều so với độ cong dọc theo biên vì vậy cần loại bỏ bớt các điểm đặc biệt dọc theo cùng một biên. Giải pháp cho việc này là sử dụng giá trị của ma trận Hessian cấp 2:
(2.2.3)
Các giá trị riêng của H tỉ lệ thuận với độ cong của D, các giá trị riêng â (giá trị nhỏ) và á (giá trị lớn) có tỉ lệ r = á/â sẽ được sử dụng. Các phần tử của H là Dxx và Dyy
2.2.3 Gắn hướng cho các keypoint
Bằng việc gán một hướng cho mỗi điểm đặc trưng keypoint dựa vào các thuộc tính ảnh cục bộ, bộ mô tả keypoint có thể được biễu diễn tương đối so với hướng này và do đó đặt được tính bất biến đối với các hiện tượng quay ảnh. Cách tiếp cận này ngược lại với các bộ mô tả bất biến hướng của Schmid (1997) ở chỗ mỗi thuộc tính ảnh sẽ dựa vào một độ đo bất biến về hướng. Nhược điểm của cách tiếp cận này đó là nó giới hạn số lượng các bộ mô tả được sử dụng và bỏ qua các thông tin về ảnh bởi vì nó không yêu cầu mọi độ đo đều phải dựa trên một hướng nhất quán.
Sau đây là kỹ thuật gán hướng cục bộ cho các điểm đặc trưng. Độ đo của các điểm đặc trưng được sử dụng để tìm ra một ảnh đã lọc Gaussian L với kích thước gần nhất sao cho mọi tính toán sẽ được thực hiện trong cùng một cách bất biến về độ đo. Với mỗi mẫu ảnh L(x,y) này, gọi m(x,y) là biên độ gradient, 9 (x,y) là hướng. Hai giá trị cuối được tính toán như sau:
2.2.4 Tạo bộ mô tả cục bộ
Các phép xử lý trên đây đã thực hiện dò tìm và gán tọa độ, kích thước, và hướng cho mỗi điểm đặc trưng keypoint. Các tham số đó yêu cầu một hệ thống tọa độ địa phương 2D có thể lặp lại được để mô tả vùng ảnh địa phương và nhờ vậy tạo ra sự bất biến đối với các tham số đó. Bước tiếp theo đây sẽ tính toán một bộ mô tả cho môt vùng ảnh địa phương mà có tính đặc trưng cao (bất biến với các thay đổi khác nhau về độ sáng, thu - phóng ảnh, xoay).
Một cách tiếp cận đơn giản đó là lấy mẫu mật độ ảnh cục bộ lân cận điểm đặc trưng ở một độ đo thích hợp, và đối sánh các mật độ này sử dụng độ đo tương quan chuẩn. Tuy nhiên, hê số tương quan đơn giản thì lại rất nhạy cảm với sự thay đổi mà gây ra sự đăng ký nhầm các mẫu, chẳng hạn nh ư các biến đổi Affine, phối cảnh 3D, hoặc bóp méo mềm. Cách tiếp cận tốt hơn nhiều được đưa ra bởi Edelman, Intrator và Poggio (1997). Cách tiếp cận này dựa trên một mô hình thị giác sinh học, cụ thể là mô hình noron phức tạp trong hệ thống não bộ. Các noron sẽ tương ứng với một gradient tại một hướng và tần số không gian cụ thể, như ng vị trí của gradient trên võng mạc được phép trượt trên một phạm vi nhỏ của khung nhìn. Dựa trên cách tiếp cận này, tác giả đã cài đặt bộ mô tả mới trong đó cho phép việc trượt vị trí sử dụng một cách tính toán khác.
Hình sau mô phỏng quá trình tính toán các bộ mô tả theo cách tiếp cận mới.
Ảnh trái là mô phỏng biên độ gradient và h ướng tại m ỗi mẫu ảnh trong một vùng lân cận với điểm keypoint. Các giá trị đó tập trung trong một cửa sổ gaussian (nằm bên trong vòng tròn). Các mẫu này sau đó được gom lại thành một lược đồ hướng mô tả vắn tắt nội dung trong 4x4 vùng con như được mô tả ở bên phải với độ dài của mỗi hàng tương ứng với tổng biên độ gradient gần hướng đó bên trong một vùng.
Keypoint descriptor
Hình2.6: Mô tả tạo bộ mô tả cục bộ
2.3 Mô tả xây dựng chương trình xác định vị trí đối tượng2.3.1 Phương pháp thực hiện 2.3.1 Phương pháp thực hiện
Xây dựng chương trình phát hiện và định vị trí đối tượng bằng cách sử dụng các kĩ thuật xử lý ảnh. Chương trình thực hiện tìm đối tượng, nếu tìm thấy sẽ ra lệnh điều khiển thiết bị bên dưới thông qua cổng RS32 tiến lại gần vật.
Trong chương trình chúng tôi sử dụng thư viện OpenCV trong xử lý ảnh và trích xuất keypoint của đối tượng bằng thuật toán SIFT. Sau đó chiết xuất trích lọc keypoint và descriptor của từng frame hình được định vị. Tiếp theo chương trình sẽ so sánh các điểm keypoint lần cận để tìm thấy những cặp keypoint phù hợp nhất. Và nếu chúng phù hợp với nhau thì sẽ trả về tọa độ x,y của đối tượng. Với phương pháp này, chúng tôi chia màn hình ra làm 9 phần tương ứng như hình sau:
Hình 2.7. Mô tả việc chia màn hình ra làm 9 phần để định vị vị trí đối tượng
Phần điều khiển thiết bị bến dưới sử dụng các thuật toán điểu khiển thiết bị được mã hóa sao cho robot di chuyển đến đúng tọa độ của đối tượng được phát hiện là khu vực số 5. Nếu tọa độ phát hiện trong khu vực 6, thì robot sẽ di chuyển sang bên phải, đối tượng sẽ xuất hiện trong khu vực 5. Nếu đối tượng rơi vào khu vực 2, robot sẽ di chuyển về phía trước để đưa đối tượng rơi vào khu vực 5. Khi đối tượng lọt vào khu vực 5, nếu robot có cách tay bắt lấy đối tượng thì cách tay sẽ đưa ra kẹp lấy đối tượng. Khi đó, robot sẽ di chuyển về phía trước để kẹp chặc đối tượng. Trong lúc robot tiến về phía trước sẽ sử dụng kinect để đo khoảng cách đến đối tượng sao cho khoảng cách đúng với thiết kết của cánh tay robot để bắt lấy đối tượng.
Các tính hiệu điều khiển từ máy tính sẽ gửi đến các vi điều khiển AVR thông qua cổng USB hoặc cổng serial RS32 với thiết bị kết nối tiếp vào interface. Sử dụng lệnh “echo” thông qua lời gọi hệ thống trong C vào cổng nối tiếp ttyUSB0. Các vi điều khiển AVR, khi nhận được lệnh từ máy tính, các động cơ và cánh tay sẽ hoạt động.
2.3.2 Mã chương trình nhận dạng đối tượng
#include <stdlib.h> #include <stdio.h> #include <math.h> #include <string.h> #include <stdio.h> #include "libfreenect.h"
#include "libfreenect_sync.h" //#include "libfreenect_cv.h" #include <opencv2/objdetect/objdetect.hpp> #include <opencv2/features2d/features2d.hpp> #include <opencv2/highgui/highgui.hpp> #include <opencv2/calib3d/calib3d.hpp> #include <opencv2/imgproc/imgproc_c.h> #include <opencv2/video/tracking.hpp> #include <iostream> #include <vector> using namespace std; IplImage *image = 0;
double compareSURFDescriptors( const float* d1, const float* d2, double best, int length )
{
double total_cost = 0; assert( length % 4 == 0 ); for( int i = 0; i < length; i += 4 ) {
double t0 = d1[i] - d2[i]; double t1 = d1[i+1] - d2[i+1]; double t2 = d1[i+2] - d2[i+2]; double t3 = d1[i+3] - d2[i+3];
total_cost += t0*t0 + t1*t1 + t2*t2 + t3*t3; if( total_cost > best )
break; }
return total_cost; }
int naiveNearestNeighbor( const float* vec, int laplacian, const CvSeq* model_keypoints, const CvSeq* model_descriptors )
{
int length = (int)(model_descriptors->elem_size/sizeof(float)); int i, neighbor = -1;
double d, dist1 = 1e6, dist2 = 1e6; CvSeqReader reader, kreader;
cvStartReadSeq( model_keypoints, &kreader, 0 ); cvStartReadSeq( model_descriptors, &reader, 0 );
for( i = 0; i < model_descriptors->total; i++ ) {
const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr; const float* mvec = (const float*)reader.ptr;
CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader ); CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader ); if( laplacian != kp->laplacian )
continue;
d = compareSURFDescriptors( vec, mvec, dist2, length ); if( d < dist1 ) { dist2 = dist1; dist1 = d; neighbor = i; } else if ( d < dist2 ) dist2 = d; } if ( dist1 < 0.6*dist2 ) return neighbor; return -1; }
void findPairs( const CvSeq* objectKeypoints, const CvSeq* objectDescriptors, const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, vector<int>& ptpairs ) {
int i;
CvSeqReader reader, kreader;
cvStartReadSeq( objectKeypoints, &kreader ); cvStartReadSeq( objectDescriptors, &reader ); ptpairs.clear();
for( i = 0; i < objectDescriptors->total; i++ ) {
const CvSURFPoint* kp = (const CvSURFPoint*)kreader.ptr; const float* descriptor = (const float*)reader.ptr;
CV_NEXT_SEQ_ELEM( kreader.seq->elem_size, kreader ); CV_NEXT_SEQ_ELEM( reader.seq->elem_size, reader );
int nearest_neighbor = naiveNearestNeighbor( descriptor, kp->laplacian, imageKeypoints, imageDescriptors );
if( nearest_neighbor >= 0 ) { ptpairs.push_back(i); ptpairs.push_back(nearest_neighbor); } } }
/* a rough implementation for object location */
int locatePlanarObject( const CvSeq* objectKeypoints, const CvSeq*
objectDescriptors, const CvSeq* imageKeypoints, const CvSeq* imageDescriptors, const CvPoint src_corners[4], CvPoint dst_corners[4] )
{ double h[9]; CvMat _h = cvMat(3, 3, CV_64F, h); vector<int> ptpairs; vector<CvPoint2D32f> pt1, pt2; CvMat _pt1, _pt2; int i, n;
findPairs( objectKeypoints, objectDescriptors, imageKeypoints, imageDescriptors, ptpairs ); n = (int)(ptpairs.size()/2); if( n < 4 ) return 0; pt1.resize(n); pt2.resize(n); for( i = 0; i < n; i++ ) { pt1[i] = ((CvSURFPoint*)cvGetSeqElem(objectKeypoints,ptpairs[i*2]))->pt; pt2[i] = ((CvSURFPoint*)cvGetSeqElem(imageKeypoints,ptpairs[i*2+1]))->pt; } _pt1 = cvMat(1, n, CV_32FC2, &pt1[0] ); _pt2 = cvMat(1, n, CV_32FC2, &pt2[0] );
if( !cvFindHomography( &_pt1, &_pt2, &_h, CV_RANSAC, 5 )) return 0;
for( i = 0; i < 4; i++ ) {
double Z = 1./(h[6]*x + h[7]*y + h[8]); double X = (h[0]*x + h[1]*y + h[2])*Z; double Y = (h[3]*x + h[4]*y + h[5])*Z;
dst_corners[i] = cvPoint(cvRound(X), cvRound(Y)); }
return 1; }
//////////
IplImage *freenect_sync_get_depth_cv(int index) {
static IplImage *image = 0; static char *data = 0;
if (!image) image = cvCreateImageHeader(cvSize(640,480), 16, 1); unsigned int timestamp;
if (freenect_sync_get_depth((void**)&data, ×tamp, index, FREENECT_DEPTH_11BIT))
return NULL;
cvSetData(image, data, 640*2); return image;
}
IplImage *freenect_sync_get_rgb_cv(int index) {
static IplImage *image = 0; static char *data = 0;
if (!image) image = cvCreateImageHeader(cvSize(640,480), 8, 3); unsigned int timestamp;
if (freenect_sync_get_video((void**)&data, ×tamp, index, FREENECT_VIDEO_RGB)) return NULL; cvSetData(image, data, 640*3); return image; } int ConnectKinect(void) { while (cvWaitKey(10) < 0) {
IplImage *image = freenect_sync_get_rgb_cv(0); if (!image) {
return -1; }
cvCvtColor(image, image, CV_RGB2BGR);
IplImage *depth = freenect_sync_get_depth_cv(0); if (!depth) {
printf("Error: Kinect not connected?\n"); return -1; } cvShowImage("RGB", image); //cvShowImage("Depth", GlViewColor(depth));