在一些特殊狀況下,常常須要依據圖像中的人臉,對圖片進行傾斜矯正。html
例如拍照角度幅度過大之類的狀況,而進行人工矯正確實很叫人頭大。c++
那是否是能夠有一種算法,能夠根據人臉的信息對圖片進行角度的修復呢?git
答案確定是確認的。github
再次例如,想要經過人臉的特徵對人物的表情和情緒進行精準判斷,算法
那麼這個時候若是能確保人臉沒有發現嚴重傾斜,無疑對準確率判斷有必定的幫助。ide
那麼假如一張圖片只有一我的臉,其實很好判斷,經過眼睛的位置的座標,根據兩眼的直線角度,函數
就能夠計算出修正的角度。post
而後旋轉圖片到對應角度便可。ui
可是若是,一張圖片存在多張人臉的時候該怎麼辦?spa
有兩種方法:
1.找到最大的那我的臉,以它爲基準
2.找到頻次最高的人臉角度,以頻次爲基準
固然在大多數狀況,方法1是比較合理的。
這兩個種狀況就留給各位看官去實現了。
本人僅僅考慮一張人臉的狀況,演示如何實現該功能。
傾斜角度計算的代碼以下:
float diffEyeX = right_eye_x - left_eye_x; float diffEyeY = right_eye_y - left_eye_y; float fAngle; float M_PI = 3.1415926535897932384626433832795f; if (fabs(diffEyeX) < 0.0000001f) fAngle = 0.f; else fAngle = atanf(diffEyeY / diffEyeX) * 180.0f / M_PI;
若是看不明白,須要好好補一下高中數學基礎。
爲了節約時間,直接複用《自動紅眼移除算法 附c++完整代碼》的代碼。
增長函數以下:
void RotateBilinear(unsigned char *sourceData, int width, int height, int Channels, int RowBytes, unsigned char *destinationData, int newWidth, int newHeight, float angle, bool keepSize = true, int fillColorR = 255, int fillColorG = 255, int fillColorB = 255) { if (sourceData == NULL || destinationData == NULL) return; float oldXradius = (float) (width - 1) / 2; float oldYradius = (float) (height - 1) / 2; float newXradius = (float) (newWidth - 1) / 2; float newYradius = (float) (newHeight - 1) / 2; double MPI = 3.14159265358979323846; double angleRad = -angle * MPI / 180.0; float angleCos = (float) cos(angleRad); float angleSin = (float) sin(angleRad); int srcStride = RowBytes; int dstOffset = newWidth * Channels - ((Channels == 1) ? newWidth : newWidth * Channels); unsigned char fillR = fillColorR; unsigned char fillG = fillColorG; unsigned char fillB = fillColorB; unsigned char *src = (unsigned char *) sourceData; unsigned char *dst = (unsigned char *) destinationData; int ymax = height - 1; int xmax = width - 1; if (Channels == 1) { float cy = -newYradius; for (int y = 0; y < newHeight; y++) { float tx = angleSin * cy + oldXradius; float ty = angleCos * cy + oldYradius; float cx = -newXradius; for (int x = 0; x < newWidth; x++, dst++) { float ox = tx + angleCos * cx; float oy = ty - angleSin * cx; int ox1 = (int) ox; int oy1 = (int) oy; if ((ox1 < 0) || (oy1 < 0) || (ox1 >= width) || (oy1 >= height)) { *dst = fillG; } else { int ox2 = (ox1 == xmax) ? ox1 : ox1 + 1; int oy2 = (oy1 == ymax) ? oy1 : oy1 + 1; float dx1 = 0; if ((dx1 = ox - (float) ox1) < 0) dx1 = 0; float dx2 = 1.0f - dx1; float dy1 = 0; if ((dy1 = oy - (float) oy1) < 0) dy1 = 0; float dy2 = 1.0f - dy1; unsigned char *p1 = src + oy1 * srcStride; unsigned char *p2 = src + oy2 * srcStride; *dst = (unsigned char) (dy2 * (dx2 * p1[ox1] + dx1 * p1[ox2]) + dy1 * (dx2 * p2[ox1] + dx1 * p2[ox2])); } cx++; } cy++; dst += dstOffset; } } else if (Channels == 3) { float cy = -newYradius; for (int y = 0; y < newHeight; y++) { float tx = angleSin * cy + oldXradius; float ty = angleCos * cy + oldYradius; float cx = -newXradius; for (int x = 0; x < newWidth; x++, dst += Channels) { float ox = tx + angleCos * cx; float oy = ty - angleSin * cx; int ox1 = (int) ox; int oy1 = (int) oy; if ((ox1 < 0) || (oy1 < 0) || (ox1 >= width) || (oy1 >= height)) { dst[0] = fillR; dst[1] = fillG; dst[2] = fillB; } else { int ox2 = (ox1 == xmax) ? ox1 : ox1 + 1; int oy2 = (oy1 == ymax) ? oy1 : oy1 + 1; float dx1 = 0; if ((dx1 = ox - (float) ox1) < 0) dx1 = 0; float dx2 = 1.0f - dx1; float dy1 = 0; if ((dy1 = oy - (float) oy1) < 0) dy1 = 0; float dy2 = 1.0f - dy1; unsigned char *p1 = src + oy1 * srcStride; unsigned char *p2 = p1; p1 += ox1 * Channels; p2 += ox2 * Channels; unsigned char *p3 = src + oy2 * srcStride; unsigned char *p4 = p3; p3 += ox1 * Channels; p4 += ox2 * Channels; dst[0] = (unsigned char) ( dy2 * (dx2 * p1[0] + dx1 * p2[0]) + dy1 * (dx2 * p3[0] + dx1 * p4[0])); dst[1] = (unsigned char) ( dy2 * (dx2 * p1[1] + dx1 * p2[1]) + dy1 * (dx2 * p3[1] + dx1 * p4[1])); dst[2] = (unsigned char) ( dy2 * (dx2 * p1[2] + dx1 * p2[2]) + dy1 * (dx2 * p3[2] + dx1 * p4[2])); } cx++; } cy++; dst += dstOffset; } } else if (Channels == 4) { float cy = -newYradius; for (int y = 0; y < newHeight; y++) { float tx = angleSin * cy + oldXradius; float ty = angleCos * cy + oldYradius; float cx = -newXradius; for (int x = 0; x < newWidth; x++, dst += Channels) { float ox = tx + angleCos * cx; float oy = ty - angleSin * cx; int ox1 = (int) ox; int oy1 = (int) oy; if ((ox1 < 0) || (oy1 < 0) || (ox1 >= width) || (oy1 >= height)) { dst[0] = fillR; dst[1] = fillG; dst[2] = fillB; dst[3] = 255; } else { int ox2 = (ox1 == xmax) ? ox1 : ox1 + 1; int oy2 = (oy1 == ymax) ? oy1 : oy1 + 1; float dx1 = 0; if ((dx1 = ox - (float) ox1) < 0) dx1 = 0; float dx2 = 1.0f - dx1; float dy1 = 0; if ((dy1 = oy - (float) oy1) < 0) dy1 = 0; float dy2 = 1.0f - dy1; unsigned char *p1 = src + oy1 * srcStride; unsigned char *p2 = p1; p1 += ox1 * Channels; p2 += ox2 * Channels; unsigned char *p3 = src + oy2 * srcStride; unsigned char *p4 = p3; p3 += ox1 * Channels; p4 += ox2 * Channels; dst[0] = (unsigned char) ( dy2 * (dx2 * p1[0] + dx1 * p2[0]) + dy1 * (dx2 * p3[0] + dx1 * p4[0])); dst[1] = (unsigned char) ( dy2 * (dx2 * p1[1] + dx1 * p2[1]) + dy1 * (dx2 * p3[1] + dx1 * p4[1])); dst[2] = (unsigned char) ( dy2 * (dx2 * p1[2] + dx1 * p2[2]) + dy1 * (dx2 * p3[2] + dx1 * p4[2])); dst[3] = 255; } cx++; } cy++; dst += dstOffset; } } } void facialPoseCorrection(unsigned char *inputImage, int Width, int Height, int Channels, int left_eye_x, int left_eye_y, int right_eye_x, int right_eye_y) { float diffEyeX = right_eye_x - left_eye_x; float diffEyeY = right_eye_y - left_eye_y; float fAngle; float M_PI = 3.1415926535897932384626433832795f; if (fabs(diffEyeX) < 0.0000001f) fAngle = 0.f; else fAngle = atanf(diffEyeY / diffEyeX) * 180.0f / M_PI; size_t numberOfPixels = Width * Height * Channels * sizeof(unsigned char); unsigned char *outputImage = (unsigned char *) malloc(numberOfPixels); if (outputImage != nullptr) { RotateBilinear(inputImage, Width, Height, Channels, Width * Channels, outputImage, Width, Height, fAngle); memcpy(inputImage, outputImage, numberOfPixels); free(outputImage); } }
上效果圖片。
原圖:
紅眼修復+傾斜矯正:
項目地址:
https://github.com/cpuimage/MTCNN
命令行參數:
mtcnn 模型文件路徑 圖片路徑
例如: mtcnn ../models ../sample.jpg
用cmake便可進行編譯示例代碼,詳情見CMakeLists.txt。
如有其餘相關問題或者需求也能夠郵件聯繫俺探討。
郵箱地址是: gaozhihan@vip.qq.com