加载中…
个人资料
  • 博客等级:
  • 博客积分:
  • 博客访问:
  • 关注人气:
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
正文 字体大小:

虹软人脸识别SDK预览画面人脸框定位问题总结

(2021-05-14 17:03:56)
标签:

虹软

分类: Android点滴
       目前人工智能是一项热门流行的技术,不仅BAT等大厂将其做为长远战略方向,而且国家也在对相关产业进行大力推广和扶持。一方面,我们可以看到AI算法毕业生供不应求,另一方面,我们也可以看到各种应用场景层出不穷,例如机场安检,小区车辆管理系统等等。
       虹软推出的人脸识别手机端离线增值SDK可以说是人脸识别应用领域被广泛采用,并且性价比极高的一款开发包。下面笔者就采用虹软SDK开发手机应用过程中,摄像头预览画面人脸检测框显示方面容易出错的相关知识做一个总结,以飨读者。

1.坐标转换
1.1.关于坐标系
       众所周知,手机摄像头的安装位置是固定的,一般情况下它的顶端在手机自然方向的右侧,所以当手机横向放置时由于后置摄像头坐标系和手机坐标系是一致的所以基本不需要处理,预览图片多可以正常显示出来,相关坐标也不需要转换。对于横向放置手机的前置摄像头预览画面,由于系统会做一个镜像处理,相应的坐标也要做镜像转换。
虹软人脸识别SDK预览画面人脸框定位问题总结
手机的自然方向,红色表示摄像头顶部
虹软人脸识别SDK预览画面人脸框定位问题总结
手机横向时,手机坐标和后置摄像头坐标一致
虹软人脸识别SDK预览画面人脸框定位问题总结
手机竖屏时,手机坐标(蓝色)和后置摄像头坐标(绿色)
虹软人脸识别SDK预览画面人脸框定位问题总结
手机竖屏时,手机坐标(蓝色)和前置摄像头坐标(绿色)

1.2.坐标变换
       对于非横向放置的手机,由于摄像头坐标系与手机坐标系不一致,所以我们需要做一个坐标变换。记住,无论手机如何旋转(手机坐标怎么变化),摄像头的坐标系是不变的。而虹软SDK返回的人脸矩形框的坐标是基于摄像头坐标系的。所以,坐标变换本质是将摄像头坐标系的坐标变换成手机坐标系的坐标。
       下面以手机竖屏为例加以说明。

1.2.1.后置摄像头(小人为他人)
虹软人脸识别SDK预览画面人脸框定位问题总结
       绿色为虹软sdk返回的坐标(基于摄像头坐标系),蓝色为手机当前坐标系,即需要转换的目标坐标系。从上图不难看出转换公式为:
destLeft = screenWidth - bottom
destTop = left
destRight = screenWidth - top
destBottom = right

1.2.2.前置摄像头(小人为自己)
虹软人脸识别SDK预览画面人脸框定位问题总结
       前置摄像头和后置摄像头比不同处在于它有一个镜像,所以转换如下:
destLeft = screenWidth - bottom
destTop = screenHeight - right
destRight = screenWidth - top
destBottom = screenHeight - left

       以上为手机竖屏portrait方向,其他手机方向类似。

2.图片缩放
       人脸识别领域需要对图片做大量的处理,因为考虑到内存的限制,我们会根据屏幕的分辨率来确定摄像头的预览分辨率。如果分辨率过大,很容易导致oom。人脸框的定位是一个精准的工作,为了处理准确和方便,我们一般以像素来做为处理坐标的单位。
       笔者在开发中,根据需求会将预览的nv21图片转换为bitmap,然后再对位图进行缩放,最后在bitmap上画人脸框。但结果总是画不准。经过研究,发现问题出在对位图的缩放上。
      原来我们会通过BitmapFactory.Options来设置缩放的参数,但该类有两个密度属性inDensity和inTargetDensity,也就是说Bitmap是有密度属性的,它的宽和高并不是以像素为单位,而是以dp为单位,密度不同会影响到转换后位图的实际大小,导致后续坐标处理出现偏差。如果为了处理方便想以像素为单位,我们缩放位图时要将其密度设置为160。
   
    static public Bitmap scaleBitmap(String imagePath, int maxWidth, int maxHeight)
    {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        options.inDensity=160;
        options.inTargetDensity=160;
        BitmapFactory.decodeFile(imagePath, options);

        int inSampleSize = 1;
        while(options.outWidth / inSampleSize > maxWidth || options.outHeight / inSampleSize > maxHeight)
        {
            inSampleSize++;
        }

        options.inSampleSize = inSampleSize;
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFile(imagePath, options);
    }

0

阅读 收藏 喜欢 打印举报/Report
  

新浪BLOG意见反馈留言板 欢迎批评指正

新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 产品答疑

新浪公司 版权所有