Hầu hết ai mà chẳng điều khiển máy tính bằng tay, tay cầm chuột, tay gõ phím, nhưng trong bài viết này sẽ giới thiệu cách điều khiển máy tính bằng tay nhưng không động đến chuột hay bàn phím. Đối với nhiều người thì điều này có lẽ cũng còn khá xa vời, trong tưởng tượng hay trong các bộ phim, 1 viễn cảnh của công nghệ tương lai hay mới chỉ xuất hiện trong các phòng thí nghiệm hay trong 1 số ít sản phẩm công nghệ hiện đại chưa đại chúng. Trong bài này tôi sẽ cho các bạn thấy điều này không phải là cái gì đó trong tưởng tượng hay trong phim ảnh mà bất kỳ ai cũng có thể tự thực hiện được.
Các công cụ cần thiết :
Tiến hành :
Mở Visual Studio và tạo 1 project Visual C++, Win32 Console Application mới. Đặt tên là diendan.go.vn_Control_Computer :
Chọn như các hình sau :
Sau khi tạo project, click chuột phải vào tên project và chọn Properties :
Chọn mục C/C++ và paste dòng sau vào khung Additional Include Directories :
Trong mục Linker --> Input --> Additional Dependencies chọn như sau :
Paste vào các dòng sau :
Nhấn OK :
Click chuột phải vào Source Files --> Add --> New Item... :
Chọn C++ File (.cpp), nhập vào tên main và nhấn Add :
Tiếp theo nháy đúp chuột vào main.cpp và paste toàn bộ đoạn code sau vào :
[spoil]
[/spoil]
Đến đây là đã xong rồi, giờ copy bộ CSLD training vào thư mục chứa project :
Nhấn F5 để chạy chương trình. Thử mở 1 file ppt bất kỳ rồi chạy slide show và điều khiển nó bằng tay xem, sẽ thật thú vị nếu buổi thuyết trình của bạn không hề động đến máy tính phải không.
Vài hình ảnh :
Điều khiển slide bằng tay
Bảng mã bàn phím :
VK_NUMPAD7
|0x67|
VK_BACK
|0x08
VK_NUMPAD8 |0x68| VK_TAB |0x09
VK_NUMPAD9 |0x69| VK_RETURN |0x0D
VK_MULTIPLY |0x6A| VK_SHIFT |0x10
VK_ADD |0x6B| VK_CONTROL |0x11
VK_SEPARATOR |0x6C| VK_MENU |0x12
VK_SUBTRACT |0x6D| VK_PAUSE |0x13
VK_DECIMAL |0x6E| VK_CAPITAL |0x14
VK_DIVIDE |0x6F| VK_ESCAPE |0x1B
VK_F1 |0x70| VK_SPACE |0x20
VK_F2 |0x71| VK_END |0x23
VK_F3 |0x72| VK_HOME |0x24
VK_F4 |0x73| VK_LEFT |0x25
VK_F5 |0x74| VK_UP |0x26
VK_F6 |0x75| VK_RIGHT |0x27
VK_F7 |0x76| VK_DOWN |0x28
VK_F8 |0x77| VK_PRINT |0x2A
VK_F9 |0x78| VK_SNAPSHOT |0x2C
VK_F10 |0x79| VK_INSERT |0x2D
VK_F11 |0x7A| VK_DELETE |0x2E
VK_F12 |0x7B| VK_LWIN |0x5B
VK_NUMLOCK |0x90| VK_RWIN |0x5C
VK_SCROLL |0x91| VK_NUMPAD0 |0x60
VK_LSHIFT |0xA0| VK_NUMPAD1 |0x61
VK_RSHIFT |0xA1| VK_NUMPAD2 |0x62
VK_LCONTROL |0xA2| VK_NUMPAD3 |0x63
VK_RCONTROL |0xA3| VK_NUMPAD4 |0x64
VK_LMENU |0xA4| VK_NUMPAD5 |0x65
VK_RMENU |0xA5| VK_NUMPAD6 |0x66
Các công cụ cần thiết :
- Máy tính có Webcam.
- Microsoft Visual Studio (phiên bản 2008 chẳng hạn) :
- Thư viện về Computer Vision của Intel, ở đây dùng phiên bản OpenCV_1.1pre1a :
- Bộ CSLD training do tôi đã chuẩn bị sẵn (nếu bạn nào quan tâm đến training và mở rộng khả năng học hỏi của máy tính có thể đặt câu hỏi tại đây) :
Chú ý : Để thuận tiện các bạn cài OpenCV vào ngay ổ C:- Microsoft Visual Studio (phiên bản 2008 chẳng hạn) :
Mã:
http://diendan.goonline.vn/showthread.php/528224-Visual-Studio-2008-Team-Suite-Full
Mã:
http://www.mediafire.com/?bxw22fxzvy70q1m
Mã:
http://www.mediafire.com/?skds3nbugbn3f
Tiến hành :
Mở Visual Studio và tạo 1 project Visual C++, Win32 Console Application mới. Đặt tên là diendan.go.vn_Control_Computer :

Chọn như các hình sau :


Sau khi tạo project, click chuột phải vào tên project và chọn Properties :

Chọn mục C/C++ và paste dòng sau vào khung Additional Include Directories :
Mã:
"C:\OpenCV\lib\cv.lib" "C:\OpenCV\lib\cxcore.lib" "C:\OpenCV\lib\highgui.lib"

Trong mục Linker --> Input --> Additional Dependencies chọn như sau :

Paste vào các dòng sau :
Mã:
"C:\OpenCV\lib\cv.lib"
"C:\OpenCV\lib\cxcore.lib"
"C:\OpenCV\lib\highgui.lib"

Nhấn OK :

Click chuột phải vào Source Files --> Add --> New Item... :

Chọn C++ File (.cpp), nhập vào tên main và nhấn Add :

Tiếp theo nháy đúp chuột vào main.cpp và paste toàn bộ đoạn code sau vào :
[spoil]
Mã:
#include "cv.h"
#include "highgui.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <math.h>
#include <float.h>
#include <limits.h>
#include <time.h>
#include <ctype.h>
#ifdef _EiC
#define WIN32
#endif
static CvMemStorage* storage = 0;
static CvHaarClassifierCascade* cascade1 = 0;
static CvHaarClassifierCascade* cascade2 = 0;
static CvHaarClassifierCascade* cascade3 = 0;
static CvHaarClassifierCascade* cascade4 = 0;
int use_nested_cascade = 0;
void detect_and_draw( IplImage* image);
const char* cascade1_name ="Next.xml";
const char* cascade2_name ="Previous.xml";
const char* cascade3_name ="Stop.xml";
const char* cascade4_name ="Play.xml";
double scale = 1;
int main( )
{
CvCapture* capture = 0;
IplImage *frame, *frame_copy = 0;
IplImage *image = 0;
const char* scale_opt = "--scale=";
int scale_opt_len = (int)strlen(scale_opt);
const char* cascade_opt = "--cascade=";
int cascade_opt_len = (int)strlen(cascade_opt);
const char* nested_cascade_opt = "--nested-cascade";
int nested_cascade_opt_len = (int)strlen(nested_cascade_opt);
cascade1 = (CvHaarClassifierCascade*)cvLoad( cascade1_name, 0, 0, 0 );
cascade2 = (CvHaarClassifierCascade*)cvLoad( cascade2_name, 0, 0, 0 );
cascade3 = (CvHaarClassifierCascade*)cvLoad( cascade3_name, 0, 0, 0 );
cascade4 = (CvHaarClassifierCascade*)cvLoad( cascade4_name, 0, 0, 0 );
storage = cvCreateMemStorage(0);
capture = cvCaptureFromCAM(0);
cvNamedWindow( "BACHKIM01", 1 );
if( capture )
{
for(;;)
{
if( !cvGrabFrame( capture ))
break;
frame = cvRetrieveFrame( capture );
if( !frame )
break;
if( !frame_copy )
frame_copy = cvCreateImage( cvSize(frame->width,frame->height),
IPL_DEPTH_8U, frame->nChannels );
if( frame->origin == IPL_ORIGIN_TL )
cvCopy( frame, frame_copy, 0 );
else
cvFlip( frame, frame_copy, 0 );
cvShowImage( "BACHKIM01", frame_copy );
detect_and_draw( frame_copy );
if( cvWaitKey( 10 ) >= 0 )
goto _cleanup_;
}
cvWaitKey(0);
_cleanup_:
cvReleaseImage( &frame_copy );
cvReleaseCapture( &capture );
}
cvDestroyWindow("BACHKIM01");
return 0;
}
void detect_and_draw( IplImage* img )
{
static CvScalar colors[] =
{
{{0,0,255}},
{{0,128,255}},
{{0,255,255}},
{{0,255,0}},
{{255,128,0}},
{{255,255,0}},
{{255,0,0}},
{{255,0,255}}
};
IplImage *gray, *small_img;
gray = cvCreateImage( cvSize(img->width,img->height), 8, 1 );
small_img = cvCreateImage( cvSize( cvRound (img->width/scale),
cvRound (img->height/scale)), 8, 1 );
cvCvtColor( img, gray, CV_BGR2GRAY );
cvResize( gray, small_img, CV_INTER_LINEAR );
cvEqualizeHist( small_img, small_img );
c***earMemStorage( storage );
double t = (double)cvGetTickCount();
CvSeq* faces=0;
int ind=0;
if( cascade1 )
{
faces = cvHaarDetectObjects( small_img, cascade1, storage,
1.2, 3, 0
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_DO_CANNY_PRUNING
//|CV_HAAR_SCALE_IMAGE
,
cvSize(90, 90) );
if(faces->total>0)
{
printf( "Next\n");
ind=1;
}
if(faces->total==0)
{
faces = cvHaarDetectObjects( small_img, cascade2, storage,
1.2, 3, 0
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_DO_CANNY_PRUNING
//|CV_HAAR_SCALE_IMAGE
,
cvSize(90, 90));
{
printf( "Previous\n");
ind=2;
}
}
if(faces->total==0)
{
faces = cvHaarDetectObjects( small_img, cascade3, storage,
1.2, 3, 0
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_DO_CANNY_PRUNING
//|CV_HAAR_SCALE_IMAGE
,
cvSize(90, 90) );
if(faces->total>0)
{
printf( "Stop\n");
ind=3;
}
}
if(faces->total==0)
{
faces = cvHaarDetectObjects( small_img, cascade4, storage,
1.2, 3, 0
//|CV_HAAR_FIND_BIGGEST_OBJECT
//|CV_HAAR_DO_ROUGH_SEARCH
|CV_HAAR_DO_CANNY_PRUNING
//|CV_HAAR_SCALE_IMAGE
,
cvSize(90, 90) );
if(faces->total>0)
{
printf( "Play\n");
ind=4;
}
}
}
t = (double)cvGetTickCount() - t;
printf( "detection time = %gms\n", t/((double)cvGetTickFrequency()*1000.) );
for( int i = 0; i < (faces ? faces->total : 0); i++ )
{
CvRect* r = (CvRect*)cvGetSeqElem( faces, i );
CvMat small_img_roi;
CvSeq* nested_objects;
CvPoint pt1,pt2;
CvScalar color = colors[i%8];
int radius;
pt1.x=cvRound(r->x * scale);
pt1.y=cvRound(r->y * scale);
pt2.x=cvRound((r->x + r->width) * scale);
pt2.y=cvRound((r->y + r->height) * scale);
cvRectangle(img, pt1,pt2,CV_RGB(255, 0, 0), 3, 8, 0);
CvFont font;
cvInitFont(&font,CV_FONT_HERSHEY_PLAIN, 2.0,2.0, 0,2, CV_AA);
switch(ind)
{
case 1: {cvPutText(img, "Right!", cvPoint(pt1.x +40,pt1.y+40), &font, CV_RGB(255,0,0));
keybd_event(0x27, 0, 0, 0); //gửi thông điệp phím right arrow bar được nhấn đến hđh
Sleep(1500);
}
break;
case 2: {cvPutText(img, "Left!", cvPoint(pt1.x+40,pt1.y+40), &font, CV_RGB(255,0,0));
keybd_event (0x25, 0, 0, 0); //gửi thông điệp phím left arrow bar được nhấn đến hđh
Sleep(1500);
}
break;
case 3: {cvPutText(img, "Stop!", cvPoint(pt1.x+40,pt1.y+40), &font, CV_RGB(255,0,0));
//Thêm bất kỳ thông điệp bàn phím nào vào đây
//Sleep(1500);
}
break;
case 4: {cvPutText(img, "Up!", cvPoint(pt1.x+40,pt1.y+40), &font, CV_RGB(255,0,0));
//Thêm bất kỳ thông điệp bàn phím nào vào đây
//Sleep(1500);
}
break;
}
printf("%d - %d\n",r->width,r->height);
}
cvShowImage( "BACHKIM01", img );
cvReleaseImage( &gray );
cvReleaseImage( &small_img );
}

Vài hình ảnh :





Điều khiển slide bằng tay
Bảng mã bàn phím :
VK_NUMPAD8 |0x68| VK_TAB |0x09
VK_NUMPAD9 |0x69| VK_RETURN |0x0D
VK_MULTIPLY |0x6A| VK_SHIFT |0x10
VK_ADD |0x6B| VK_CONTROL |0x11
VK_SEPARATOR |0x6C| VK_MENU |0x12
VK_SUBTRACT |0x6D| VK_PAUSE |0x13
VK_DECIMAL |0x6E| VK_CAPITAL |0x14
VK_DIVIDE |0x6F| VK_ESCAPE |0x1B
VK_F1 |0x70| VK_SPACE |0x20
VK_F2 |0x71| VK_END |0x23
VK_F3 |0x72| VK_HOME |0x24
VK_F4 |0x73| VK_LEFT |0x25
VK_F5 |0x74| VK_UP |0x26
VK_F6 |0x75| VK_RIGHT |0x27
VK_F7 |0x76| VK_DOWN |0x28
VK_F8 |0x77| VK_PRINT |0x2A
VK_F9 |0x78| VK_SNAPSHOT |0x2C
VK_F10 |0x79| VK_INSERT |0x2D
VK_F11 |0x7A| VK_DELETE |0x2E
VK_F12 |0x7B| VK_LWIN |0x5B
VK_NUMLOCK |0x90| VK_RWIN |0x5C
VK_SCROLL |0x91| VK_NUMPAD0 |0x60
VK_LSHIFT |0xA0| VK_NUMPAD1 |0x61
VK_RSHIFT |0xA1| VK_NUMPAD2 |0x62
VK_LCONTROL |0xA2| VK_NUMPAD3 |0x63
VK_RCONTROL |0xA3| VK_NUMPAD4 |0x64
VK_LMENU |0xA4| VK_NUMPAD5 |0x65
VK_RMENU |0xA5| VK_NUMPAD6 |0x66