树莓派实现人脸打卡机

树莓派搭建nexus2.x私服

树莓派搭建视频监控平台

树莓派视频监控平台实现录制归档

树莓派实现人脸打卡机 (本文)

1. 功能设计

树莓派人脸打卡机,主要包括两个大方向的功能要求:
a. 人脸采集存档
b. 人脸识别签到
这两个功能配合使用就能实现人脸打卡了, 通过人脸采集将人脸信息预存档在系统中,签到的时候,当人靠近摄像头时实时采集人脸,然后比对现有人脸,如果信息匹配则认为签到成功。

下面是签到的效果:
当人脸签到成功后,程序界面底部会显示签到时间和签到人的工号。

raspi-face-sign-in

2. 开发人脸采集模块

人脸采集模块主要的工作就是从摄像头采集视频帧,然后交给界面回显,这里使用的是JavaCV中的opencv模块。
频繁采集视频帧是一个很耗CPU的过程,我在这里做了一些优化处理,即:当检测到没有人脸的时候,程序休眠更长的时间(1秒),而当检测到人脸时,采集间隔调整为180毫秒。

下面是完整的代码:

/** * @author itqn */ public class FaceCapture implements Runnable { private VideoCapture capture; private CascadeClassifier classifier; private OpenCVFrameConverter.ToMat matConvert; private JavaFXFrameConverter converter; private BiConsumer<Image, Rect> videoConsumer; public FaceCapture(BiConsumer<Image, Rect> videoConsumer) { this.videoConsumer = videoConsumer; init(); } private void init() { capture = new VideoCapture(); classifier = new CascadeClassifier("samples//haarcascade_frontalface_alt.xml"); matConvert = new OpenCVFrameConverter.ToMat(); converter = new JavaFXFrameConverter(); capture.open(0); } private void destroy() { capture.close(); } @Override public void run() { boolean find; Mat image = new Mat(); RectVector vector = new RectVector(); while (capture.isOpened()) { find = false; capture.read(image); classifier.detectMultiScale(image, vector); for (Rect rect : vector.get()) { find = true; Image video = converter.convert(matConvert.convert(image)); videoConsumer.accept(video, rect); break; } // if no face sleep 1 second try { if (find) { TimeUnit.MILLISECONDS.sleep(180); } else { TimeUnit.SECONDS.sleep(1); } } catch (InterruptedException ignore) { } } } }

这里调用者通过注册videoConsumer,来消费采集到的人脸图片,以及人脸区域。

3. 开发人脸识别模块

人脸识别这里直接采用opencv的native API,采用直方图对比的方式对比,这里采用相关性数据作为人脸识别成功的基准,如果相关度高于0.7则认为人脸匹配。
程序通过将采集到的人脸信息跟已经存档的人脸信息注意对比,到达基准0.7以上则返回工号(图片是以工号命名的)。

private static final double EXPECT_SCORE = 0.7d; public static String parser(String tmp, String dir) { Mat tmpImg = Imgcodecs.imread(tmp, 1); File imgDir = new File(dir); String[] fList = imgDir.list((d, n) -> n.endsWith(".png")); if (fList == null) { return null; } for (String f : fList) { Mat dstImg = Imgcodecs.imread(dir + File.separator + f, 1); Mat h1 = new Mat(); Mat h2 = new Mat(); Imgproc.calcHist(Collections.singletonList(tmpImg), channels, new Mat(), h1, histSize, ranges); Imgproc.calcHist(Collections.singletonList(dstImg), channels, new Mat(), h2, histSize, ranges); Core.normalize(h1, h1, 0d, 1d, Core.NORM_MINMAX, -1, new Mat()); Core.normalize(h2, h2, 0d, 1d, Core.NORM_MINMAX, -1, new Mat()); double score = Imgproc.compareHist(h1, h2, Imgproc.HISTCMP_CORREL); if (score > EXPECT_SCORE) { return f.substring(0, f.length() - 4); } } return null; }

这里也可以将图片灰度化处理再对比。

Imgproc.cvtColor(dst, hsv, Imgproc.COLOR_BGR2GRAY); 4. 开发界面控制层

界面使用JavaFX来开发,功能比较单一,只要程序启动的时候,启动视频采集线程即可。
这里需要注意的是,当长时间没有识别到人脸的时候,界面不应该显示之前的人脸信息, 所以需要另起一个线程来监控是否有人脸识别信息,如果没有,则显示默认的图片。

人脸采集回显部分

private void startVideoCapture() { new Thread(new FaceCapture((v, r) -> { Image tmp = FaceUtils.sub(v, r.x(), r.y(), r.width(), r.height()); try { FaceUtils.store(tmp, tmpPath); String id = FaceParser.parser(tmpPath, dir); if (id != null) { Platform.runLater(() -> { message.setText(sdf.format(new Date()) + ", 工号:" + id + "签到成功。"); // for sign service }); } } catch (IOException e) { alert.setContentText(e.getMessage()); alert.show(); } Platform.runLater(() -> { video.setImage(v); timestamp.set(System.currentTimeMillis()); if (!find.get()) { avatar.setImage(tmp); find.set(true); } }); })).start(); }

空闲监控,显示默认图部分

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wsxfsx.html