注意:本文樣例圖片為了避免侵權,均使用AIGC生成;
本文介紹影樓精修中膚色統一算法的實現方案,并以像素蛋糕為例,進行分析說明。
膚色統一就是將人像照片中皮膚區域的顏色進行統一,看起來顏色均勻一致,不會出現顏色不均勻問題。我們以像素蛋糕軟件為例,示例圖如下:
可以看到,在對人臉和身體進行膚色統一之后,皮膚的顏色一致性變好了不少,給人主觀感受更佳。
像素蛋糕的膚色統一功能歷經了多個版本的迭代,目前包含AI膚色統一和膚色統一兩個版本,同時又將人臉和身體區域做了區分,得到了臉部區域膚色統一和身體區域膚色統一功能調節,從用戶或者設計師角度來講,可控性更強,同時支持了單人和多人的處理。
膚色統一和AI膚色統一功能的差異:可能是AI膚色統一使用了基于深度學習的模型方案,直接自動對皮膚顏色和亮度進行調節,而膚色統一則是基于傳統圖像處理方案構建的;
我們這里以傳統膚色統一為例進行說明。通過效果上的測試,將像素蛋糕的膚色統一特點總結如下:
1.皮膚區域顏色一致性提高,顏色向某個顏色靠攏,而且,這個顏色并非皮膚區域的顏色均值,而是亮度較高的顏色;
2.皮膚暗部區域的亮度會自動調整變亮;
3.人臉和皮膚區域可分開處理;
我們將按照這三個特點來進行實現;
算法方案
假設原圖為S,最終效果圖為D;
1.對S進行皮膚分割,獲得皮膚區域Mask圖Mask_skin,這一步需要較高的準確性,皮膚區域為1-255的灰度值,非皮膚區域為0;目前皮膚分割相對比較準確的圖片API,如:美圖皮膚分割API > 阿里云皮膚分割API;
美圖API:https://ai.meitu.com/index?t=1747366168417
阿里云API:https://vision.aliyun.com/experience/detail?tagName=imageseg&children=SegmentSkin
2.對S進行人臉點位檢測,根據人臉關鍵點,計算人臉區域Mask圖Mask_face;
3.將膚色統一劃分為皮膚區域亮度統一和顏色統一兩個模塊;
3.1亮度統一,以身體皮膚區域處理為例,對皮膚區域進行基于直方圖信息的暗部區域自動亮度矯正;
思路:統計皮膚區域亮度直方圖,計算亮度中值,統計亮度低于中值的暗部區域皮膚像素數量,用于動態計算亮度增強程度,根據亮度增強程度,對暗部區域亮度進行矯正,非暗部區域保持不變;
這里給出亮度統一的核心代碼:
void SkinBrightenWithHistogram(unsigned char* bgraData, int width, int height, int stride, unsigned char* skinMask) {int size = width * height;float* luminance = (float*)malloc(sizeof(float) * size);int histBins[256] = { 0 };int skinCount = 0;// 1. 提取亮度信息并構建直方圖for (int y = 0; y < height; ++y) {unsigned char* row = bgraData + y * stride;for (int x = 0; x < width; ++x, row += 4) {int idx = y * width + x;float r = row[2], g = row[1], b = row[0];float yVal = 0.299f * r + 0.587f * g + 0.114f * b;luminance[idx] = yVal;if (skinMask[idx] > 20) {int bin = (int)CLIP3(yVal, 0, 255);histBins[bin]++;skinCount++;}}}if (skinCount == 0) {free(luminance);return;}// 2. 使用直方圖累加計算中值亮度int medianIndex = skinCount / 2;int cumulative = 0;int medianBin = 0;for (int i = 0; i < 256; ++i) {cumulative += histBins[i];if (cumulative >= medianIndex) {medianBin = i;break;}}float medianY = (float)medianBin;// 3. 統計暗部像素數量(亮度 < 中值)int darkCount = 0;for (int i = 0; i < medianBin; ++i) {darkCount += histBins[i];}float darkRatio = (float)darkCount / skinCount;4. 根據暗部比例動態計算增強強度 alpha//float alpha = CLAMP(0.3f + darkRatio * 0.7f, 0.3f, 1.0f);//if (medianY < 80.0f) {// alpha *= 1.2f;//}//float gamma = 1.2f;// 5. 根據暗部比例動態計算增強強度 alpha(提高提亮效果)float alpha = CLIP3(0.4f + darkRatio * 1.0f, 0.4f, 1.2f); // 強化基礎 alphaif (medianY < 80.0f) {alpha *= 1.4f; // 放大提亮倍數}else if (medianY < 100.0f) {alpha *= 1.2f; // 中等放大}float gamma = 1.4f; // 增加 gamma(讓暗部提亮更明顯)// 6. 提亮暗部區域for (int y = 0; y < height; ++y) {unsigned char* row = bgraData + y * stride;for (int x = 0; x < width; ++x, row += 4) {int idx = y * width + x;if (skinMask[idx] < 5) continue;float Y = luminance[idx];if (Y < medianY) {float norm = CLIP3(1.0f - (Y / (medianY + 1e-5f)), 0.0f, 1.0f);float boost = alpha * powf(norm, gamma);float newY = CLIP3(Y + boost * medianY, 0, 255);float scale = newY / (Y + 1e-5f);int gray = row[1] * skinMask[idx] / 255; // 用綠色通道估算灰度用于保色插值for (int c = 0; c < 3; ++c) {float val = row[2 - c] * scale;val = (unsigned char)CLIP3(val, 0, 255);row[2 - c] = (val * gray + row[2 - c] * (255 - gray)) / 255;}}}}free(luminance);
}
3.2顏色統一,基于HSV或Lab顏色空間,計算皮膚區域顏色亮度直方圖統計的中值以上的皮膚像素RGB均值,映射到HSV或Lab顏色空間,得到對應H或ab的均值(也可直接統計H或ab的均值);以HSV為例,根據原圖皮膚像素的HSV和目標均值HSV,重新映射得到顏色統一為均值顏色后的HSV,然后映射到RGB;
HSV_result=(H_mean, S_src,V_src)
RGB_result=HSV_result---->RGB
4.使用3的方式,分別對人臉區域和身體皮膚區域進行膚色統一,分別得到效果圖D_face和D_body;
5.設置人臉區域膚色統一參數k_face,身體膚色統一參數k_body,范圍[0,100],將原圖S和D_face,D_body進行alpha blend,得到最終效果圖D;
上述算法流程就是本人基于傳統數字圖像處理算法構建的膚色統一算法,效果測試和對比如下:
通過測試效果對比,可以看到,對比原圖S,本文算法在脖子等暗部區域亮度有一定的自動校正,同時,人臉和身體皮膚區域的膚色區域一致,基本實現了像素蛋糕類似的功能;
對于AI膚色統一,效果上會比傳統方法更自然,同時,對于暗部的亮度矯正也會更準確,后續將陸續進行分析;