在完成了物体检测功能的开发后,我对鸿蒙系统的视觉能力有了越来越深的了解。老板似乎也看到了我在这方面的成长,直接给我布置了一个新任务——实现骨骼检测功能。骨骼检测的目标是识别图像或视频中人体的骨骼结构,标记出关键的关节点位置,比如头部、手肘、膝盖等位置。这项任务听起来非常有趣,但也不乏挑战。

image.png

老板仍然是那样,直接甩给我一个链接——鸿蒙的骨骼检测API文档。看着文档,我感觉自己好像成了鸿蒙视觉API的“代言人”了。既然任务已经来了,那就撸起袖子开始干吧。

第一步:理解骨骼检测API

根据文档的描述,骨骼检测的核心是利用MLSkeletonAnalyzer类,它可以检测图片或视频中的人体骨骼关节点,返回这些关节点的位置和连接关系。这些信息可以用来做很多有趣的事情,比如人体姿势分析、运动跟踪等。我想象了一下,骨骼检测的应用场景非常广泛,不仅可以用在健身应用中进行动作纠正,还可以用于游戏中进行动作捕捉。

image.png

理解了API的基本功能后,我决定先实现一个简单的demo,看看这个骨骼检测的效果如何,再根据效果进行调优和扩展。

第二步:配置开发环境并编写代码

根据文档的提示,我首先在项目中引入了骨骼检测的依赖。

dependencies {
    implementation 'com.huawei.hms:ml-computer-vision:5.0.0.300'
}

接下来,我开始编写骨骼检测的代码。为了实现这个功能,我创建了一个类,用于封装骨骼检测的逻辑。

  1. 初始化骨骼检测器

首先,我们需要初始化骨骼检测器。在初始化过程中,可以配置一些检测参数。

import com.huawei.hms.mlsdk.skeleton.MLSkeletonAnalyzer;
import com.huawei.hms.mlsdk.skeleton.MLSkeletonAnalyzerSetting;
import com.huawei.hms.mlsdk.skeleton.MLSkeleton;
import com.huawei.hms.mlsdk.common.MLFrame;

public class SkeletonDetectionDemo {
    private MLSkeletonAnalyzer skeletonAnalyzer;

    public void initializeAnalyzer() {
        MLSkeletonAnalyzerSetting setting = new MLSkeletonAnalyzerSetting.Factory()
                .setSyncType(MLSkeletonAnalyzerSetting.TYPE_SYNC)
                .create();
        skeletonAnalyzer = MLSkeletonAnalyzer.Factory.getInstance().getSkeletonAnalyzer(setting);
    }
}

在这段代码中,我创建了一个MLSkeletonAnalyzerSetting,并设置了同步模式。同步模式意味着处理完一帧图像后,会立即得到结果,适合用于图片的骨骼检测。

  1. 实现骨骼检测

初始化完骨骼检测器后,我编写了一个方法,用于检测图片中的骨骼。

public void detectSkeleton(Bitmap bitmap) {
    MLFrame frame = MLFrame.fromBitmap(bitmap);
    Task<List<MLSkeleton>> task = skeletonAnalyzer.asyncAnalyseFrame(frame);
    
    task.addOnSuccessListener(skeletons -> {
        for (MLSkeleton skeleton : skeletons) {
            List<MLSkeleton.Point> points = skeleton.getJoints();
            for (MLSkeleton.Point point : points) {
                System.out.println("关节点类型: " + point.getType() + ", 坐标: (" + point.getPosition().x + ", " + point.getPosition().y + ")");
            }
        }
    }).addOnFailureListener(e -> {
        System.err.println("骨骼检测失败: " + e.getMessage());
    });
}

在这段代码中,我使用asyncAnalyseFrame方法来检测图像中的骨骼结构。检测完成后,返回了一个骨骼关节点的列表,每个关节点包含位置和类型信息,这些信息可以用来在图像上绘制出人体骨骼的结构。

第三步:测试与调优

在完成了代码编写后,我开始测试骨骼检测的功能。为了验证它的效果,我找了一些健身动作的照片和一些日常生活中的人物照片,看看骨骼检测的表现如何。初次测试的结果还算不错,大部分关节点都能正确地识别出来,尤其是较为标准的姿势,比如站立、行走等。

不过,当我用一些复杂姿势的图片进行测试时,发现有些关节点检测得并不理想,尤其是手臂和腿交叉的情况下。为了提高检测的准确性,我参考了文档中的建议,对骨骼检测的参数进行了一些调整,比如切换到TYPE_ASYNC模式,这样可以利用多线程处理,提升检测的速度和准确性。

MLSkeletonAnalyzerSetting setting = new MLSkeletonAnalyzerSetting.Factory()
        .setSyncType(MLSkeletonAnalyzerSetting.TYPE_ASYNC)
        .create();
skeletonAnalyzer = MLSkeletonAnalyzer.Factory.getInstance().getSkeletonAnalyzer(setting);

此外,我还在图像预处理上下了一些功夫,比如先对图像进行灰度转换和对比度增强,这样可以让骨骼更清晰地呈现,从而提高检测的效果。经过几轮的测试和调整,我发现检测的效果明显提升了,尤其是在光线复杂或背景杂乱的情况下。

第四步:功能扩展与实际应用

在完成了骨骼检测的基本功能后,我开始思考如何将它应用到更有趣的场景中。结合之前开发的物体检测和主体分割功能,我决定尝试制作一个实时健身指导的demo,帮助用户在做健身动作时纠正姿势。

首先,我编写了一个方法,用于实时获取视频帧并对每一帧进行骨骼检测,然后将检测结果与标准动作进行对比。

public void analyzeFitnessPose(Bitmap bitmap, List<MLSkeleton> standardSkeletons) {
    MLFrame frame = MLFrame.fromBitmap(bitmap);
    Task<List<MLSkeleton>> task = skeletonAnalyzer.asyncAnalyseFrame(frame);

    task.addOnSuccessListener(userSkeletons -> {
        for (MLSkeleton userSkeleton : userSkeletons) {
            compareSkeletons(userSkeleton, standardSkeletons);
        }
    }).addOnFailureListener(e -> {
        System.err.println("健身姿势分析失败: " + e.getMessage());
    });
}

private void compareSkeletons(MLSkeleton userSkeleton, List<MLSkeleton> standardSkeletons) {
    // 简单对比用户的关节点与标准动作的关节点
    for (MLSkeleton.Point userPoint : userSkeleton.getJoints()) {
        for (MLSkeleton.Point standardPoint : standardSkeletons.get(0).getJoints()) {
            if (userPoint.getType() == standardPoint.getType()) {
                float distance = calculateDistance(userPoint.getPosition(), standardPoint.getPosition());
                System.out.println("关节点 " + userPoint.getType() + " 与标准位置的距离: " + distance);
            }
        }
    }
}

private float calculateDistance(PointF p1, PointF p2) {
    return (float) Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}

通过对比用户的关节点与标准姿势的关节点,我可以计算出每个关节点的偏差,并给出相应的反馈。这种方式非常适合用在健身指导应用中,帮助用户及时纠正错误的姿势,避免运动损伤。

最后的感悟

这次的骨骼检测功能开发让我进一步体会到了图像处理的复杂性和乐趣。骨骼检测不仅仅是识别关节点的位置,还涉及到如何利用这些信息为用户提供有价值的反馈。从理解API到不断优化,再到将功能应用到实际场景,这个过程让我学到了很多。

通过这次开发,我更加深刻地认识到,技术本身只是工具,真正的价值在于如何将这些技术应用到实际生活中去,帮助用户解决问题。无论是物体检测、主体分割,还是这次的骨骼检测,每一次任务都是一次探索未知的机会,每一次改进和调优都是为了让产品更加贴近用户的需求。

image.png

如果你也在从事类似的开发工作,我的建议是:不要害怕复杂的技术,勇敢去尝试。每一次的挑战都是一次成长的机会,只要保持好奇心和耐心,最终你会发现,技术的世界充满了乐趣和无限的可能。开发不仅是实现功能的过程,更是将自己的想法变为现实、为用户创造价值的过程。


郝敬学
22 声望1 粉丝