本文共 17567 字,大约阅读时间需要 58 分钟。
先看SingleFace.cpp
初始化InitInstance函数, 返回
return SUCCEEDED(m_FTHelper.Init(m_hWnd, FTHelperCallingBack, this, m_depthType, m_depthRes, m_bNearMode, TRUE, // if near mode doesn't work, fall back to default mode m_colorType, m_colorRes, m_bSeatedSkeletonMode));
回调函数长这样, 其会在后面的SubmitFraceTrackingResult函数里面执行, 主要设置鸡蛋脸的一些参数
/** The "Face Tracker" helper class is generic. It will call back this function* after a face has been successfully tracked. The code in the call back passes the parameters* to the Egg Avatar, so it can be animated.*/void SingleFace::FTHelperCallingBack(PVOID pVoid){ SingleFace* pApp = reinterpret_cast(pVoid); if (pApp) { IFTResult* pResult = pApp->m_FTHelper.GetResult(); if (pResult && SUCCEEDED(pResult->GetStatus())) { FLOAT* pAU = NULL; UINT numAU; pResult->GetAUCoefficients(&pAU, &numAU); pApp->m_eggavatar.SetCandideAU(pAU, numAU); FLOAT scale; FLOAT rotationXYZ[3]; FLOAT translationXYZ[3]; pResult->Get3DPose(&scale, rotationXYZ, translationXYZ); pApp->m_eggavatar.SetTranslations(translationXYZ[0], translationXYZ[1], translationXYZ[2]); pApp->m_eggavatar.SetRotations(rotationXYZ[0], rotationXYZ[1], rotationXYZ[2]); } }}
在Helper的init函数里面创建了一个线程
HRESULT FTHelper::Init(HWND hWnd, FTHelperCallBack callBack, PVOID callBackParam, NUI_IMAGE_TYPE depthType, NUI_IMAGE_RESOLUTION depthRes, BOOL bNearMode, BOOL bFallbackToDefault, NUI_IMAGE_TYPE colorType, NUI_IMAGE_RESOLUTION colorRes, BOOL bSeatedSkeletonMode){ if (!hWnd || !callBack) { return E_INVALIDARG; } m_hWnd = hWnd; m_CallBack = callBack; m_CallBackParam = callBackParam; m_ApplicationIsRunning = true; m_depthType = depthType; m_depthRes = depthRes; m_bNearMode = bNearMode; m_bFallbackToDefault = bFallbackToDefault; m_bSeatedSkeletonMode = bSeatedSkeletonMode; m_colorType = colorType; m_colorRes = colorRes; m_hFaceTrackingThread = CreateThread(NULL, 0, FaceTrackingStaticThread, (PVOID)this, 0, 0); return S_OK;}
DWORD WINAPI FTHelper::FaceTrackingStaticThread(PVOID lpParam){ FTHelper* context = static_cast(lpParam); if (context) { return context->FaceTrackingThread(); } return 0;}
这个线程里面
DWORD WINAPI FTHelper::FaceTrackingThread(){ FT_CAMERA_CONFIG videoConfig; FT_CAMERA_CONFIG depthConfig; FT_CAMERA_CONFIG* pDepthConfig = NULL; // Try to get the Kinect camera to work HRESULT hr = m_KinectSensor.Init(m_depthType, m_depthRes, m_bNearMode, m_bFallbackToDefault, m_colorType, m_colorRes, m_bSeatedSkeletonMode); if (SUCCEEDED(hr)) { m_KinectSensorPresent = TRUE; m_KinectSensor.GetVideoConfiguration(&videoConfig); m_KinectSensor.GetDepthConfiguration(&depthConfig); pDepthConfig = &depthConfig; m_hint3D[0] = m_hint3D[1] = FT_VECTOR3D(0, 0, 0); } else { m_KinectSensorPresent = FALSE; WCHAR errorText[MAX_PATH]; ZeroMemory(errorText, sizeof(WCHAR) * MAX_PATH); wsprintf(errorText, L"Could not initialize the Kinect sensor. hr=0x%x\n", hr); MessageBoxW(m_hWnd, errorText, L"Face Tracker Initialization Error\n", MB_OK); return 1; } // Try to start the face tracker. m_pFaceTracker = FTCreateFaceTracker(_opt); if (!m_pFaceTracker) { MessageBoxW(m_hWnd, L"Could not create the face tracker.\n", L"Face Tracker Initialization Error\n", MB_OK); return 2; } hr = m_pFaceTracker->Initialize(&videoConfig, pDepthConfig, NULL, NULL); if (FAILED(hr)) { WCHAR path[512], buffer[1024]; GetCurrentDirectoryW(ARRAYSIZE(path), path); wsprintf(buffer, L"Could not initialize face tracker (%s). hr=0x%x", path, hr); MessageBoxW(m_hWnd, /*L"Could not initialize the face tracker.\n"*/ buffer, L"Face Tracker Initialization Error\n", MB_OK); return 3; } hr = m_pFaceTracker->CreateFTResult(&m_pFTResult); if (FAILED(hr) || !m_pFTResult) { MessageBoxW(m_hWnd, L"Could not initialize the face tracker result.\n", L"Face Tracker Initialization Error\n", MB_OK); return 4; } // Initialize the RGB image. m_colorImage = FTCreateImage(); if (!m_colorImage || FAILED(hr = m_colorImage->Allocate(videoConfig.Width, videoConfig.Height, FTIMAGEFORMAT_UINT8_B8G8R8X8))) { return 5; } if (pDepthConfig) { m_depthImage = FTCreateImage(); if (!m_depthImage || FAILED(hr = m_depthImage->Allocate(depthConfig.Width, depthConfig.Height, FTIMAGEFORMAT_UINT16_D13P3))) { return 6; } } SetCenterOfImage(NULL); m_LastTrackSucceeded = false; while (m_ApplicationIsRunning) { CheckCameraInput(); InvalidateRect(m_hWnd, NULL, FALSE); UpdateWindow(m_hWnd); Sleep(16); } m_pFaceTracker->Release(); m_pFaceTracker = NULL; if(m_colorImage) { m_colorImage->Release(); m_colorImage = NULL; } if(m_depthImage) { m_depthImage->Release(); m_depthImage = NULL; } if(m_pFTResult) { m_pFTResult->Release(); m_pFTResult = NULL; } m_KinectSensor.Release(); return 0;}
这个线程会在一个循环里面执行
while (m_ApplicationIsRunning) { CheckCameraInput(); InvalidateRect(m_hWnd, NULL, FALSE); UpdateWindow(m_hWnd); Sleep(16); }
然后CheckCameraInput处理每帧
// Get a video image and process it.void FTHelper::CheckCameraInput(){ HRESULT hrFT = E_FAIL; if (m_KinectSensorPresent && m_KinectSensor.GetVideoBuffer()) { HRESULT hrCopy = m_KinectSensor.GetVideoBuffer()->CopyTo(m_colorImage, NULL, 0, 0); if (SUCCEEDED(hrCopy) && m_KinectSensor.GetDepthBuffer()) { hrCopy = m_KinectSensor.GetDepthBuffer()->CopyTo(m_depthImage, NULL, 0, 0); } // Do face tracking if (SUCCEEDED(hrCopy)) { FT_SENSOR_DATA sensorData(m_colorImage, m_depthImage, m_KinectSensor.GetZoomFactor(), m_KinectSensor.GetViewOffSet()); FT_VECTOR3D* hint = NULL; if (SUCCEEDED(m_KinectSensor.GetClosestHint(m_hint3D))) { hint = m_hint3D; } if (m_LastTrackSucceeded) { hrFT = m_pFaceTracker->ContinueTracking(&sensorData, hint, m_pFTResult); } else { hrFT = m_pFaceTracker->StartTracking(&sensorData, NULL, hint, m_pFTResult); } } } m_LastTrackSucceeded = SUCCEEDED(hrFT) && SUCCEEDED(m_pFTResult->GetStatus()); if (m_LastTrackSucceeded) { SubmitFraceTrackingResult(m_pFTResult); } else { m_pFTResult->Reset(); } SetCenterOfImage(m_pFTResult);}
在SubmitFraceTrackingResult函数里面获取结果参数,并将网格边框显示出来。
BOOL FTHelper::SubmitFraceTrackingResult(IFTResult* pResult){ if (pResult != NULL && SUCCEEDED(pResult->GetStatus())) { if (m_CallBack) { (*m_CallBack)(m_CallBackParam);//回调函数会在这里执行 } if (m_DrawMask) { FLOAT* pSU = NULL; UINT numSU; BOOL suConverged; m_pFaceTracker->GetShapeUnits(NULL, &pSU, &numSU, &suConverged); POINT viewOffset = { 0, 0}; FT_CAMERA_CONFIG cameraConfig; if (m_KinectSensorPresent) { m_KinectSensor.GetVideoConfiguration(&cameraConfig); } else { cameraConfig.Width = 640; cameraConfig.Height = 480; cameraConfig.FocalLength = 500.0f; } IFTModel* ftModel; HRESULT hr = m_pFaceTracker->GetFaceModel(&ftModel); if (SUCCEEDED(hr)) { hr = VisualizeFaceModel(m_colorImage, ftModel, &cameraConfig, pSU, 1.0, viewOffset, pResult, 0x00FFFF00); ftModel->Release(); } } } return TRUE;}
HRESULT VisualizeFaceModel(IFTImage* pColorImg, IFTModel* pModel, FT_CAMERA_CONFIG const* pCameraConfig, FLOAT const* pSUCoef, FLOAT zoomFactor, POINT viewOffset, IFTResult* pAAMRlt, UINT32 color){ if (!pColorImg || !pModel || !pCameraConfig || !pSUCoef || !pAAMRlt) { return E_POINTER; } HRESULT hr = S_OK; UINT vertexCount = pModel->GetVertexCount(); FT_VECTOR2D* pPts2D = reinterpret_cast(_malloca(sizeof(FT_VECTOR2D) * vertexCount)); if (pPts2D) { FLOAT *pAUs; UINT auCount; hr = pAAMRlt->GetAUCoefficients(&pAUs, &auCount); if (SUCCEEDED(hr)) { FLOAT scale, rotationXYZ[3], translationXYZ[3]; hr = pAAMRlt->Get3DPose(&scale, rotationXYZ, translationXYZ); if (SUCCEEDED(hr)) { hr = pModel->GetProjectedShape(pCameraConfig, zoomFactor, viewOffset, pSUCoef, pModel->GetSUCount(), pAUs, auCount, scale, rotationXYZ, translationXYZ, pPts2D, vertexCount); if (SUCCEEDED(hr)) { POINT* p3DMdl = reinterpret_cast (_malloca(sizeof(POINT) * vertexCount)); if (p3DMdl) { for (UINT i = 0; i < vertexCount; ++i) { p3DMdl[i].x = LONG(pPts2D[i].x + 0.5f); p3DMdl[i].y = LONG(pPts2D[i].y + 0.5f); } FT_TRIANGLE* pTriangles; UINT triangleCount; hr = pModel->GetTriangles(&pTriangles, &triangleCount); if (SUCCEEDED(hr)) { struct EdgeHashTable { UINT32* pEdges; UINT edgesAlloc; void Insert(int a, int b) { UINT32 v = (min(a, b) << 16) | max(a, b); UINT32 index = (v + (v << 8)) * 49157, i; for (i = 0; i < edgesAlloc - 1 && pEdges[(index + i) & (edgesAlloc - 1)] && v != pEdges[(index + i) & (edgesAlloc - 1)]; ++i) { } pEdges[(index + i) & (edgesAlloc - 1)] = v; } } eht; eht.edgesAlloc = 1 << UINT(log(2.f * (1 + vertexCount + triangleCount)) / log(2.f)); eht.pEdges = reinterpret_cast (_malloca(sizeof(UINT32) * eht.edgesAlloc)); if (eht.pEdges) { ZeroMemory(eht.pEdges, sizeof(UINT32) * eht.edgesAlloc); for (UINT i = 0; i < triangleCount; ++i) { eht.Insert(pTriangles[i].i, pTriangles[i].j); eht.Insert(pTriangles[i].j, pTriangles[i].k); eht.Insert(pTriangles[i].k, pTriangles[i].i); } for (UINT i = 0; i < eht.edgesAlloc; ++i) { if(eht.pEdges[i] != 0) { pColorImg->DrawLine(p3DMdl[eht.pEdges[i] >> 16], p3DMdl[eht.pEdges[i] & 0xFFFF], color, 1); } } _freea(eht.pEdges); } // Render the face rect in magenta RECT rectFace; hr = pAAMRlt->GetFaceRect(&rectFace); if (SUCCEEDED(hr)) { POINT leftTop = {rectFace.left, rectFace.top}; POINT rightTop = {rectFace.right - 1, rectFace.top}; POINT leftBottom = {rectFace.left, rectFace.bottom - 1}; POINT rightBottom = {rectFace.right - 1, rectFace.bottom - 1}; UINT32 nColor = 0xff00ff; SUCCEEDED(hr = pColorImg->DrawLine(leftTop, rightTop, nColor, 1)) && SUCCEEDED(hr = pColorImg->DrawLine(rightTop, rightBottom, nColor, 1)) && SUCCEEDED(hr = pColorImg->DrawLine(rightBottom, leftBottom, nColor, 1)) && SUCCEEDED(hr = pColorImg->DrawLine(leftBottom, leftTop, nColor, 1)); } } _freea(p3DMdl); } else { hr = E_OUTOFMEMORY; } } } } _freea(pPts2D); } else { hr = E_OUTOFMEMORY; } return hr;}
然后在主窗口的消息循环中
case WM_PAINT: hdc = BeginPaint(hWnd, &ps); // Draw the avatar window and the video window PaintWindow(hdc, hWnd); EndPaint(hWnd, &ps); break;
画图函数长这样
// Draw the egg head and the camera video with the mask superimposed.BOOL SingleFace::PaintWindow(HDC hdc, HWND hWnd){ static int errCount = 0; BOOL ret = FALSE; RECT rect; GetClientRect(hWnd, &rect); int width = rect.right - rect.left; int height = rect.bottom - rect.top; int halfWidth = width/2; // Show the video on the right of the window errCount += !ShowVideo(hdc, width - halfWidth, height, halfWidth, 0); // Draw the egg avatar on the left of the window errCount += !ShowEggAvatar(hdc, halfWidth, height, 0, 0); return ret;}
其中ShowVideo会调用 m_FTHelper.GetColorImage(),获取helper画好的网格和边框。
// Drawing the video windowBOOL SingleFace::ShowVideo(HDC hdc, int width, int height, int originX, int originY){ BOOL ret = TRUE; // Now, copy a fraction of the camera image into the screen. IFTImage* colorImage = m_FTHelper.GetColorImage(); if (colorImage) { int iWidth = colorImage->GetWidth(); int iHeight = colorImage->GetHeight(); if (iWidth > 0 && iHeight > 0) { int iTop = 0; int iBottom = iHeight; int iLeft = 0; int iRight = iWidth; // Keep a separate buffer. if (m_pVideoBuffer && SUCCEEDED(m_pVideoBuffer->Allocate(iWidth, iHeight, FTIMAGEFORMAT_UINT8_B8G8R8A8))) { // Copy do the video buffer while converting bytes colorImage->CopyTo(m_pVideoBuffer, NULL, 0, 0); // Compute the best approximate copy ratio. float w1 = (float)iHeight * (float)width; float w2 = (float)iWidth * (float)height; if (w2 > w1 && height > 0) { // video image too wide float wx = w1/height; iLeft = (int)max(0, m_FTHelper.GetXCenterFace() - wx / 2); iRight = iLeft + (int)wx; if (iRight > iWidth) { iRight = iWidth; iLeft = iRight - (int)wx; } } else if (w1 > w2 && width > 0) { // video image too narrow float hy = w2/width; iTop = (int)max(0, m_FTHelper.GetYCenterFace() - hy / 2); iBottom = iTop + (int)hy; if (iBottom > iHeight) { iBottom = iHeight; iTop = iBottom - (int)hy; } } int const bmpPixSize = m_pVideoBuffer->GetBytesPerPixel(); SetStretchBltMode(hdc, HALFTONE); BITMAPINFO bmi = { sizeof(BITMAPINFO), iWidth, iHeight, 1, static_cast(bmpPixSize * CHAR_BIT), BI_RGB, m_pVideoBuffer->GetStride() * iHeight, 5000, 5000, 0, 0}; if (0 == StretchDIBits(hdc, originX, originY, width, height, iLeft, iBottom, iRight-iLeft, iTop-iBottom, m_pVideoBuffer->GetBuffer(), &bmi, DIB_RGB_COLORS, SRCCOPY)) { ret = FALSE; } } } } return ret;}
ShowEggAvatar会调用m_eggavatar.DrawImage(m_pImageBuffer);画鸡蛋脸, 注意鸡蛋脸的参数在回调函数里面设置好了
// Drawing codeBOOL SingleFace::ShowEggAvatar(HDC hdc, int width, int height, int originX, int originY){ static int errCount = 0; BOOL ret = FALSE; if (m_pImageBuffer && SUCCEEDED(m_pImageBuffer->Allocate(width, height, FTIMAGEFORMAT_UINT8_B8G8R8A8))) { memset(m_pImageBuffer->GetBuffer(), 0, m_pImageBuffer->GetStride() * height); // clear to black m_eggavatar.SetScaleAndTranslationToWindow(height, width); m_eggavatar.DrawImage(m_pImageBuffer); BITMAPINFO bmi = { sizeof(BITMAPINFO), width, height, 1, static_cast(m_pImageBuffer->GetBytesPerPixel() * CHAR_BIT), BI_RGB, m_pImageBuffer->GetStride() * height, 5000, 5000, 0, 0}; errCount += (0 == StretchDIBits(hdc, 0, 0, width, height, 0, 0, width, height, m_pImageBuffer->GetBuffer(), &bmi, DIB_RGB_COLORS, SRCCOPY)); ret = TRUE; } return ret;}
转载地址:http://uaxdi.baihongyu.com/