http://www.mediawiki.org/wiki/Special:Code/pywikipedia/11593
Revision: 11593 Author: drtrigon Date: 2013-05-26 17:21:17 +0000 (Sun, 26 May 2013) Log Message: ----------- new feature; head pose detection from face markers (eyes, nose and mouth)
Modified Paths: -------------- trunk/pywikipedia/catimages.py
Modified: trunk/pywikipedia/catimages.py =================================================================== --- trunk/pywikipedia/catimages.py 2013-05-26 14:12:52 UTC (rev 11592) +++ trunk/pywikipedia/catimages.py 2013-05-26 17:21:17 UTC (rev 11593) @@ -353,7 +353,8 @@ 'Eyes': [], 'Mouth': (), 'Nose': (), - 'Ears': [], } + 'Ears': [], + 'Pose': (), } data['Coverage'] = float(data['Position'][2]*data['Position'][3])/(self.image_size[0]*self.image_size[1]) #if (c >= confidence): # eyes = nestedObjects @@ -389,6 +390,47 @@ radius = cv.Round((nrwidth + nrheight)*0.25*scale) #cv2.circle( img, (cx, cy), radius, color, 3, 8, 0 ) data['Ears'].append( (cx-radius, cy-radius, 2*radius, 2*radius) ) + if data['Mouth'] and data['Nose'] and data['Eyes'] and (len(data['Eyes']) == 2): + # head model "little girl" for use in "MeshLab": + # http://www.turbosquid.com/FullPreview/Index.cfm/ID/302581 + # http://meshlab.sourceforge.net/ + D3points = [[ 70.0602, 109.898, 20.8234], # left eye + [ 2.37427, 110.322, 21.7776], # right eye + [ 36.8301, 78.3185, 52.0345], # nose + [ 36.6391, 51.1675, 38.5903],] # mouth + #[ 119.268, 91.3111, -69.6397], # left ear + #[-49.1328, 91.3111, -67.2481],] # right ear + D2points = [np.array(data['Eyes'][0]), np.array(data['Eyes'][1]), + np.array(data['Nose']), np.array(data['Mouth']),] + D2points = [ item[:2] + item[2:]/2. for item in D2points ] + neutral = np.array([[np.pi],[0.],[0.]]) + # calculate pose + rvec, tvec, cm, err = self._util_get_Pose(D3points, D2points, self.image_size) + #data['Pose'] = tuple(rvec[:,0]) + check = not (err[:,0,:].max() > 0.5) + if not check: + rvec = neutral # reset to neutral pose + tvec = np.array([[0.],[0.],[100.]]) # reset to neutral position (same order as max of D3points) + pywikibot.warning(u'Could not calculate pose of face, too big errors. ' + u'(looks like neutral pose/position is somehow singular)') + ## debug: draw pose + ##rvec *= 0 + #mat, perp = self._util_getD2coords_calc(np.eye(3), cm, rvec, tvec, hacky=False) + ## from '_util_drawAxes(...)' + #for i, item in enumerate(mat.transpose()): + # p = tuple((50+10*item).astype(int))[:2] + # cv2.line(img, (50, 50), p, (0., 0., 255.), 1) + # cv2.putText(img, str(i), p, cv2.FONT_HERSHEY_PLAIN, 1., (0., 0., 255.)) + #cv2.imshow("win", img) + #cv2.waitKey() + # calculate delta to neutral pose + drv = -cv2.composeRT(-rvec, np.zeros((3,1)), + neutral, np.zeros((3,1)))[0] + rvec = cv2.Rodrigues(cv2.Rodrigues(rvec)[0])[0] # NOT unique!!! + nrv = cv2.composeRT(neutral, np.zeros((3,1)), + drv, np.zeros((3,1)))[0] + #print (rvec - nrv < 1E-12) # compare + data['Pose'] = map(float, tuple(drv[:,0])) result.append( data )
## see '_drawRect' @@ -401,6 +443,40 @@ self._info['Faces'] += result return
+ def _util_get_Pose(self, D3points, D2points, shape): + #shape = (img.shape[1], img.shape[0]) + """ Calculate pose from head model "little girl" w/o camera or other + calibrations needed. + + D2points: left eye, right eye, nose, mouth + """ + # howto (credits to "Roy"): + # http://www.youtube.com/watch?v=ZDNH4BT5Do4 + # http://www.morethantechnical.com/2010/03/19/quick-and-easy-head-pose-estimat... + # http://www.morethantechnical.com/2012/10/17/head-pose-estimation-with-opencv... + # e.g. with head model "little girl" for use in "MeshLab": + # http://www.turbosquid.com/FullPreview/Index.cfm/ID/302581 + # http://meshlab.sourceforge.net/ + + # set-up camera matrix (no calibration needed!) + max_d = max(shape) + cameraMatrix = [[max_d, 0, shape[0]/2.0], + [ 0, max_d, shape[1]/2.0], + [ 0, 0, 1.0],] + + # calculate pose + rvec, tvec = cv2.solvePnP(np.array(D3points).astype('float32'), np.array(D2points).astype('float32'), np.array(cameraMatrix).astype('float32'), None) + + # compare to 2D points + err = [] + for i, vec in enumerate(np.array(D3points)): + nvec = np.dot(cameraMatrix, (np.dot(cv2.Rodrigues(rvec)[0], vec) + tvec[:,0])) + err.append(((D2points[i] - nvec[:2]/nvec[2]), D2points[i], nvec[:2]/nvec[2])) + + pywikibot.output(u'result for UN-calibrated camera:\n rot=%s' % rvec.transpose()[0]) + + return rvec, tvec, np.array(cameraMatrix), (np.array(err)/max_d) + # https://pypi.python.org/pypi/xbob.flandmark # http://cmp.felk.cvut.cz/~uricamic/flandmark/ def _detect_FaceLandmark_xBOB(self): @@ -1601,6 +1677,8 @@
def _detect_Chessboard_CV(self): # Chessboard (opencv reference detector) + # http://www.c-plusplus.de/forum/273920-full + # http://www.youtube.com/watch?v=bV-jAnQ-tvw # http://nullege.com/codes/show/src%40o%40p%40opencvpython-HEAD%40samples%40ch...
self._info['Chessboard'] = [] @@ -1818,7 +1896,7 @@ # mat = mat/max(matnorm[0]) # return (mat, D3coords)
- def _util_getD2coords_calc(self, D3coords, cameraMatrix, rvec, tvec): + def _util_getD2coords_calc(self, D3coords, cameraMatrix, rvec, tvec, hacky=True): """Calculate s m' = A [R|t] M' in order to project 3D points down to 2D.
m' = (u, v, 1)^T, M' = (X, Y, Z, 1)^T, A: camera m. and [R|t]: rotation- @@ -1826,7 +1904,7 @@
@see http://docs.opencv.org/modules/calib3d/doc/camera_calibration_and_3d_reconst... """ - # cv2.decomposeProjectionMatrix(...), cv2.composeRT(...) + # cv2.decomposeProjectionMatrix(...) cm = cameraMatrix.copy() cm[0:2,2] = [0., 0.] rmat = np.zeros((3,4)) @@ -1840,8 +1918,22 @@ coords = np.dot(rmat, cv2.convertPointsToHomogeneous(D3coords.astype('float32')).transpose()[:,0,:]) coords2D = np.dot((cm), coords) perp = coords - origin - mat = coords2D - origin2D - mat = mat/max([np.linalg.norm(mat[:,i]) for i in range(3)]) + if hacky: + # for '_detect_Chessboard_CV' but looks a bit strange ... may be wrong?! + mat = coords2D - origin2D + mat = mat/max([np.linalg.norm(mat[:,i]) for i in range(3)]) + else: + for i in range(3): # rescale with s + coords2D[:,i] /= coords2D[2,i] + origin2D[:,i] /= origin2D[2,i] + mat = coords2D - origin2D + # simple'n'fast solution, if just 2D results are needed + #mat, jacobian = cv2.projectPoints(np.append(np.zeros((1,3)), + # D3coords, + # axis=0), + # rvec, tvec, cm, np.zeros((5,1))) + #mat = mat[:,0,:] + #mat = (mat[1:,:] - mat[0,:]).transpose() return (mat, perp)
# def _util_drawAxes(self, mat, x, y, im):
pywikipedia-svn@lists.wikimedia.org