├── .idea ├── compiler.xml ├── encodings.xml ├── misc.xml └── vcs.xml ├── README.md ├── pom.xml ├── spd.iml └── src ├── main └── java │ ├── algorithm │ └── AlgorithmUtil.java │ ├── detector │ ├── Detector.java │ └── impl │ │ ├── HeadDetector.java │ │ └── UpperBodyDetector.java │ ├── model │ ├── Angle.java │ ├── HeadInfo.java │ ├── Location.java │ ├── Point.java │ ├── SittingPosition.java │ ├── SittingPositionParam.java │ ├── SpdImage.java │ └── UpperBodyInfo.java │ ├── service │ ├── SittingPositionDetection.java │ └── impl │ │ ├── DeepLearningSittingPosition.java │ │ └── SimpleSittingPositionDetection.java │ └── spd │ └── Spd.java └── test └── java └── detector └── impl └── HeadDetectorTest.java /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spd 2 | spd是sitting position detection的缩写 3 | spd是一套基于Baidu AI成熟的人脸检测和人体分析接口实现的一套坐姿识别工具 4 | 5 | # 算法思路 6 | 本项目参考了知网中的一篇论文并针对具体应用调整了部分参数。 7 | 该项目的最初目的是为了实验室的项目服务。 8 | 9 | # 开始使用 10 | 你可以参考下面的测试类,了解如何使用,我们对细节进行了高度的封装,只暴露了一个接口。 11 | 12 | ``` 13 | public class Test{ 14 | 15 | 16 | public static void main(String[] args) throws FileNotFoundException { 17 | HashMap config1=new HashMap(); 18 | config1.put("appId","xxxxxx"); 19 | config1.put("apiKey","xxxxxxxx"); 20 | config1.put("secretKey","xxxxxxxx"); 21 | 22 | HashMap config2=new HashMap(); 23 | config2.put("appId","xxxxxx"); 24 | config2.put("apiKey","xxxxxxx"); 25 | config2.put("secretKey","xxxxxxxxx"); 26 | 27 | Spd spd=Spd.getInstance(config1,config2); 28 | String base=getImageStr("/home/zeng/IdeaProjects/spd/src/main/resources/test7.jpg"); 29 | 30 | //获取图片base64编码信息 31 | //。。。 32 | long s1=System.currentTimeMillis(); 33 | SittingPosition sittingPosition=spd.getSittingPosition(1,base); 34 | long s2=System.currentTimeMillis(); 35 | System.out.println((s2-s1)+"ms"); 36 | System.out.println(sittingPosition); 37 | } 38 | 39 | public static String getImageStr(String imgFile) 40 | {//将图片文件转化为字节数组字符串,并对其进行Base64编码处理 41 | InputStream in = null; 42 | byte[] data = null; 43 | //读取图片字节数组 44 | try 45 | { 46 | in = new FileInputStream(imgFile); 47 | data = new byte[in.available()]; 48 | in.read(data); 49 | in.close(); 50 | } 51 | catch (IOException e) 52 | { 53 | e.printStackTrace(); 54 | } 55 | //对字节数组Base64编码 56 | BASE64Encoder encoder = new BASE64Encoder(); 57 | return encoder.encode(data);//返回Base64编码过的字节数组字符串 58 | } 59 | } 60 | ``` 61 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | cn.finalabproject 8 | spd 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 13 | com.baidu.aip 14 | java-sdk 15 | 4.3.2 16 | 17 | 18 | 19 | com.baidu.aip 20 | java-sdk 21 | 4.8.0 22 | 23 | 24 | 25 | org.projectlombok 26 | lombok 27 | 1.16.20 28 | provided 29 | 30 | 31 | 32 | junit 33 | junit 34 | 4.12 35 | test 36 | 37 | 38 | 39 | 40 | com.google.code.gson 41 | gson 42 | 2.8.5 43 | 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /spd.iml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main/java/algorithm/AlgorithmUtil.java: -------------------------------------------------------------------------------- 1 | package algorithm; 2 | 3 | public class AlgorithmUtil { 4 | // public static Double 5 | } 6 | -------------------------------------------------------------------------------- /src/main/java/detector/Detector.java: -------------------------------------------------------------------------------- 1 | package detector; 2 | 3 | import model.SpdImage; 4 | 5 | public interface Detector { 6 | public boolean init(String appId,String apiKey,String secretKey); 7 | 8 | public Object detection(SpdImage spdImage) throws Exception; 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/detector/impl/HeadDetector.java: -------------------------------------------------------------------------------- 1 | package detector.impl; 2 | 3 | import com.baidu.aip.face.AipFace; 4 | import com.google.gson.Gson; 5 | import detector.Detector; 6 | import model.Angle; 7 | import model.HeadInfo; 8 | import model.Location; 9 | import model.SpdImage; 10 | import org.json.JSONArray; 11 | import org.json.JSONObject; 12 | 13 | import java.util.HashMap; 14 | 15 | /** 16 | * 头部检测器 17 | * @author zeng 18 | */ 19 | public class HeadDetector implements Detector { 20 | private AipFace client=null; 21 | private boolean state=false; 22 | private static HeadDetector headDetector=new HeadDetector(); 23 | 24 | private HeadDetector(){ 25 | 26 | } 27 | 28 | public static HeadDetector getHeadDetecto(){ 29 | return headDetector; 30 | } 31 | 32 | public boolean init(String appId, String apiKey, String secretKey) { 33 | client=new AipFace(appId,apiKey,secretKey); 34 | if (client!=null){ 35 | state=true; 36 | } 37 | return state; 38 | } 39 | 40 | public HeadInfo detection(SpdImage spdImage) throws Exception { 41 | HeadInfo headInfo=new HeadInfo(); 42 | if (!state){ 43 | throw new Exception("检测器初始化失败!"); 44 | } 45 | try { 46 | JSONObject ret=client.detect(spdImage.getImage(),spdImage.getType(),new HashMap()); 47 | 48 | JSONObject result= (JSONObject) ret.get("result"); 49 | 50 | JSONArray faceList= (JSONArray) result.get("face_list"); 51 | 52 | if (faceList.length()<=0){ 53 | return null; 54 | } 55 | //获取人脸信息 56 | JSONObject faceInfo=faceList.getJSONObject(0); 57 | 58 | //获取3d旋转角度 59 | JSONObject angle= (JSONObject) faceInfo.get("angle"); 60 | JSONObject location=(JSONObject)faceInfo.get("location"); 61 | Gson gson=new Gson(); 62 | 63 | if (angle!=null){ 64 | Angle angle1=gson.fromJson(angle.toString(), Angle.class); 65 | headInfo.setAngel(angle1); 66 | }else { 67 | return null; 68 | } 69 | if (location!=null){ 70 | System.out.println(location); 71 | Location location1=gson.fromJson(location.toString(),Location.class); 72 | headInfo.setLocation(location1); 73 | }else { 74 | return null; 75 | } 76 | }catch (Exception e){ 77 | return null; 78 | } 79 | return headInfo; 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/detector/impl/UpperBodyDetector.java: -------------------------------------------------------------------------------- 1 | package detector.impl; 2 | 3 | 4 | import com.baidu.aip.bodyanalysis.AipBodyAnalysis; 5 | import com.baidu.aip.util.Base64Util; 6 | import com.google.gson.Gson; 7 | import detector.Detector; 8 | import model.Point; 9 | import model.SpdImage; 10 | import model.UpperBodyInfo; 11 | import org.json.JSONArray; 12 | import org.json.JSONObject; 13 | 14 | import javax.imageio.stream.FileImageInputStream; 15 | import java.io.ByteArrayOutputStream; 16 | import java.io.File; 17 | import java.io.FileNotFoundException; 18 | import java.io.IOException; 19 | import java.util.HashMap; 20 | 21 | /** 22 | * 上身检测器 23 | * @author zeng 24 | */ 25 | public class UpperBodyDetector implements Detector { 26 | AipBodyAnalysis client=null; 27 | private boolean state=false; 28 | Gson gson=new Gson(); 29 | JSONObject bodyParts=null; 30 | JSONObject location=null; 31 | private static UpperBodyDetector upperBodyDetector=new UpperBodyDetector(); 32 | 33 | private UpperBodyDetector(){ 34 | 35 | } 36 | 37 | public static UpperBodyDetector getUpperBodyDetector(){ 38 | return upperBodyDetector; 39 | } 40 | public boolean init(String appId, String apiKey, String secretKey) { 41 | client=new AipBodyAnalysis(appId,apiKey,secretKey); 42 | if (client==null){ 43 | return false; 44 | } 45 | state=true; 46 | return true; 47 | } 48 | 49 | public UpperBodyInfo detection(SpdImage spdImage) throws Exception { 50 | if (!state){ 51 | return null; 52 | } 53 | UpperBodyInfo upperBodyInfo=new UpperBodyInfo(); 54 | upperBodyInfo.setWidth(spdImage.getWidth()); 55 | upperBodyInfo.setHeight(spdImage.getHeight()); 56 | 57 | byte[] bytes=spdImage.getImageArr(); 58 | JSONObject ret=client.bodyAnalysis(bytes,new HashMap()); 59 | System.out.println(ret); 60 | if (ret==null||ret.get("person_info")==null){ 61 | return null; 62 | } 63 | JSONArray jsonArray= (JSONArray) ret.get("person_info"); 64 | if (jsonArray.length()<=0){ 65 | return null; 66 | } 67 | JSONObject personInfo=jsonArray.getJSONObject(0); 68 | 69 | bodyParts= (JSONObject) personInfo.get("body_parts"); 70 | location=(JSONObject)personInfo.get("location"); 71 | 72 | double height=(Double)location.get("height"); 73 | double width=(Double) location.get("width"); 74 | 75 | upperBodyInfo.setBodyHeight(height); 76 | upperBodyInfo.setBodyWidth(width); 77 | Point nose=getKeyPoint("nose"); 78 | Point neck=getKeyPoint("neck"); 79 | 80 | Point leftShoulder=getKeyPoint("left_shoulder"); 81 | Point rightShoulder=getKeyPoint("right_shoulder"); 82 | 83 | Point leftElbow=getKeyPoint("left_elbow"); 84 | Point rightElbow=getKeyPoint("right_elbow"); 85 | 86 | Point leftWrist=getKeyPoint("left_wrist"); 87 | Point rightWrist=getKeyPoint("right_wrist"); 88 | 89 | upperBodyInfo.setNose(nose); 90 | upperBodyInfo.setNeck(neck); 91 | 92 | upperBodyInfo.setLeftShoulder(leftShoulder); 93 | upperBodyInfo.setRightShoulder(rightShoulder); 94 | 95 | upperBodyInfo.setLeftElbow(leftElbow); 96 | upperBodyInfo.setRightElbow(rightElbow); 97 | 98 | upperBodyInfo.setLeftWrist(leftWrist); 99 | upperBodyInfo.setRightWrist(rightWrist); 100 | 101 | return upperBodyInfo; 102 | } 103 | 104 | public Point getKeyPoint(String key){ 105 | Point point=null; 106 | String jsonNose=bodyParts.get(key).toString(); 107 | point=gson.fromJson(jsonNose, Point.class); 108 | return point; 109 | } 110 | 111 | //图片到byte数组 112 | public byte[] image2byte(String path){ 113 | byte[] data = null; 114 | FileImageInputStream input = null; 115 | try { 116 | input = new FileImageInputStream(new File(path)); 117 | ByteArrayOutputStream output = new ByteArrayOutputStream(); 118 | byte[] buf = new byte[1024]; 119 | int numBytesRead = 0; 120 | while ((numBytesRead = input.read(buf)) != -1) { 121 | output.write(buf, 0, numBytesRead); 122 | } 123 | data = output.toByteArray(); 124 | output.close(); 125 | input.close(); 126 | } 127 | catch (FileNotFoundException ex1) { 128 | ex1.printStackTrace(); 129 | } 130 | catch (IOException ex1) { 131 | ex1.printStackTrace(); 132 | } 133 | return data; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/model/Angle.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Getter 7 | @Setter 8 | public class Angle { 9 | private Double roll; 10 | private Double pitch; 11 | private Double yaw; 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/model/HeadInfo.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | 9 | /** 10 | * 描述头部信息 11 | * @author zeng 12 | */ 13 | @Getter 14 | @Setter 15 | @AllArgsConstructor 16 | @NoArgsConstructor 17 | public class HeadInfo { 18 | //参考宽度 19 | private Integer width; 20 | //参考高度 21 | private Integer height; 22 | //人脸位置 23 | private Location location; 24 | //人脸旋转角度参数 25 | private Angle angel; 26 | //人脸可信度 27 | private Double probability; 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/model/Location.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Setter 7 | @Getter 8 | public class Location { 9 | private Double top; 10 | private Double left; 11 | private Double rotation; 12 | private Double width; 13 | private Double height; 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/model/Point.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | 6 | @Setter 7 | @Getter 8 | public class Point { 9 | private Double x; 10 | private Double y; 11 | @Override 12 | public String toString() { 13 | return "("+this.x+","+this.y+")"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/model/SittingPosition.java: -------------------------------------------------------------------------------- 1 | package model; 2 | /** 3 | * 坐姿数据 4 | * @author zeng 5 | */ 6 | public class SittingPosition { 7 | private int id; 8 | private int uid; 9 | /** 10 | * 0 正常 11 | * 1 头左偏 12 | * 2 头右偏 13 | * 3 左手错误放置 14 | * 4 右手错误放置 15 | * 5 身体倾斜 16 | * 6 趴桌 17 | */ 18 | private int status; 19 | private double degree; 20 | 21 | public int getId() { 22 | return id; 23 | } 24 | 25 | public void setId(int id) { 26 | this.id = id; 27 | } 28 | 29 | public int getUid() { 30 | return uid; 31 | } 32 | 33 | public void setUid(int uid) { 34 | this.uid = uid; 35 | } 36 | 37 | public int getStatus() { 38 | return status; 39 | } 40 | 41 | public void setStatus(int status) { 42 | this.status = status; 43 | } 44 | 45 | public double getDegree() { 46 | return degree; 47 | } 48 | 49 | public void setDegree(double degree) { 50 | this.degree = degree; 51 | } 52 | 53 | @Override 54 | public String toString() { 55 | return "SittingPosition{" + 56 | "id=" + id + 57 | ", uid=" + uid + 58 | ", status=" + status + 59 | ", degree=" + degree + 60 | '}'; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/model/SittingPositionParam.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | /** 4 | * 描述坐姿信息的参照值 5 | */ 6 | public class SittingPositionParam { 7 | //边界1:左偏30度 8 | public static final Integer HEAD_LEFT_ROTATION=12; 9 | //边界2:右偏30度 10 | public static final Integer HEAD_RIGHT_ROTATION=-12; 11 | // 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/model/SpdImage.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | 4 | import com.baidu.aip.util.Base64Util; 5 | import sun.misc.BASE64Decoder; 6 | 7 | /** 8 | * 统一图片形式 9 | */ 10 | public class SpdImage { 11 | public final static String URL="URL"; 12 | public final static String BASE64="BASE64"; 13 | 14 | 15 | private Integer width; 16 | private Integer height; 17 | 18 | private String image=null; 19 | private String type=SpdImage.BASE64; 20 | 21 | public SpdImage(){ 22 | 23 | } 24 | 25 | public SpdImage(String image){ 26 | this.image=image; 27 | } 28 | 29 | public String getImage(){ 30 | return image; 31 | } 32 | 33 | public String getType(){ 34 | return type; 35 | } 36 | 37 | public byte[] getImageArr() { 38 | BASE64Decoder base64Decoder=new BASE64Decoder(); 39 | byte[] bytes=null; 40 | try { 41 | bytes=base64Decoder.decodeBuffer(image); 42 | }catch (Exception e){ 43 | e.printStackTrace(); 44 | } 45 | 46 | return bytes; 47 | } 48 | 49 | public Integer getWidth() { 50 | return width; 51 | } 52 | 53 | public Integer getHeight() { 54 | return height; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/main/java/model/UpperBodyInfo.java: -------------------------------------------------------------------------------- 1 | package model; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | /** 9 | * 描述上半身信息 10 | * @author zeng 11 | */ 12 | @Getter 13 | @Setter 14 | @AllArgsConstructor 15 | @NoArgsConstructor 16 | public class UpperBodyInfo { 17 | //参考宽度 18 | private Integer width; 19 | 20 | //参考高度 21 | private Integer height; 22 | 23 | //关键点1 鼻子 24 | private Point nose; 25 | 26 | //关键点2 颈部 27 | private Point neck; 28 | 29 | //关键点3 左肩 30 | private Point leftShoulder; 31 | 32 | //关键点4 右肩 33 | private Point rightShoulder; 34 | 35 | //关键点5 左手肘 36 | private Point leftElbow; 37 | 38 | //关键点6 右手肘 39 | private Point rightElbow; 40 | 41 | //关键点7 左手腕 42 | private Point leftWrist; 43 | 44 | //关键点8 右手腕 45 | private Point rightWrist; 46 | 47 | private double bodyWidth; 48 | 49 | private double bodyHeight; 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/service/SittingPositionDetection.java: -------------------------------------------------------------------------------- 1 | package service; 2 | 3 | import model.HeadInfo; 4 | import model.SittingPosition; 5 | import model.UpperBodyInfo; 6 | 7 | /** 8 | * 坐姿检测 9 | */ 10 | public interface SittingPositionDetection { 11 | public SittingPosition getSittingPosition(HeadInfo headInfo, UpperBodyInfo upperBodyInfo); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/service/impl/DeepLearningSittingPosition.java: -------------------------------------------------------------------------------- 1 | package service.impl; 2 | 3 | import model.HeadInfo; 4 | import model.SittingPosition; 5 | import model.UpperBodyInfo; 6 | import service.SittingPositionDetection; 7 | 8 | /** 9 | * 具有深度学习能力的坐姿检测服务 10 | */ 11 | public class DeepLearningSittingPosition implements SittingPositionDetection { 12 | public SittingPosition getSittingPosition(HeadInfo headInfo, UpperBodyInfo upperBodyInfo) { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/service/impl/SimpleSittingPositionDetection.java: -------------------------------------------------------------------------------- 1 | package service.impl; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | import model.*; 8 | import service.SittingPositionDetection; 9 | 10 | /** 11 | * 该坐姿检测程序的理论来源于知网的一篇论文的数据,准确性达到百分之90左右 12 | */ 13 | public class SimpleSittingPositionDetection implements SittingPositionDetection { 14 | 15 | /** 16 | * 基于头部信息和人体关键点获取坐姿信息 17 | * @param headInfo 18 | * @param upperBodyInfo 19 | * @return 20 | */ 21 | public SittingPosition getSittingPosition(HeadInfo headInfo, UpperBodyInfo upperBodyInfo) { 22 | SittingPosition sittingPosition=new SittingPosition(); 23 | //数据修复,保证数据残缺时可以得到有效数据 24 | 25 | UnderarmData underarmData=new UnderarmData(); 26 | BodySlant bodySlant=new BodySlant(); 27 | HandPosition handPosition=new HandPosition(); 28 | HeadSlant headSlant=new HeadSlant(); 29 | 30 | if (upperBodyInfo!=null){ 31 | //1. 获取是否趴桌 32 | underarmData=analysisUnderarm(upperBodyInfo); 33 | //2. 身体是否倾斜 34 | bodySlant=analysisBodySlant(upperBodyInfo); 35 | //4. 获取手部位置 36 | handPosition=analysisHandPosition(upperBodyInfo); 37 | } 38 | if (headInfo!=null){ 39 | //3. 获取头部偏离 40 | headSlant=analysisHeadSlantData(headInfo); 41 | } 42 | 43 | 44 | 45 | 46 | //获取标志性错误(错误最大最明显) 47 | if (underarmData.isUnderarm){ 48 | sittingPosition.setStatus(6); 49 | sittingPosition.setDegree(underarmData.getDegree()); 50 | return sittingPosition; 51 | }else if (bodySlant.isBodySlant){ 52 | sittingPosition.setStatus(5); 53 | sittingPosition.setDegree(bodySlant.getDegree()); 54 | return sittingPosition; 55 | }else if (headSlant.isSlant){ 56 | if (headSlant.getDirection()==Direction.LEFT){ 57 | sittingPosition.setStatus(1); 58 | sittingPosition.setDegree(1); 59 | return sittingPosition; 60 | }else { 61 | sittingPosition.setStatus(2); 62 | sittingPosition.setDegree(1); 63 | return sittingPosition; 64 | } 65 | }else { 66 | sittingPosition.setStatus(0); 67 | sittingPosition.setDegree(0); 68 | return sittingPosition; 69 | } 70 | } 71 | 72 | /** 73 | * 百度返回的数据中包含了人脸3d角度的偏移,可以直接通过参数判断头部是否偏了 74 | * 判断头部偏离系数,范围为[0,1],当返回值为0时,表示头部未发生偏离, 75 | * @param headInfo 76 | * @return 偏离系数 77 | */ 78 | private static HeadSlant analysisHeadSlantData(HeadInfo headInfo){ 79 | Double rotation=headInfo.getLocation().getRotation(); 80 | int flag=rotation>0?1:-1; 81 | rotation=Math.abs(rotation); 82 | 83 | //判断是否在合理的范围内 84 | if(rotation<=SittingPositionParam.HEAD_LEFT_ROTATION){ 85 | return new HeadSlant(false,Direction.NO,0);//没有偏离 86 | } 87 | 88 | HeadSlant slantData=new HeadSlant(); 89 | slantData.setDegree(rotation*(1.0/78)-(12.0/78)); 90 | slantData.setSlant(true); 91 | if (flag==1){ 92 | slantData.setDirection(Direction.LEFT); 93 | }else { 94 | slantData.setDirection(Direction.RIGHT); 95 | } 96 | return slantData; 97 | } 98 | 99 | 100 | /** 101 | * 判断是否趴下 102 | * 人体区域高度和人体区域宽度的比值小于常数k 103 | * @param upperBodyInfo 104 | * @return 105 | */ 106 | private static UnderarmData analysisUnderarm(UpperBodyInfo upperBodyInfo){ 107 | double bodyWidth=upperBodyInfo.getBodyWidth(); 108 | double bodyHeight=upperBodyInfo.getBodyHeight(); 109 | 110 | //利用宽度和高度比 111 | if (bodyWidth==0){ 112 | return new UnderarmData(false,0); 113 | } 114 | double ratio=bodyHeight/bodyWidth; 115 | if (ratio<0.5){ 116 | if (ratio<0.1){ 117 | return new UnderarmData(false,0); 118 | } 119 | double degree=-1/0.4*ratio+1/4; 120 | return new UnderarmData(true,degree); 121 | }else { 122 | return new UnderarmData(false,0); 123 | } 124 | } 125 | 126 | /** 127 | * 判断身体是否倾斜 128 | * 根据论文中训练的模型表示,下面这种情况可以视为身体倾斜 129 | * 左偏 L2<80度 130 | * 右偏 L2>100度 131 | * 132 | */ 133 | 134 | private static BodySlant analysisBodySlant(UpperBodyInfo upperBodyInfo){ 135 | Point p1=upperBodyInfo.getRightShoulder();//左肩位置 136 | Point p2=upperBodyInfo.getNeck();//脖子位置 137 | 138 | double tan2=(p1.getY()-p2.getY())/(p1.getX()-p2.getX()); 139 | tan2=Math.abs(tan2); 140 | double l2=0; 141 | if (p1.getY()=10){ 149 | double degree=1.0/35.0*cha-2.0/7.0; 150 | return new BodySlant(true,degree); 151 | }else { 152 | return new BodySlant(false,0); 153 | } 154 | } 155 | 156 | /** 157 | * 判断手部位置 158 | * @param upperBodyInfo 159 | * @return = 0 正常 160 | * = 1 161 | * = -1 162 | */ 163 | private static HandPosition analysisHandPosition(UpperBodyInfo upperBodyInfo){ 164 | Point point1=upperBodyInfo.getNose();//鼻子 165 | Point point2=upperBodyInfo.getNeck();//脖子 166 | Point point3=upperBodyInfo.getLeftElbow();//左手腕 167 | Point point4=upperBodyInfo.getRightElbow();//右手腕 168 | 169 | if (point1!=null&&point2!=null&&point3!=null&&point4!=null){ 170 | if ((point3.getY()-point1.getY())*(point3.getY()-point2.getY())<0){ 171 | return new HandPosition(true,Direction.LEFT); 172 | } 173 | if (((point4.getY()-point1.getY())*(point4.getY()-point2.getY()))<0){ 174 | return new HandPosition(true,Direction.RIGHT); 175 | } 176 | } 177 | return new HandPosition(false,Direction.NO); 178 | } 179 | 180 | //--------------------对检测结果的封装------------------------------------------- 181 | 182 | //方向 183 | enum Direction{ 184 | LEFT,RIGHT,NO; 185 | } 186 | 187 | //1.头偏离 188 | @Getter 189 | @Setter 190 | @AllArgsConstructor 191 | @NoArgsConstructor 192 | private static class HeadSlant{ 193 | private boolean isSlant;//是否偏离 194 | private Direction direction;//偏离方向 195 | private double degree;//偏离程度 196 | 197 | } 198 | 199 | //2.趴下 200 | @Setter 201 | @Getter 202 | @AllArgsConstructor 203 | @NoArgsConstructor 204 | private static class UnderarmData{ 205 | private boolean isUnderarm; 206 | private double degree; 207 | } 208 | 209 | //3.身体倾斜 210 | @Setter 211 | @Getter 212 | @AllArgsConstructor 213 | @NoArgsConstructor 214 | private static class BodySlant{ 215 | private boolean isBodySlant; 216 | private double degree; 217 | } 218 | 219 | //4.手部位置 220 | @Setter 221 | @Getter 222 | @AllArgsConstructor 223 | @NoArgsConstructor 224 | private static class HandPosition{ 225 | private boolean isError; 226 | private Direction direction; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/main/java/spd/Spd.java: -------------------------------------------------------------------------------- 1 | package spd; 2 | 3 | import detector.impl.HeadDetector; 4 | import detector.impl.UpperBodyDetector; 5 | import model.HeadInfo; 6 | import model.SittingPosition; 7 | import model.SpdImage; 8 | import model.UpperBodyInfo; 9 | import service.SittingPositionDetection; 10 | import service.impl.SimpleSittingPositionDetection; 11 | 12 | import java.util.HashMap; 13 | 14 | /** 15 | * spd是该包的入口 16 | */ 17 | public class Spd { 18 | private static Spd spd=new Spd(); 19 | private static HeadDetector headDetector=null; 20 | private static UpperBodyDetector upperBodyDetector=null; 21 | 22 | private Spd(){ 23 | } 24 | //单例模式,谁让baidu Api免费用户不支持并发呢 25 | public static Spd getInstance(HashMap headDetectorConfig,HashMap upperBodyDetectorConfig){ 26 | //获取单例 27 | headDetector=HeadDetector.getHeadDetecto(); 28 | upperBodyDetector=UpperBodyDetector.getUpperBodyDetector(); 29 | 30 | boolean b1=headDetector.init(headDetectorConfig.get("appId"),headDetectorConfig.get("apiKey"),headDetectorConfig.get("secretKey")); 31 | boolean b2=upperBodyDetector.init(upperBodyDetectorConfig.get("appId"),upperBodyDetectorConfig.get("apiKey"),upperBodyDetectorConfig.get("secretKey")); 32 | if (!(b1&&b2)){ 33 | return null; 34 | } 35 | return spd; 36 | } 37 | 38 | /** 39 | * 获取坐姿信息 40 | * @param uid 用户id 41 | * @param base 图片的base64码,大小不超过2MB 42 | * @return 坐姿信息类 43 | */ 44 | public SittingPosition getSittingPosition(Integer uid,String base){ 45 | HeadInfo headInfo=null; 46 | UpperBodyInfo upperBodyInfo=null; 47 | 48 | SpdImage spdImage=new SpdImage(base); 49 | //获取基础信息:头部信息,关键点信息 50 | try { 51 | headInfo=headDetector.detection(spdImage); 52 | upperBodyInfo=upperBodyDetector.detection(spdImage); 53 | 54 | if (headInfo==null&&upperBodyInfo==null){ 55 | return null; 56 | } 57 | }catch (Exception e){ 58 | e.printStackTrace(); 59 | } 60 | 61 | SittingPosition sittingPosition=null; 62 | 63 | SittingPositionDetection sittingPositionDetection=new SimpleSittingPositionDetection(); 64 | sittingPosition=sittingPositionDetection.getSittingPosition(headInfo,upperBodyInfo); 65 | sittingPosition.setUid(uid); 66 | return sittingPosition; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/test/java/detector/impl/HeadDetectorTest.java: -------------------------------------------------------------------------------- 1 | package detector.impl; 2 | 3 | public class HeadDetectorTest { 4 | 5 | } 6 | --------------------------------------------------------------------------------