├── .gitignore ├── .idea ├── compiler.xml ├── misc.xml ├── modules.xml ├── uiDesigner.xml └── workspace.xml ├── LICENSE ├── README.md ├── pom.xml ├── src └── main │ ├── java │ └── com │ │ └── cqmckj │ │ └── twjc │ │ ├── Twjc.java │ │ └── util │ │ └── Config.java │ └── resources │ └── app.properties └── twjc.iml /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled class file 2 | *.class 3 | 4 | # Log file 5 | *.log 6 | 7 | # BlueJ files 8 | *.ctxt 9 | 10 | # Mobile Tools for Java (J2ME) 11 | .mtj.tmp/ 12 | 13 | # Package Files # 14 | *.jar 15 | *.war 16 | *.nar 17 | *.ear 18 | *.zip 19 | *.tar.gz 20 | *.rar 21 | 22 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 23 | hs_err_pid* 24 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | Android 19 | 20 | 21 | Android > Lint > Correctness 22 | 23 | 24 | Android Lint for Kotlin 25 | 26 | 27 | Java 28 | 29 | 30 | Java language level migration aidsJava 31 | 32 | 33 | 34 | 35 | Android 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/uiDesigner.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 66 | 67 | 68 | 71 | 72 | 73 | 84 | 85 | 86 | 87 | 88 | true 89 | DEFINITION_ORDER 90 | 91 | 92 | 99 | 100 | 101 | 102 | 103 | 104 | 107 | 108 | 111 | 112 | 115 | 116 | 117 | 118 | 121 | 122 | 125 | 126 | 129 | 130 | 133 | 134 | 135 | 136 | 139 | 140 | 143 | 144 | 145 | 146 | 147 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 177 | 178 | 181 | 182 | 183 | 184 | 187 | 188 | 191 | 192 | 195 | 196 | 197 | 198 | 201 | 202 | 205 | 206 | 209 | 210 | 213 | 214 | 217 | 218 | 219 | 220 | 223 | 224 | 227 | 228 | 231 | 232 | 235 | 236 | 239 | 240 | 243 | 244 | 247 | 248 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 288 | 289 | 290 | 291 | 292 | 305 | 306 | 307 | 308 | 311 | 312 | 325 | 326 | 327 | 332 | 333 | 334 | 360 | 361 | 362 | 387 | 388 | 395 | 396 | 397 | 410 | 411 | 412 | 413 | 418 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 444 | 462 | 469 | 470 | 471 | 472 | 473 | 474 | 491 | 492 | 513 | 526 | 527 | 536 | 540 | 541 | 542 | 549 | 552 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 636 | 637 | 638 | 648 | 649 | 650 | 667 | 668 | 669 | 670 | 671 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 705 | 712 | 713 | project 714 | 715 | 716 | 717 | 718 | 719 | 720 | 721 | 722 | 723 | 724 | 725 | 726 | 727 | 728 | 729 | 730 | 731 | 732 | 733 | 734 | 735 | 736 | 749 | 750 | 751 | 752 | 753 | 754 | 755 | 756 | 757 | 758 | 759 | 760 | 761 | 762 | 764 | 765 | 766 | 767 | 1581054244217 768 | 774 | 775 | 776 | 777 | 779 | 780 | 781 | 782 | 783 | 784 | 785 | 786 | 787 | 788 | 789 | 790 | 791 | 792 | 793 | 794 | 795 | 796 | 797 | 798 | 799 | 800 | 801 | 802 | 803 | 804 | 805 | 806 | 807 | 808 | 809 | 810 | 811 | 812 | 813 | 814 | 815 | 817 | 818 | 820 | 821 | 822 | 824 | 825 | 826 | 827 | 830 | 831 | 832 | 833 | 834 | 835 | 836 | 837 | 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | 847 | 848 | 849 | 850 | 851 | 852 | 853 | 854 | 855 | 856 | 857 | 858 | 859 | 860 | 861 | 862 | 863 | 864 | 865 | 866 | 867 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | 876 | 877 | 878 | 879 | 880 | 881 | 882 | 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | 891 | 892 | 893 | 894 | 895 | 896 | 897 | 898 | 899 | 900 | 901 | 902 | 903 | 904 | 905 | 906 | 907 | 908 | 909 | 910 | 911 | 912 | 913 | 914 | 915 | 916 | 917 | 918 | 919 | 920 | 921 | 922 | 923 | 924 | 925 | 926 | 927 | 928 | 929 | 930 | 931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 | 940 | 941 | 942 | 943 | 944 | 945 | 946 | 947 | 948 | 949 | 950 | 951 | 952 | 953 | 954 | 959 | 960 | 961 | 962 | 963 | 964 | No facets are configured 965 | 966 | 971 | 972 | 973 | 974 | 975 | 976 | 977 | 982 | 983 | 984 | 985 | 986 | 987 | Android API 26 Platform 988 | 989 | 994 | 995 | 996 | 997 | 998 | 999 | twjc 1000 | 1001 | 1006 | 1007 | 1008 | 1009 | 1010 | 1011 | 1.7 1012 | 1013 | 1018 | 1019 | 1020 | 1021 | 1022 | 1023 | 1024 | 1029 | 1030 | 1031 | 1032 | 1033 | 1034 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 printlin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 简介 2 | 本项目通过人脸识别技术与热成像技术相结合,实现了人体体温自动化检测。使用OpenCV的级联分类器实现人脸位置检测,再结合热成像数据计算人脸位置的最高温度值作为人体体温值。 3 | # 使用 4 | 1. 下载并安装OpenCV 5 | 2. 克隆本仓库`git clone https://github.com/printlin/twjc.git` 6 | 3. 使用IDEA打开工程 7 | 4. 配置参数 8 | 5. 运行 9 | # 环境配置 10 | ### 配置OpenCV Jar包 11 | 依次打开`File > Project Structure > Modules > Dependencies`,点击加号,添加OpenCV安装目录中的opencv-xxx.jar。 12 | ### 配置OpenCV DLL 13 | 依次打开`Run/Debug Configurations > Application > Configuration > VM options`,填入`-Djava.library.path=D:\opencv\opencv\build\java\x64`,等号后面填写您本地的OpenCV目录。 14 | ### 参考 15 | [java 调用opencv IDEA环境配置](https://blog.csdn.net/zwl18210851801/article/details/81075781) 16 | # 参数配置 17 | 在项目resource目录下有一个app.properties配置文件,可进行自定义配置。 18 | - 报警温度`tw.limit=37.4` 19 | - 热成像最大温度`rcx.max=40` 20 | - 热成像最小温度`rcx.min=0` 21 | - 热成像检测范围`rcx.range=10` 22 | - 最高温度点的标记圆圈半径`mark.radius=10` 23 | - 标记的线条大小`mark.thickness=2` 24 | - 标记颜色R通道`mark.color.r=0` 25 | - 标记颜色G通道`mark.color.g=255` 26 | - 标记颜色B通道`mark.color.b=0` 27 | - 彩色相机索引`camera.rgb=0` 28 | - 热成像相机索引`camera.rcx=1` 29 | - 识别模型`detect.path=D:\\opencv\\opencv\\sources\\data\\haarcascades\\haarcascade_frontalface_default.xml` 30 | - 人脸最大像素值`detect.face.max=400` 31 | - 人脸最小像素值`detect.face.min=50` 32 | - 识别间隔毫秒`detect.sleep=50` 33 | # 运行效果 34 | ![效果图1](https://github.com/printlin/images/blob/master/twjc/run1.png) 35 | ![效果图2](https://github.com/printlin/images/blob/master/twjc/run2.png) 36 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | com.cqmckj.twjc 8 | twjc 9 | 1.0-SNAPSHOT 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/java/com/cqmckj/twjc/Twjc.java: -------------------------------------------------------------------------------- 1 | package com.cqmckj.twjc; 2 | 3 | import com.cqmckj.twjc.util.Config; 4 | import org.opencv.core.*; 5 | import org.opencv.imgproc.Imgproc; 6 | import org.opencv.objdetect.CascadeClassifier; 7 | import org.opencv.videoio.VideoCapture; 8 | 9 | import javax.swing.*; 10 | import java.awt.*; 11 | import java.awt.event.WindowAdapter; 12 | import java.awt.event.WindowEvent; 13 | import java.awt.image.BufferedImage; 14 | 15 | /** 16 | * Created by yl 17 | * 2020/2/7. 18 | */ 19 | public class Twjc{ 20 | private boolean isLoop = true; 21 | private JFrame frame; 22 | private JLabel img,gImg; 23 | public Twjc(){ 24 | frame = new JFrame("体温检测"); 25 | frame.setLayout(null); 26 | frame.setSize(1280,530); 27 | Font font = new Font("宋体",0,14); 28 | JLabel labelRgb = new JLabel("彩色相机"); 29 | labelRgb.setFont(font); 30 | labelRgb.setBounds(10, 10, 200, 20); 31 | JLabel labelRcx = new JLabel("热成像相机"); 32 | labelRcx.setFont(font); 33 | labelRcx.setBounds(650, 10, 200, 20); 34 | img = new JLabel(); 35 | img.setBounds(0, 30, 640, 480); 36 | gImg = new JLabel(); 37 | gImg.setBounds(640, 30, 640, 480); 38 | frame.add(labelRgb); 39 | frame.add(labelRcx); 40 | frame.add(img); 41 | frame.add(gImg); 42 | frame.setVisible(true); 43 | int screen_width = Toolkit.getDefaultToolkit().getScreenSize().width; 44 | int screen_height = Toolkit.getDefaultToolkit().getScreenSize().height; 45 | frame.setLocation((screen_width - frame.getWidth()) / 2, 46 | (screen_height - frame.getHeight()) / 2); 47 | frame.addWindowListener(new WindowAdapter() { 48 | public void windowClosing(WindowEvent arg0) { 49 | frame.setVisible(false); 50 | isLoop = false; 51 | System.exit(0); 52 | } 53 | }); 54 | frame.setVisible(true); 55 | 56 | init(); 57 | } 58 | 59 | static { 60 | //加载opencv 61 | System.loadLibrary(Core.NATIVE_LIBRARY_NAME); 62 | } 63 | private void init(){ 64 | Scalar markColor = null; 65 | Scalar markColorDef = new Scalar(Config.markColorB,Config.markColorG,Config.markColorR); 66 | Scalar markColorErr = new Scalar(0,0,255); 67 | org.opencv.core.Point circlePoint = new org.opencv.core.Point(); 68 | org.opencv.core.Point putTextPoint = new org.opencv.core.Point(); 69 | org.opencv.core.Point rectanglePoint1 = new org.opencv.core.Point(); 70 | org.opencv.core.Point rectanglePoint2 = new org.opencv.core.Point(); 71 | 72 | VideoCapture videoCaptureRgb = new VideoCapture(); 73 | VideoCapture videoCaptureRcx = new VideoCapture(); 74 | CascadeClassifier faceDetector = new CascadeClassifier(Config.detectPath); 75 | if (!videoCaptureRgb.open(Config.cameraRgb)) { 76 | System.out.println("彩色相机打开失败"); 77 | System.exit(0); 78 | return; 79 | } 80 | if (!videoCaptureRcx.open(Config.cameraRcx)) { 81 | System.out.println("热成像相机打开失败"); 82 | videoCaptureRgb.release(); 83 | System.exit(0); 84 | return; 85 | } 86 | Mat rgbImage = new Mat(); 87 | Mat rcxImage = new Mat(); 88 | MatOfRect faceDetections = new MatOfRect(); 89 | while (isLoop) { 90 | //限制检测频率 91 | try {Thread.sleep(Config.detectSleep);} catch (InterruptedException e) {} 92 | //获得彩色图像 93 | if (!videoCaptureRgb.read(rgbImage)) { 94 | continue; 95 | } 96 | //获得热成像图像 97 | if (!videoCaptureRcx.read(rcxImage)) { 98 | continue; 99 | } 100 | //基于彩色图像进行人脸识别 101 | faceDetector.detectMultiScale(rgbImage, faceDetections); 102 | //遍历识别结果 103 | for(Rect rect:faceDetections.toArray()){ 104 | //人脸大小过滤 105 | if(rect.width>Config.detectFaceMax || rect.widthConfig.twLimit){ 115 | markColor=markColorErr; 116 | tempErr(faceImg,temp); 117 | }else{ 118 | markColor=markColorDef; 119 | } 120 | //绘图 121 | circlePoint.x=rect.x+nubRe[1]; 122 | circlePoint.y=rect.y+nubRe[2]; 123 | Imgproc.circle(rgbImage,circlePoint,Config.markRadius,markColor,Config.markThickness); 124 | putTextPoint.x=rect.x+2; 125 | putTextPoint.y=rect.y+20; 126 | Imgproc.putText(rgbImage,temp+"",putTextPoint,0, .7, markColor,Config.markThickness,Imgproc.LINE_AA,false); 127 | rectanglePoint1.x=rect.x; 128 | rectanglePoint1.y=rect.y; 129 | rectanglePoint2.x=rect.x + rect.width; 130 | rectanglePoint2.y=rect.y + rect.height; 131 | Imgproc.rectangle(rgbImage, rectanglePoint1, rectanglePoint2,markColor,Config.markThickness); 132 | } 133 | //UI显示图像 134 | img.setIcon(new ImageIcon(conver2Image(rgbImage))); 135 | gImg.setIcon(new ImageIcon(conver2Image(rcxImage))); 136 | } 137 | //释放资源 138 | videoCaptureRgb.release(); 139 | videoCaptureRcx.release(); 140 | faceDetections.release(); 141 | rgbImage.release(); 142 | rcxImage.release(); 143 | } 144 | 145 | /** 146 | * 温度异常回调函数 147 | * @param faceImg 人脸图像 148 | * @param temp 温度 149 | */ 150 | private static void tempErr(Mat faceImg,float temp){ 151 | //TODO 自行实现 152 | } 153 | 154 | /** 155 | * 热成像转换为温度 156 | * @param temp 色彩平均值 157 | * @return 热成像上下限中的比值,一位小数 158 | */ 159 | private static float formatTemp(int temp){ 160 | return ((int)((temp/255f)*(Config.rcxMax-Config.rcxMin)*10))/10f; 161 | } 162 | 163 | /** 164 | * 从图像中查找最高的温度范围 165 | * @param image 图像 166 | * @return 0:温度最大值 1:x位置 2:y位置 167 | */ 168 | private static int[] findMaxTemp(BufferedImage image){ 169 | int[] re = {0,0,0}; 170 | int width = image.getWidth(); 171 | int height = image.getHeight(); 172 | int avg,max=-1,maxX=-1,maxY=-1; 173 | for(int i = 0;imax){ 177 | maxX = i; 178 | maxY = j; 179 | max = avg; 180 | } 181 | } 182 | } 183 | re[0] = max; 184 | re[1] = maxX; 185 | re[2] = maxY; 186 | return re; 187 | } 188 | 189 | /** 190 | * 统计一定范围像素点的GRB平均值 191 | * @param image 图像 192 | * @param x 像素点x位置 193 | * @param y 像素点y位置 194 | * @param len 范围,以xy为起点的正方形边长 195 | * @return 平均值 196 | */ 197 | private static int avgPix(BufferedImage image,int x,int y,int len){ 198 | int sum = 0; 199 | for(int i=0;i>16)&0xff) + ((rgb>>8)&0xff) +(rgb&0xff) ) / 3; 214 | } 215 | 216 | /** 217 | * opencv图像对象转BufferedImage 218 | * @param mat opencv图像对象 219 | * @return BufferedImage 220 | */ 221 | private static BufferedImage conver2Image(Mat mat) { 222 | int width = mat.cols(); 223 | int height = mat.rows(); 224 | int dims = mat.channels(); 225 | int[] pixels = new int[width*height]; 226 | byte[] rgbdata = new byte[width*height*dims]; 227 | mat.get(0, 0, rgbdata); 228 | BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); 229 | int index = 0; 230 | int r=0, g=0, b=0; 231 | for(int row=0; row 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | --------------------------------------------------------------------------------