A utility class which tracks the movement of hands. The camera image is the input.
The output can be used for hand gesture analysis.
Revisión | 4c3b6090dc4dcdc87c090b1adf058c23585664b3 (tree) |
---|---|
Tiempo | 2013-07-28 23:13:39 |
Autor | tkawata <takuji.kawata@gmai...> |
Commiter | tkawata |
[NDNGestureVision]
- Minor improvement for hand tracking.
- Minor code refactoring. Changed all tab spaces to space characters.
@@ -31,11 +31,11 @@ | ||
31 | 31 | NDNGestureVision::NDNGestureVision(int imageWidth, int imageHeight, int scanningStep, float minFlowThreshold, int numOfFlows) |
32 | 32 | : m_imageWidth(imageWidth), m_imageHeight(imageHeight), m_scanningStep(scanningStep), m_numOfFlows(numOfFlows) |
33 | 33 | { |
34 | - assert(imageWidth > 0 && imageHeight > 0 && scanningStep > 0); | |
34 | + assert(imageWidth > 0 && imageHeight > 0 && scanningStep > 0); | |
35 | 35 | |
36 | - m_scanningXMax = imageWidth / scanningStep; | |
37 | - m_scanningYMax = imageHeight / scanningStep; | |
38 | - m_mapSize = m_scanningXMax * m_scanningYMax; | |
36 | + m_scanningXMax = imageWidth / scanningStep; | |
37 | + m_scanningYMax = imageHeight / scanningStep; | |
38 | + m_mapSize = m_scanningXMax * m_scanningYMax; | |
39 | 39 | m_map = cv::Mat(m_scanningYMax, m_scanningXMax, CV_8U); |
40 | 40 | m_minFlowThresholdP2 = minFlowThreshold * minFlowThreshold; |
41 | 41 | m_tempSortedFlowNums = new int[numOfFlows * 2]; |
@@ -65,13 +65,13 @@ NDNGestureVision::NDNGestureVision(int imageWidth, int imageHeight, int scanning | ||
65 | 65 | NDNGestureVision::~NDNGestureVision() |
66 | 66 | { |
67 | 67 | if (m_tempSortedFlowNums) |
68 | - { | |
69 | - delete [] m_tempSortedFlowNums; | |
70 | - } | |
71 | - if (m_tempSortedFlowExtents) | |
72 | - { | |
73 | - delete [] m_tempSortedFlowExtents; | |
74 | - } | |
68 | + { | |
69 | + delete [] m_tempSortedFlowNums; | |
70 | + } | |
71 | + if (m_tempSortedFlowExtents) | |
72 | + { | |
73 | + delete [] m_tempSortedFlowExtents; | |
74 | + } | |
75 | 75 | } |
76 | 76 | |
77 | 77 | const NDNGestureVision::FlowRegion* NDNGestureVision::getFlow(int index) const |
@@ -89,7 +89,7 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
89 | 89 | cv::cvtColor(newImage, newGrayScaleImage, CV_BGR2GRAY); |
90 | 90 | |
91 | 91 | if( m_prevGray.data ) |
92 | - { | |
92 | + { | |
93 | 93 | /*------------------- |
94 | 94 | * Find flow regions |
95 | 95 | */ |
@@ -97,57 +97,57 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
97 | 97 | cv::calcOpticalFlowFarneback(m_prevGray, newGrayScaleImage, flowMat, 0.5, 3, 15, 3, 5, 1.2, 0); |
98 | 98 | m_map = cv::Mat::zeros(m_map.size(),CV_8U); |
99 | 99 | |
100 | - std::vector<FlowRegion> flowRegions; | |
101 | - FlowRegion dummy; | |
102 | - flowRegions.push_back(dummy); | |
100 | + std::vector<FlowRegion> flowRegions; | |
101 | + FlowRegion dummy; | |
102 | + flowRegions.push_back(dummy); | |
103 | 103 | |
104 | - int nextRegionNum = 1; | |
105 | - for(int y = 0; y < m_scanningYMax; y++) | |
106 | - { | |
107 | - int x = 0; | |
104 | + int nextRegionNum = 1; | |
105 | + for(int y = 0; y < m_scanningYMax; y++) | |
106 | + { | |
107 | + int x = 0; | |
108 | 108 | while(x < m_scanningXMax) |
109 | 109 | { |
110 | 110 | const cv::Point2f& fxy = flowMat.at<cv::Point2f>(y * m_scanningStep, x * m_scanningStep); |
111 | 111 | |
112 | 112 | if (fxy.x * fxy.x + fxy.y * fxy.y <= m_minFlowThresholdP2) |
113 | - { | |
113 | + { | |
114 | 114 | x++; |
115 | - } | |
116 | - else | |
117 | - { | |
118 | - int extent = 1; | |
119 | - float dxTotal = fxy.x; | |
120 | - float dyTotal = fxy.y; | |
121 | - std::set<int> attachingRegions; | |
115 | + } | |
116 | + else | |
117 | + { | |
118 | + int extent = 1; | |
119 | + float dxTotal = fxy.x; | |
120 | + float dyTotal = fxy.y; | |
121 | + std::set<int> attachingRegions; | |
122 | 122 | int upperY = y - 1; |
123 | 123 | if (upperY >= 0) |
124 | - { | |
124 | + { | |
125 | 125 | if (m_map.at<uchar>(upperY, x) > 0) |
126 | - { | |
126 | + { | |
127 | 127 | attachingRegions.insert(m_map.at<uchar>(upperY,x)); |
128 | - } | |
129 | - } | |
130 | - int ry = y * m_scanningStep; | |
131 | - int sp = x; | |
132 | - int xp = x + 1; | |
128 | + } | |
129 | + } | |
130 | + int ry = y * m_scanningStep; | |
131 | + int sp = x; | |
132 | + int xp = x + 1; | |
133 | 133 | int rx = xp * m_scanningStep; |
134 | - while (xp < m_scanningXMax) | |
135 | - { | |
134 | + while (xp < m_scanningXMax) | |
135 | + { | |
136 | 136 | const cv::Point2f& rfxy = flowMat.at<cv::Point2f>(ry, rx); |
137 | 137 | if (rfxy.dot(fxy) < 0 || rfxy.x * rfxy.x + rfxy.y * rfxy.y < m_minFlowThresholdP2) |
138 | - break; | |
138 | + break; | |
139 | 139 | |
140 | - dxTotal += rfxy.x; | |
141 | - dyTotal += rfxy.y; | |
142 | - extent++; | |
140 | + dxTotal += rfxy.x; | |
141 | + dyTotal += rfxy.y; | |
142 | + extent++; | |
143 | 143 | if (upperY >= 0 && m_map.at<uchar>(upperY, xp) > 0) |
144 | - { | |
144 | + { | |
145 | 145 | attachingRegions.insert(m_map.at<uchar>(upperY, xp)); |
146 | - } | |
147 | - xp++; | |
146 | + } | |
147 | + xp++; | |
148 | 148 | rx += m_scanningStep; |
149 | - } | |
150 | - int ep = xp; | |
149 | + } | |
150 | + int ep = xp; | |
151 | 151 | |
152 | 152 | cv::Point2f direction; |
153 | 153 | direction.x = dxTotal / extent; |
@@ -156,103 +156,103 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
156 | 156 | direction.x = direction.x / distance; |
157 | 157 | direction.y = direction.y / distance; |
158 | 158 | |
159 | - std::set<int>::iterator it = attachingRegions.begin(); | |
160 | - int mapNum = 0; | |
161 | - float maxR = 0; | |
162 | - while(it != attachingRegions.end()) | |
163 | - { | |
164 | - float r = flowRegions[*it].direction.dot(direction); | |
165 | - if (r > 0 && maxR < r) | |
166 | - { | |
167 | - maxR = r; | |
168 | - mapNum = *it; | |
169 | - } | |
170 | - ++it; | |
171 | - } | |
172 | - | |
173 | - int regionNum = 0; | |
174 | - if (maxR > 0) | |
175 | - { | |
176 | - regionNum = mapNum; | |
159 | + std::set<int>::iterator it = attachingRegions.begin(); | |
160 | + int mapNum = 0; | |
161 | + float maxR = 0; | |
162 | + while(it != attachingRegions.end()) | |
163 | + { | |
164 | + float r = flowRegions[*it].direction.dot(direction); | |
165 | + if (r > 0 && maxR < r) | |
166 | + { | |
167 | + maxR = r; | |
168 | + mapNum = *it; | |
169 | + } | |
170 | + ++it; | |
171 | + } | |
172 | + | |
173 | + int regionNum = 0; | |
174 | + if (maxR > 0) | |
175 | + { | |
176 | + regionNum = mapNum; | |
177 | 177 | float rate = ((float)extent) / ((float)(flowRegions[regionNum].extent + extent)); |
178 | - direction.x = flowRegions[regionNum].direction.x * (1.0f - rate) + direction.x * rate; | |
179 | - direction.y = flowRegions[regionNum].direction.y * (1.0f - rate) + direction.y * rate; | |
180 | - float adj = 1.0f / sqrt(direction.x * direction.x + direction.y * direction.y); | |
181 | - flowRegions[regionNum].direction.x = direction.x * adj; | |
182 | - flowRegions[regionNum].direction.y = direction.y * adj; | |
183 | - flowRegions[regionNum].distance = flowRegions[regionNum].distance * (1.0f -rate) + distance * rate; | |
184 | - flowRegions[regionNum].extent = flowRegions[regionNum].extent + extent; | |
185 | - if (sp * m_scanningStep < flowRegions[regionNum].xmin) | |
186 | - { | |
187 | - flowRegions[regionNum].xmin = sp * m_scanningStep; | |
188 | - } | |
189 | - if (flowRegions[regionNum].xmax < (ep - 1) * m_scanningStep) | |
190 | - { | |
191 | - flowRegions[regionNum].xmax = (ep - 1) * m_scanningStep; | |
192 | - } | |
193 | - flowRegions[regionNum].ymax = y * m_scanningStep; | |
194 | - } | |
178 | + direction.x = flowRegions[regionNum].direction.x * (1.0f - rate) + direction.x * rate; | |
179 | + direction.y = flowRegions[regionNum].direction.y * (1.0f - rate) + direction.y * rate; | |
180 | + float adj = 1.0f / sqrt(direction.x * direction.x + direction.y * direction.y); | |
181 | + flowRegions[regionNum].direction.x = direction.x * adj; | |
182 | + flowRegions[regionNum].direction.y = direction.y * adj; | |
183 | + flowRegions[regionNum].distance = flowRegions[regionNum].distance * (1.0f -rate) + distance * rate; | |
184 | + flowRegions[regionNum].extent = flowRegions[regionNum].extent + extent; | |
185 | + if (sp * m_scanningStep < flowRegions[regionNum].xmin) | |
186 | + { | |
187 | + flowRegions[regionNum].xmin = sp * m_scanningStep; | |
188 | + } | |
189 | + if (flowRegions[regionNum].xmax < (ep - 1) * m_scanningStep) | |
190 | + { | |
191 | + flowRegions[regionNum].xmax = (ep - 1) * m_scanningStep; | |
192 | + } | |
193 | + flowRegions[regionNum].ymax = y * m_scanningStep; | |
194 | + } | |
195 | 195 | else |
196 | - { | |
197 | - FlowRegion flowRegion; | |
198 | - flowRegion.extent = extent; | |
199 | - flowRegion.direction = direction; | |
200 | - flowRegion.distance = distance; | |
201 | - flowRegion.xmin = sp * m_scanningStep; | |
202 | - flowRegion.xmax = (ep - 1) * m_scanningStep; | |
203 | - flowRegion.ymin = y * m_scanningStep; | |
204 | - flowRegion.ymax = flowRegion.ymin; | |
205 | - | |
206 | - flowRegions.push_back(flowRegion); | |
207 | - regionNum = nextRegionNum; | |
208 | - nextRegionNum++; | |
209 | - | |
210 | - assert(nextRegionNum == flowRegions.size()); | |
211 | - } | |
196 | + { | |
197 | + FlowRegion flowRegion; | |
198 | + flowRegion.extent = extent; | |
199 | + flowRegion.direction = direction; | |
200 | + flowRegion.distance = distance; | |
201 | + flowRegion.xmin = sp * m_scanningStep; | |
202 | + flowRegion.xmax = (ep - 1) * m_scanningStep; | |
203 | + flowRegion.ymin = y * m_scanningStep; | |
204 | + flowRegion.ymax = flowRegion.ymin; | |
205 | + | |
206 | + flowRegions.push_back(flowRegion); | |
207 | + regionNum = nextRegionNum; | |
208 | + nextRegionNum++; | |
209 | + | |
210 | + assert(nextRegionNum == flowRegions.size()); | |
211 | + } | |
212 | 212 | |
213 | 213 | for (int i = sp; i < ep; i++) |
214 | - { | |
214 | + { | |
215 | 215 | m_map.at<uchar>(y, i) = regionNum; |
216 | - } | |
217 | - x = ep; | |
218 | - } | |
219 | - } | |
220 | - } | |
216 | + } | |
217 | + x = ep; | |
218 | + } | |
219 | + } | |
220 | + } | |
221 | 221 | |
222 | - for (int i = 0; i < m_numOfFlows * 2; i++) | |
223 | - { | |
224 | - m_tempSortedFlowNums[i] = 0; | |
225 | - m_tempSortedFlowExtents[i] = 0; | |
226 | - } | |
222 | + for (int i = 0; i < m_numOfFlows * 2; i++) | |
223 | + { | |
224 | + m_tempSortedFlowNums[i] = 0; | |
225 | + m_tempSortedFlowExtents[i] = 0; | |
226 | + } | |
227 | 227 | |
228 | - int sortedFlowSizeMax = nextRegionNum <= m_numOfFlows * 2 ? nextRegionNum - 1 : m_numOfFlows * 2; | |
228 | + int sortedFlowSizeMax = nextRegionNum <= m_numOfFlows * 2 ? nextRegionNum - 1 : m_numOfFlows * 2; | |
229 | 229 | |
230 | 230 | /*------------------- |
231 | 231 | * Sort flowRegions with its extents and put the region numbers into m_tempSortedFlowNums[], m_tempSortedFlowExtents[] |
232 | 232 | * The size of the list is stored in sortedFlowSize. |
233 | 233 | */ |
234 | - int cnt = 0; | |
235 | - for (int i = 1 ; i < nextRegionNum; i++) | |
236 | - { | |
234 | + int cnt = 0; | |
235 | + for (int i = 1 ; i < nextRegionNum; i++) | |
236 | + { | |
237 | 237 | if (flowRegions[i].extent <= m_minExt) |
238 | - continue; | |
238 | + continue; | |
239 | 239 | |
240 | - for (int j = 0; j < sortedFlowSizeMax; j++) | |
241 | - { | |
240 | + for (int j = 0; j < sortedFlowSizeMax; j++) | |
241 | + { | |
242 | 242 | if (flowRegions[i].extent > m_tempSortedFlowExtents[j]) |
243 | - { | |
243 | + { | |
244 | 244 | for (int k = sortedFlowSizeMax - 2; k >= j; k--) |
245 | - { | |
246 | - m_tempSortedFlowNums[k + 1] = m_tempSortedFlowNums[k]; | |
247 | - m_tempSortedFlowExtents[k + 1] = m_tempSortedFlowExtents[k]; | |
248 | - } | |
249 | - m_tempSortedFlowNums[j] = i; | |
245 | + { | |
246 | + m_tempSortedFlowNums[k + 1] = m_tempSortedFlowNums[k]; | |
247 | + m_tempSortedFlowExtents[k + 1] = m_tempSortedFlowExtents[k]; | |
248 | + } | |
249 | + m_tempSortedFlowNums[j] = i; | |
250 | 250 | m_tempSortedFlowExtents[j] = flowRegions[i].extent; |
251 | - cnt++; | |
252 | - break; | |
253 | - } | |
254 | - } | |
255 | - } | |
251 | + cnt++; | |
252 | + break; | |
253 | + } | |
254 | + } | |
255 | + } | |
256 | 256 | int sortedFlowSize = sortedFlowSizeMax < cnt ? sortedFlowSizeMax : cnt; |
257 | 257 | |
258 | 258 | /*------------------- |
@@ -323,14 +323,13 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
323 | 323 | if (dmin < range * range) |
324 | 324 | { |
325 | 325 | cv::Point2f direction = flowRegions[m_tempSortedFlowNums[idx]].direction; |
326 | - if (abs(direction.x) < abs(direction.y)) | |
327 | - { | |
326 | + | |
327 | + if (direction.y != 0) | |
328 | 328 | expectedFlowDirectionY = direction.y < 0 ? -1 : 1; |
329 | - } | |
330 | - else | |
331 | - { | |
329 | + | |
330 | + if (direction.x != 0) | |
332 | 331 | expectedFlowDirectionX = direction.x < 0 ? -1 : 1; |
333 | - } | |
332 | + | |
334 | 333 | focusedRegionMoving = true; |
335 | 334 | } |
336 | 335 | } |
@@ -352,14 +351,14 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
352 | 351 | int ey = range - step; |
353 | 352 | |
354 | 353 | if (expectedFlowDirectionX > 0) |
355 | - sx /= 3; | |
354 | + sx = 0; | |
356 | 355 | else if (expectedFlowDirectionX < 0) |
357 | - ex /= 3; | |
356 | + ex = 0; | |
358 | 357 | |
359 | 358 | if (expectedFlowDirectionY > 0) |
360 | - sy /= 3; | |
359 | + sy = 0; | |
361 | 360 | else if (expectedFlowDirectionY < 0) |
362 | - ey /= 3; | |
361 | + ey = 0; | |
363 | 362 | |
364 | 363 | for (int y = sy; y <= ey; y += step) |
365 | 364 | { |
@@ -642,7 +641,7 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
642 | 641 | stillPointCnt++; |
643 | 642 | int d = calcColorDiff(m_backgroundImage.at<cv::Vec3b>(iy,ix), newImage.at<cv::Vec3b>(iy,ix)); |
644 | 643 | if (d > m_backgroundDiffThreshold) |
645 | - { | |
644 | + { | |
646 | 645 | stillPointChangedPCnt++; |
647 | 646 | stillPointChangePSum += d; |
648 | 647 | } |
@@ -784,7 +783,7 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
784 | 783 | * Draw output mat |
785 | 784 | */ |
786 | 785 | if (pGestureVisionDrawMat) |
787 | - { | |
786 | + { | |
788 | 787 | int xs = pGestureVisionDrawMat->size().width / 4; |
789 | 788 | int ys = pGestureVisionDrawMat->size().height / 4; |
790 | 789 |
@@ -886,7 +885,7 @@ void NDNGestureVision::calculateFlow(const cv::Mat &newImage, cv::Mat *pGestureV | ||
886 | 885 | } |
887 | 886 | } |
888 | 887 | |
889 | - newGrayScaleImage.copyTo(m_prevGray); | |
888 | + newGrayScaleImage.copyTo(m_prevGray); | |
890 | 889 | } |
891 | 890 | |
892 | 891 | cv::Rect NDNGestureVision::findFingures(CvSeq *contour, std::vector<cv::Point>& fingures) |