ワイドアイ効果
ワイドアイ効果の原理
広い目の美しさの特殊効果の原理はほとんど同じですが、AIとCGの組み合わせです。
美の基本原理は、ディープラーニングとCGです。ディープラーニングは顔検出と顔キーポイント検出に使用されます。コンピュータグラフィックスは、皮膚剥離、顔の間引き、化粧塗りなどに使用します。一般的にAndroidではOpenGLES、IOSではMetalが使用されています。
AIから全体のプロセスの美しさを説明するために、学生のソースコードを参照してくださいに直接最後まで引っ張ることができ、それは基本的に同じであることを、ジッタリ声の美しさの効果のオープンソースの実装を見てきました!
顔検出&顔のキーポイント
- 顔検出は、写真やビデオストリーム内の顔の検出を指し、イメージ内の顔を検索します。
- 通常の処理モードを選択
TengineKit
Free Mobile Real-Time Face 212 Keypoint SDK.は、簡単に統合できる顔検出と顔キーポイントSDKです。
github.com/Tengin...
TengineKit
口紅効果
Gradleの設定
プロジェクトのBuild.gradleに
repositories {
...
mavenCentral()
...
}
allprojects {
repositories {
...
mavenCentral()
...
}
}
メインモジュールにbuild.gradleを追加します。
dependencies {
...
implementation 'com.tengine.android:tenginekit:1.0.5'
...
}
マニフェストの構成
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
Gif から渡されるイメージのストリームを処理します。
まずはTengineKitの初期化から始めましょう。
- 通常の処理モードを選択します。
- 顔検出と顔キーポイント機能をオンにします。
- イメージストリームのフォーマットをRGBAに設定します。
- 入力イメージストリームの幅と高さを設定します。ここではgifイメージのプレビュー幅と高さを設定しています。
- 出力イメージストリームの幅を設定します。ここではGifImageViewの幅を設定していますが、gifと同じなので、代わりにgifの幅を使用してください。
com.tenginekit.Face.init(getBaseContext(),
AndroidConfig.create()
.setNormalMode()
.openFunc(AndroidConfig.Func.Detect)
.openFunc(AndroidConfig.Func.Landmark)
.setInputImageFormat(AndroidConfig.ImageFormat.RGBA)
.setInputImageSize(facingGif.getGifWidth(), facingGif.getGifHeight())
.setOutputImageSize(facingGif.getGifWidth(), facingGif.getGifHeight())
);
キーポイントから目の中心を取得します。
Point getLeftEyeCenter(FaceLandmarkInfo fi){
FaceLandmarkPoint p1 = fi.landmarks.get(105);
FaceLandmarkPoint p2 = fi.landmarks.get(113);
return new Point((int)((p1.X + p2.X) / 2), (int)((p1.Y + p2.Y) / 2));
}
Point getRightEyeCenter(FaceLandmarkInfo fi){
FaceLandmarkPoint p1 = fi.landmarks.get(121);
FaceLandmarkPoint p2 = fi.landmarks.get(129);
return new Point((int)((p1.X + p2.X) / 2), (int)((p1.Y + p2.Y) / 2));
}
目の拡大アルゴリズム
public class MagnifyEyeUtils {
/**
* 目の拡大アルゴリズム
* @param bitmap 元のビットマップ
* @param centerPoint 中心点を拡大する
* @param radius ズーム半径
* @param sizeLevel 拡大[0,4]
* @return 目を拡大した後のイメージ
*/
public static Bitmap magnifyEye(Bitmap bitmap, Point centerPoint, int radius, float sizeLevel) {
Bitmap dstBitmap = bitmap.copy(Bitmap.Config.RGB_565, true);
int left = centerPoint.x - radius < 0 ? 0 : centerPoint.x - radius;
int top = centerPoint.y - radius < 0 ? 0 : centerPoint.y - radius;
int right = centerPoint.x + radius > bitmap.getWidth() ? bitmap.getWidth() - 1 : centerPoint.x + radius;
int bottom = centerPoint.y + radius > bitmap.getHeight() ? bitmap.getHeight() - 1 : centerPoint.y + radius;
int powRadius = radius * radius;
int offsetX, offsetY, powDistance, powOffsetX, powOffsetY;
int disX, disY;
//数値が負の場合、結果は縮小される。
float strength = (5 + sizeLevel * 2) / 10;
for (int i = top; i <= bottom; i++) {
offsetY = i - centerPoint.y;
for (int j = left; j <= right; j++) {
offsetX = j - centerPoint.x;
powOffsetX = offsetX * offsetX;
powOffsetY = offsetY * offsetY;
powDistance = powOffsetX + powOffsetY;
if (powDistance <= powRadius) {
double distance = Math.sqrt(powDistance);
double sinA = offsetX / distance;
double cosA = offsetY / distance;
double scaleFactor = distance / radius - 1;
scaleFactor = (1 - scaleFactor * scaleFactor * (distance / radius) * strength);
distance = distance * scaleFactor;
disY = (int) (distance * cosA + centerPoint.y + 0.5);
disY = checkY(disY, bitmap);
disX = (int) (distance * sinA + centerPoint.x + 0.5);
disX = checkX(disX, bitmap);
//中心点をそのままにする
if (!(j == centerPoint.x && i == centerPoint.y)) {
dstBitmap.setPixel(j, i, bitmap.getPixel(disX, disY));
//dstBitmap.setPixel(j, i, Color.WHITE);
}
}
}
}
return dstBitmap;
}
private static int checkY(int disY, Bitmap bitmap) {
if (disY < 0) {
disY = 0;
} else if (disY >= bitmap.getHeight()) {
disY = bitmap.getHeight() - 1;
}
return disY;
}
private static int checkX(int disX, Bitmap bitmap) {
if (disX < 0) {
disX = 0;
} else if (disX >= bitmap.getWidth()) {
disX = bitmap.getWidth() - 1;
}
return disX;
}
}
このコードは github.com/Ma...
レンダリング
渡されたビットマップはRGB_565で、標準のRGBA形式に変換する必要があります。
facingGif.setOnFrameAvailable(new GifImageView.OnFrameAvailable() {
@Override
public Bitmap onFrameAvailable(Bitmap bitmap) {
// bitmap RGB_565
Bitmap out_bitmap = Bitmap.createBitmap(
facingGif.getGifWidth(),
facingGif.getGifHeight(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(out_bitmap);
canvas.drawBitmap(bitmap, 0, 0, null);
bitmap.recycle();
byte[] bytes = bitmap2Bytes(out_bitmap);
Face.FaceDetect faceDetect = com.tenginekit.Face.detect(bytes);
if(faceDetect.getFaceCount() > 0){
faceLandmarks = faceDetect.landmark2d();
if(faceLandmarks != null){
for (int i = 0; i < faceLandmarks.size(); i++) {
FaceLandmarkInfo fi = faceLandmarks.get(i);
out_bitmap = MagnifyEyeUtils.magnifyEye(out_bitmap, getLeftEyeCenter(fi), 40, 4);
out_bitmap = MagnifyEyeUtils.magnifyEye(out_bitmap, getRightEyeCenter(fi), 40, 4);
}
}
}
return out_bitmap;
}
});
比較
提案
TengineKit - 無料、高速、簡単、モバイルでのリアルタイム顔検出&FaceLandmark SDK。
Makeup - "女神 "を反逆者に、メイクアップをコード化しましょう!
CainCamera - CainCamera は美容カメラ、イメージ、ショートビデオの開発について学ぶための Android プロジェクトです。
ソースコード
github.com/jiangzhongb...