2 vkeyb - camera motion detection virtual keyboard
3 Copyright (C) 2012 Eleni Maria Stea <elene.mst@gmail.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
24 #define NUM_FEATURES 400
25 #define MHI_DURATION 1000
28 static unsigned long get_msec();
30 bool stop_capture = false;
37 if(pipe(pipefd) == -1) {
38 perror("failed to create synchronization pipe");
42 int res = pthread_create(&ptd, 0, capture_thread, 0);
44 fprintf(stderr, "Failed to create capturing thread: %s\n", strerror(res));
50 void *capture_thread(void *arg)
52 cv::VideoCapture cap(0);
54 fprintf(stderr, "failed to open video capture device\n");
57 cv::Mat next_frm, prev_frm, frm8b, prev_frm8b, colimg;
60 cv::flip(next_frm, next_frm, 1);
61 colimg = next_frm.clone();
62 cv::cvtColor(next_frm, next_frm, CV_RGB2GRAY);
63 next_frm.convertTo(frm8b, CV_8UC1);
65 while(!stop_capture) {
66 prev_frm = next_frm.clone();
67 prev_frm.convertTo(prev_frm8b, CV_8UC1);
70 cv::flip(next_frm, next_frm, 1);
71 colimg = next_frm.clone();
72 cv::cvtColor(next_frm, next_frm, CV_RGB2GRAY);
73 next_frm.convertTo(frm8b, CV_8UC1);
75 double direction = calculate_motion_dir(frm8b, prev_frm8b, colimg);
76 write(pipefd[1], &direction, sizeof direction);
82 double calculate_motion_dir(cv::Mat &frm8b, cv::Mat &prev_frm8b, cv::Mat &colimg)
84 std::vector<cv::Point2f> prev_corners;
85 std::vector<cv::Point2f> corners;
86 std::vector<unsigned char> status;
87 std::vector<float> err;
89 cv::goodFeaturesToTrack(prev_frm8b, prev_corners, (float)NUM_FEATURES, 0.01, 3.0);
90 cv::goodFeaturesToTrack(frm8b, corners, (float)NUM_FEATURES, 0.01, 3.0);
91 cv::calcOpticalFlowPyrLK(prev_frm8b, frm8b, prev_corners, corners, status, err);
93 cv::Point motion_vector = cv::Point((int)((double)colimg.cols / 2.0), (int)((double)colimg.rows / 2.0));
95 for(size_t i=0; i<status.size(); i++) {
100 p.x = prev_corners[i].x;
101 p.y = prev_corners[i].y;
106 double angle = atan2((double)(q.y - p.y), (double)(q.x - p.x));
107 double hypotenuse = sqrt(pow((double)(q.y - p.y), 2.0) + pow((double)(q.x - p.x), 2.0));
109 p.x = (int)(q.x - hypotenuse * cos(angle));
110 p.y = (int)(q.y - hypotenuse * sin(angle));
112 cv::line(colimg, q, p, cv::Scalar(255, 255, 0), 1, CV_AA, 0);
115 cv::Point vec2d = cv::Point(q.x - p.x, q.y - p.y);
116 motion_vector = cv::Point(motion_vector.x + vec2d.x, motion_vector.y + vec2d.y);
120 q.x = (int)(p.x + cos(angle) + M_PI / 4.0);
121 q.y = (int)(p.y + sin(angle) + M_PI / 4.0);
123 cv::line(colimg, q, p, cv::Scalar(0, 0, 255), 1, CV_AA, 0);
125 q.x = (int)(p.x + cos(angle) - M_PI / 4.0);
126 q.y = (int)(p.y + sin(angle) - M_PI / 4.0);
128 cv::line(colimg, q, p, cv::Scalar(255, 0, 0), 1, CV_AA, 0);*/
132 cv::Point ctr = cv::Point((int)((double)colimg.cols / 2.0), (int)((double)colimg.rows / 2.0));
133 cv::Point xproj = cv::Point(motion_vector.x, ctr.y);
135 cv::line(colimg, motion_vector, ctr, cv::Scalar(255, 0, 0), 3, CV_AA, 0);
136 cv::line(colimg, xproj, ctr, cv::Scalar(0, 0, 255), 3, CV_AA, 0);
138 return (xproj.x - ctr.x);
141 double calculate_orientation(cv::Mat &frm, cv::Mat &prev_frm)
144 cv::absdiff(frm, prev_frm, silhouette);
147 cv::cvtColor(silhouette, silhouette, CV_RGB2GRAY);
148 silhouette.convertTo(sil8, CV_8UC1);
150 double max_val, min_val;
151 cv::minMaxLoc(sil8, &min_val, &max_val);
153 cv::threshold(sil8, sil8, 119, max_val, CV_THRESH_BINARY);
156 sil8.convertTo(mhi, CV_32FC1, 1.0 / (float)UCHAR_MAX);
158 cv::Mat mask = cv::Mat(mhi.size().width, mhi.size().height, CV_8UC1);
159 cv::Mat orientation = mhi.clone();
161 double duration = MHI_DURATION;
162 unsigned long timestamp = get_msec() + duration;
164 double min_delta = abs(timestamp);
165 double max_delta = abs(timestamp) + 500.0;
167 cv::updateMotionHistory(sil8, mhi, timestamp, duration);
168 cv::calcMotionGradient(mhi, mask, orientation, min_delta, max_delta, 3);
170 double global_orientation = cv::calcGlobalOrientation(orientation, mask, mhi, timestamp, duration);
172 return global_orientation;
176 static unsigned long get_msec()
178 static struct timeval tv0;
181 gettimeofday(&tv, 0);
182 if(tv0.tv_sec == 0 && tv0.tv_usec == 0) {
186 return (tv.tv_sec - tv0.tv_sec) * 1000 + (tv.tv_usec - tv0.tv_usec) / 1000;