android 相机人脸检测 人脸识别 画人脸边框 识别成功保存图片 mlkit 机器学习

ML Kit 是一款移动 SDK,可将 Google 的设备端机器学习专业知识融入到 Android 和 iOS 应用中。使用我们功能强大且易于使用的生成式 AI、Vision 和 Natural Language API 来解决应用中的常见难题,或打造全新的用户体验。所有功能均由 Google 的一流机器学习模型提供支持,并且免费提供给您。

随着移动端计算性能的增强,实时人脸检测与表情识别的需求日益增加,如社交应用的趣味滤镜、视频会议中的人脸跟踪、以及安防领域的面部分析等。本文章通过结合 CameraX 和 ML Kit,展示了如何在Android上快速实现高效、稳定的人脸检测与表情识别功能。

项目亮点:

1 实时检测人脸特征:包括微笑指数、眼睛睁开状态等关键信息。

2 直观的视觉化界面:自定义绘制层显示检测的轮廓点、表情符号和辅助信息。

3 现代化架构:基于CameraX,摆脱传统复杂的Camera API,兼顾易用性与性能。

4 高度可扩展性:可以轻松拓展到AR特效、行为分析等高级应用场景。

1. CameraX简介

1.1 CameraX介绍

Q1: 什么是 CameraX?

CameraX 是 Google 提供的 Android 相机库,旨在简化 Android 开发中相机功能的实现,并提供更好的跨设备兼容性。它通过统一的 API 接口来简化底层硬件的管理,使得开发者能够轻松地在不同的 Android 设备上实现相机相关的功能,而不需要处理复杂的设备差异。

Q2: CameraX 的工作原理是什么?

CameraX 的核心是相机功能的封装:它提供了几个模块来处理常见的相机操作:

Preview:展示实时的相机预览。

ImageCapture:拍摄照片。

ImageAnalysis:对图像流进行实时分析,用于人脸识别、物体检测等应用。

VideoCapture:录制视频。

这些模块通过统一的 API 接口进行操作。开发者通过 CameraX 可以轻松访问相机硬件的功能,而无需关心设备差异。CameraX 自动处理设备的适配工作,确保应用能够在各种 Android 设备上正常运行。

Q3: CameraX 一般用于哪里?有什么优缺点?

适用场景:

相机预览:展示实时摄像头画面,例如视频聊天、相机应用中的实时预览。

图像分析:进行人脸检测、物体识别、条码扫描等实时图像处理。

拍照与视频录制:在应用中实现拍照、录像功能。

优点:

简化开发:CameraX 提供了易于使用的 API 接口,开发者可以快速实现相机功能。

跨设备兼容性:CameraX 自动适配不同型号的设备,减少了设备差异带来的问题。

与 Jetpack 集成:与 Android Jetpack 库紧密集成,支持生命周期管理,提升了开发效率。

图像分析支持:通过 ImageAnalysis 模块,CameraX 可以与 ML Kit 等机器学习工具结合,进行实时图像分析。

缺点:

硬件限制:虽然 CameraX 提供了广泛的兼容性,但仍可能受设备硬件性能和系统限制的影响,特别是在低端设备上。

功能相对简单:尽管 CameraX 提供了很多基本的相机功能,但对于一些复杂的相机操作(如深度摄像、手动对焦等)可能不如传统相机 API 灵活。

2. ML Kit简介

2.1 ML Kit介绍

Q1: 什么是 ML Kit?

ML Kit 是 Google 提供的一个跨平台的机器学习工具包,旨在简化 Android 和 iOS 应用中机器学习功能的集成。它提供了一些现成的 API,帮助开发者在应用中快速实现各种机器学习任务,如图像分析、文字识别、语言处理等,无需深入了解机器学习的细节。

Q2: ML Kit 的工作原理是什么?

ML Kit 的核心是通过集成各种机器学习模型和预训练的 API,简化开发者的工作。开发者只需要调用相应的 API 接口,提供输入数据(如图像、文字、声音等),ML Kit 会自动进行处理并返回结果。ML Kit 支持两种主要的工作模式:

基于云端的 ML(Cloud-based):

ML Kit 使用 Google Cloud 的机器学习模型来处理数据,适用于需要高计算能力或大规模数据集的任务。例如,文字识别、面部识别等任务,处理速度和准确度较高,但需要网络连接。

基于设备的 ML(On-device):

ML Kit 使用本地设备的硬件资源来处理数据,无需互联网连接。它通过优化后的轻量级模型进行计算,适用于低延迟、隐私敏感的任务。例如,面部检测、条码扫描、语言翻译等任务。

Q3: ML Kit 一般用于哪里?有什么优缺点?

适用场景:

文字识别:将图像中的文字转换为文本,例如在文档扫描、图片文字识别中使用。

条码识别:快速扫描条形码和二维码,广泛应用于购物、票务等场景。

人脸检测:分析图像中的人脸,识别面部特征,用于人脸识别、面部表情分析等应用。

图像标记:识别图像中的常见对象,如动物、植物、物品等,用于图像分类、物体识别等。

语言翻译:通过机器学习技术对不同语言的文本进行实时翻译。

姿势检测:识别人体的关键姿势,用于运动、健身、游戏等场景。

优点:

易于使用:ML Kit 提供简单的 API 接口,开发者可以快速集成各种机器学习功能,而无需深入了解机器学习的技术细节。

预训练模型:ML Kit 提供的许多功能已经包含了预训练好的机器学习模型,减少了开发和训练模型的时间和成本。

跨平台支持:ML Kit 不仅支持 Android 平台,也支持 iOS,开发者可以在不同平台之间共享机器学习代码。

本地化支持:部分功能(如人脸检测、条码识别等)支持本地计算,无需网络连接,适合离线使用。

高效性:基于设备的模型使用本地硬件计算,具有较低的延迟和较好的性能。

项目效果图

:有人脸边框 ,眼睛 鼻子 嘴巴 眉毛

1 。导入依赖包

java 复制代码
        implementation 'androidx.constraintlayout:constraintlayout:2.1.4'

        // ML Kit 人脸检测
        implementation 'com.google.mlkit:face-detection:16.1.5'

//        // CameraX
        def camerax_version = "1.3.1"
        implementation "androidx.camera:camera-core:${camerax_version}"
        implementation "androidx.camera:camera-camera2:${camerax_version}"
        implementation "androidx.camera:camera-lifecycle:${camerax_version}"
        implementation "androidx.camera:camera-view:${camerax_version}"

2 。AndroidManifest.xml 添加权限 和配置Activity

java 复制代码
    <!-- 摄像头权限 -->
    <uses-permission android:name="android.permission.CAMERA" />
    <!-- 声明使用摄像头特性 -->
    <uses-feature android:name="android.hardware.camera" />
    <uses-feature android:name="android.hardware.camera.autofocus" />

        <activity
            android:name=" .RenLianActivity"
            android:screenOrientation="portrait"
            android:theme="@style/NoActionBarCustoms" />

3.RenLianActivity

java 复制代码
package com.sbas.mybledemohk;

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Gravity;
import android.view.Surface;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.AspectRatio;
import androidx.camera.core.CameraSelector;
import androidx.camera.core.ImageAnalysis;
import androidx.camera.core.ImageCapture;
import androidx.camera.core.ImageCaptureException;
import androidx.camera.core.ImageProxy;
import androidx.camera.core.Preview;
import androidx.camera.lifecycle.ProcessCameraProvider;
import androidx.camera.view.PreviewView;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

import com.google.android.material.button.MaterialButton;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.mlkit.vision.common.InputImage;
import com.google.mlkit.vision.face.Face;
import com.google.mlkit.vision.face.FaceDetection;
import com.google.mlkit.vision.face.FaceDetector;
import com.google.mlkit.vision.face.FaceDetectorOptions;
import com.google.mlkit.vision.face.FaceContour;
import com.google.mlkit.vision.face.FaceLandmark;

import java.io.File;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import android.annotation.SuppressLint;

@SuppressWarnings("deprecation")
@SuppressLint("UnsafeOptInUsageError")
public class RenLianActivity extends AppCompatActivity {


    private Handler  handler = new Handler(){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            takePhoto();
        }
    };

    private static final String TAG = "MainActivity";
    private static final int REQUEST_CODE_PERMISSIONS = 10;
    private static final String[] REQUIRED_PERMISSIONS = new String[]{Manifest.permission.CAMERA};

    private PreviewView viewFinder;
    private ExecutorService cameraExecutor;
    private FaceDetector faceDetector;
    private FaceOverlayView faceOverlayView;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);

        viewFinder = findViewById(R.id.viewFinder);
        faceOverlayView = findViewById(R.id.faceOverlay);

        // 检查权限
        if (allPermissionsGranted()) {
            startCamera();
        } else {
            ActivityCompat.requestPermissions(this, REQUIRED_PERMISSIONS, REQUEST_CODE_PERMISSIONS);
        }

        // 设置人脸检测器 它用于处理从相机获取的图像并检测人脸。使用FaceDetectorOptions来设置检  测的性能模式、轮廓模式和分类模式。
        FaceDetectorOptions options = new FaceDetectorOptions.Builder()
                .setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
                .setContourMode(FaceDetectorOptions.CONTOUR_MODE_ALL)
                .setClassificationMode(FaceDetectorOptions.CLASSIFICATION_MODE_ALL)
                .build();
        faceDetector = FaceDetection.getClient(options);

        cameraExecutor = Executors.newSingleThreadExecutor();
    }
    ImageCapture imageCapture;
    private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);

        cameraProviderFuture.addListener(() -> {
            try {
            
                ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
       //在此方法中,使用ProcessCameraProvider来绑定前置相机(CameraSelector.DEFAULT_FRONT_CAMERA)并设置预览、图像分析等。 
//Preview:用于显示相机的预览视图。
//ImageAnalysis:处理每一帧图像,进行人脸分析。
                Preview preview = new Preview.Builder()
                        .setTargetRotation(Surface.ROTATION_0)
                        .build();

                preview.setSurfaceProvider(viewFinder.getSurfaceProvider());
                viewFinder.post(() -> {
                    faceOverlayView.setPreviewSize(
                            viewFinder.getWidth(),
                            viewFinder.getHeight()
                    );
                });
                //拍照   使用这个类  。
                imageCapture = new ImageCapture.Builder().build();
                ImageAnalysis imageAnalysis = new ImageAnalysis.Builder()
                        .setTargetRotation(Surface.ROTATION_0)
                        .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
                        .build();

                imageAnalysis.setAnalyzer(cameraExecutor, this::analyzeFace);

                CameraSelector cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA;

                cameraProvider.unbindAll();
                cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageAnalysis,imageCapture);

            } catch (ExecutionException | InterruptedException e) {
                Log.e(TAG, "相机启动失败", e);
            }
        }, ContextCompat.getMainExecutor(this));
    }
    private  void takePhoto() {
        if(imageCapture == null){
            Toast.makeText(this,"拍照异常",Toast.LENGTH_SHORT).show();
            return;
        }
        File file = new File(getExternalFilesDir(null), System.currentTimeMillis() + ".jpg");
        ImageCapture.OutputFileOptions outputFileOptions = new ImageCapture.OutputFileOptions.Builder(file).build();
        Toast.makeText(this,file.getAbsolutePath().toString(),Toast.LENGTH_SHORT).show();
        imageCapture.takePicture(outputFileOptions, cameraExecutor, new ImageCapture.OnImageSavedCallback() {
            @Override
            public void onImageSaved(@NonNull ImageCapture.OutputFileResults outputFileResults) {
                Uri uri = outputFileResults.getSavedUri();
                if(uri != null){
                    File absoluteFile = file.getAbsoluteFile();
                    Log.i(TAG, "onImageSaved: absoluteFile:" + absoluteFile);
                }

            }
            @Override
            public void onError(@NonNull ImageCaptureException exception) {
                Log.i(TAG, "onError: " + exception.getMessage(),exception);
            }
        });
    }

    private void  UploadImg(File file )  {

    }
    long  last_time  = 0;
    @SuppressWarnings("deprecation")
    private void analyzeFace(@NonNull ImageProxy image) {
        // 更新图像信息
        faceOverlayView.setImageSourceInfo(
                image.getWidth(),
                image.getHeight(),
                image.getImageInfo().getRotationDegrees()
        );
//在analyzeFace()方法中,通过InputImage.fromMediaImage()将ImageProxy转换为InputImage,然后调用faceDetector.process(inputImage)进行人脸检测。
        InputImage inputImage = InputImage.fromMediaImage(
                image.getImage(),
                image.getImageInfo().getRotationDegrees()
        );

        faceDetector.process(inputImage)
                .addOnSuccessListener(faces -> {
                    runOnUiThread(() -> {
                        if((System.currentTimeMillis() - last_time)>150000) {
                            for (Face face : faces) {
                                for (FaceContour contour : face.getAllContours()) {
                                    if (contour.getFaceContourType() == FaceContour.LOWER_LIP_BOTTOM) {  //识别到人脸自动拍照  ,间隔15S。
                                            last_time = System.currentTimeMillis();
                                            handler.sendEmptyMessageDelayed(0,1000);
                                    }
                                }
                            }
                        }
                        //绘制人脸信息
   //通过自定义的 FaceOverlayView 绘制检测结果,包括:  微笑指数、眼睛睁开状态等文本信息。
                        faceOverlayView.updateFaces(faces);
                    });
                })
                .addOnFailureListener(e -> Log.e(TAG, "人脸检测失败", e))
                .addOnCompleteListener(result -> image.close());
    }

    private boolean allPermissionsGranted() {
        for (String permission : REQUIRED_PERMISSIONS) {
            if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
                return false;
            }
        }
        return true;
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE_PERMISSIONS) {
            if (allPermissionsGranted()) {
                startCamera();
            } else {
                Toast.makeText(this, "未授予权限,无法使用相机", Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        cameraExecutor.shutdown();
        handler.removeMessages(0);
    }
}

4 activity_main2.xml

java 复制代码
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

        <!-- CameraX预览视图 -->
        <androidx.camera.view.PreviewView
            android:id="@+id/viewFinder"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <!-- 自定义绘制人脸数据的视图 -->
        <com.sbas.mybledemohk.FaceOverlayView
            android:id="@+id/faceOverlay"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

FaceOverlayView

java 复制代码
package com.sbas.mybledemohk;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

import com.google.mlkit.vision.face.Face;
import com.google.mlkit.vision.face.FaceContour;

import java.util.ArrayList;
import java.util.List;
//代码定义了一个自定义的FaceOverlayView类,它继承自View,用于在Android应用中显示基于ML Kit人脸检测的结果,包括面部轮廓、表情(如微笑、眼睛睁开等)和其他信息(如表情符号等)。下面是代码的详细解释:
public class FaceOverlayView extends View {
    private Paint facePaint;
    private Paint contourPaint;
    private Paint textPaint;
    private Paint emojiBgPaint;
    private List<Face> faces;
    private int previewWidth;
    private int previewHeight;
    private float scaleX;
    private float scaleY;
    private int imageWidth;
    private int imageHeight;
    private int rotation;
    private float emojiSize = 60f;
    private String currentMood = "😐";
    private float blinkProgress = 0f;
    private boolean isBlinking = false;

    public FaceOverlayView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        facePaint = new Paint();
        facePaint.setColor(Color.WHITE);
        facePaint.setStyle(Paint.Style.STROKE);
        facePaint.setStrokeWidth(2.0f);

        contourPaint = new Paint();
        contourPaint.setColor(Color.YELLOW);
        contourPaint.setStyle(Paint.Style.FILL);
        contourPaint.setStrokeWidth(2.0f);

        textPaint = new Paint();
        textPaint.setColor(Color.WHITE);
        textPaint.setTextSize(40f);
        textPaint.setAntiAlias(true);

        emojiBgPaint = new Paint();
        emojiBgPaint.setColor(Color.argb(100, 0, 0, 0));
        emojiBgPaint.setStyle(Paint.Style.FILL);

        faces = new ArrayList<>();
    }

    public void setImageSourceInfo(int width, int height, int rotation) {
        this.imageWidth = width;
        this.imageHeight = height;
        this.rotation = rotation;
        calculateScaleFactor();
    }

    public void setPreviewSize(int width, int height) {
        previewWidth = width;
        previewHeight = height;
        calculateScaleFactor();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        calculateScaleFactor();
    }

    private void calculateScaleFactor() {
        if (imageWidth > 0 && imageHeight > 0 && previewWidth > 0 && previewHeight > 0) {
            float targetWidth = previewWidth * 0.4f;
            scaleX = targetWidth / imageWidth;
            scaleY = scaleX;
            scaleX = (float) 3;
            scaleY = (float) 3;
            Log.i("tag",scaleX  +"   "+scaleY);
        }
    }

    public void updateFaces(List<Face> faces) {
        this.faces = faces;
        invalidate();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        if (faces == null || faces.isEmpty() || imageWidth == 0 || imageHeight == 0) {
            return;
        }



        for (Face face : faces) {
            String mood = "😐";
            if (face.getSmilingProbability() != null) {
                float smileProb = face.getSmilingProbability();
                if (smileProb > 0.8) mood = "😄";
                else if (smileProb > 0.3) mood = "🙂";
            }

            if (face.getLeftEyeOpenProbability() != null && face.getRightEyeOpenProbability() != null) {
                float leftEye = face.getLeftEyeOpenProbability();
                float rightEye = face.getRightEyeOpenProbability();
                if (leftEye < 0.3 && rightEye < 0.3) {
                    mood = "😉";
                }
            }
            currentMood = mood;

            for (FaceContour contour : face.getAllContours()) {
                switch (contour.getFaceContourType()) {
                    case FaceContour.FACE:
                        contourPaint.setColor(Color.RED);
                        break;
                    case FaceContour.LEFT_EYE:
                    case FaceContour.RIGHT_EYE:
                        contourPaint.setColor(Color.CYAN);
                        break;
                    case FaceContour.LEFT_EYEBROW_TOP:
                    case FaceContour.RIGHT_EYEBROW_TOP:
                    case FaceContour.LEFT_EYEBROW_BOTTOM:
                    case FaceContour.RIGHT_EYEBROW_BOTTOM:
                        contourPaint.setColor(Color.BLUE);
                        break;
                    case FaceContour.NOSE_BRIDGE:
                    case FaceContour.NOSE_BOTTOM:
                        contourPaint.setColor(Color.RED);
                        break;
                    case FaceContour.UPPER_LIP_TOP:
                    case FaceContour.UPPER_LIP_BOTTOM:
                    case FaceContour.LOWER_LIP_TOP:
                    case FaceContour.LOWER_LIP_BOTTOM:
                        contourPaint.setColor(Color.MAGENTA);
                        break;
                    default:
                        contourPaint.setColor(Color.YELLOW);
                }

                for (PointF point : contour.getPoints()) {
                    float x = point.x * scaleX + 0 ;
                    float y = point.y * scaleY + 0 ;
//                    Log.i("tag",x  +"   "+y);
                    canvas.drawCircle(x, y, 3f, contourPaint);

                    if (contour.getPoints().size() > 1) {
                        Paint linePaint = new Paint(contourPaint);
                        linePaint.setStyle(Paint.Style.STROKE);
                        linePaint.setStrokeWidth(2f);

                        List<PointF> points = contour.getPoints();
                        for (int i = 0; i < points.size() - 1; i++) {
                            PointF p1 = points.get(i);
                            PointF p2 = points.get(i + 1);
                            float x1 = p1.x * scaleX + 0;
                            float y1 = p1.y * scaleY + 0;
                            float x2 = p2.x * scaleX + 0;
                            float y2 = p2.y * scaleY + 0;

                            canvas.drawLine(x1, y1, x2, y2, linePaint);
                        }
                    }
                }
            }
            float offsetX = 50;
            float offsetY = 50;
            float bgLeft = offsetX;
            float bgTop = offsetY + imageHeight * scaleY + 20;
            float bgRight = bgLeft + 300;
            float bgBottom = bgTop + 150;
//            canvas.drawRoundRect(bgLeft, bgTop, bgRight, bgBottom, 20, 20, emojiBgPaint);

//            canvas.drawText(currentMood, bgLeft + 20, bgTop + 50, textPaint);
//            textPaint.setTextSize(30f);

//            if (face.getSmilingProbability() != null) {
//                String smileText = String.format("微笑指数: %.0f%%", face.getSmilingProbability() * 100);
//                canvas.drawText(smileText, bgLeft + 20, bgTop + 100, textPaint);
//            }
//
//            if (face.getLeftEyeOpenProbability() != null) {
//                String eyeText = String.format("眼睛睁开: %.0f%%",
//                        (face.getLeftEyeOpenProbability() + face.getRightEyeOpenProbability()) * 50);
//                canvas.drawText(eyeText, bgLeft + 20, bgTop + 140, textPaint);
//            }
        }

        invalidate();
    }
}

代码定义了一个自定义的FaceOverlayView类,它继承自View,用于在Android应用中显示基于ML Kit人脸检测的结果,包括面部轮廓、表情(如微笑、眼睛睁开等)和其他信息(如表情符号等)。下面是代码的详细解释:

(1) 成员变量

facePaint、contourPaint、textPaint 和 emojiBgPaint:这些是Paint对象,分别用于绘制面部边框、面部轮廓、文本(例如显示微笑指数、眼睛睁开程度等)和表情符号背景。

faces:一个List对象,用于存储当前的所有检测到的人脸。

previewWidth、previewHeight:相机预览的宽度和高度。

scaleX、scaleY:缩放因子,用于将图像的坐标映射到预览视图的坐标。

imageWidth、imageHeight:图像的宽度和高度(即传入的原始图像尺寸)。

rotation:图像的旋转角度。

emojiSize:表情符号的大小。

blinkProgress 和 isBlinking:用于处理眼睛睁开状态和眨眼的进度。

相关推荐
晚霞的不甘2 小时前
Flutter for OpenHarmony 布局核心:Row 与 Column 深度解析与实战
android·前端·javascript·flutter
Shea的笔记本2 小时前
MindSpore实战笔记:Pix2Pix图像转换复现全记录
笔记·算法·机器学习·web3
Yeats_Liao2 小时前
长文本优化:KV Cache机制与显存占用平衡策略
人工智能·深度学习·学习·机器学习·华为
_李小白2 小时前
【Android 美颜相机】第十四天:图片锐化原理
数码相机·opencv·计算机视觉
ll_god2 小时前
android compose ui 结合 ViewModel适配方案
android·ui
2501_948120152 小时前
Android智能手机信息安全管理系统的研究
android·智能手机
DengDongQi3 小时前
Android Compose 应用中实现全局Dialog管理器的设计与实践
android
容华谢后3 小时前
Android消息推送 MQTT方案实践
android
叫我:松哥3 小时前
spark+flask的新能源车数据分析与智能推荐系统,融合大数据分析、机器学习和人工智能技术
人工智能·机器学习·信息可视化·数据分析·spark·flask·bootstrap