1
zhanghua
2024-09-26 c775c6953d9759e70f08acbfa8f6d7490aaae3d1
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
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
package com.netsdk.demo.customize.faceReconEx;
 
import com.netsdk.demo.util.QueueGeneration;
import com.netsdk.lib.NetSDKLib;
import com.sun.jna.Pointer;
 
import java.io.File;
import java.io.UnsupportedEncodingException;
import java.util.UUID;
import java.util.concurrent.LinkedBlockingDeque;
 
import static com.netsdk.demo.util.StructFieldChooser.GetSelectedSingleFieldValue;
import static com.netsdk.lib.Utils.getOsPrefix;
 
/**
 * @author : 47040
 * @since : Created in 2020/7/20 20:10
 */
public class FaceReconAnalyzerDataCallBack implements NetSDKLib.fAnalyzerDataCallBack {
 
    //////////////////////////////// 单例////////////////////////////////
 
    private static FaceReconAnalyzerDataCallBack singleInstance;
 
    public static FaceReconAnalyzerDataCallBack getSingleInstance() {
        if (singleInstance == null) {
            singleInstance = new FaceReconAnalyzerDataCallBack();
        }
        return singleInstance;
    }
 
    //////////////////////////////// 系统编码 ////////////////////////////////
 
    public static String encode;
 
    static {
        String osPrefix = getOsPrefix();
        if (osPrefix.toLowerCase().startsWith("win32-amd64")) {
            encode = "GBK";
        } else if (osPrefix.toLowerCase().startsWith("linux-amd64")) {
            encode = "UTF-8";
        }
    }
 
    //////////////////////////////// 静态池 ////////////////////////////////
    // 用阻塞队列模拟一个静态对象池
 
    // 设置一个队列模拟静态池,容量看情况改,越大可同时处理的事件就越多,占用内存也越多
    private final static int MAX_TASK_COUNT = 10;   // 队列容量
    private final static LinkedBlockingDeque<NetSDKLib.DEV_EVENT_FACERECOGNITION_INFO> faceReconPool = new LinkedBlockingDeque<>(MAX_TASK_COUNT);
 
    static {
        // 初始化队列
        for (int i = 0; i < MAX_TASK_COUNT; i++) {
            faceReconPool.offer(new NetSDKLib.DEV_EVENT_FACERECOGNITION_INFO());
        }
    }
 
    //////////////////////////////// 图片文件夹 ////////////////////////////////
 
    private static final String imageSaveFolder = "faceReconEx/";      // 图片保存路径
 
    static {
        // 创建图片文件夹
        File path = new File(imageSaveFolder);
        if (!path.exists()) {
            if (!path.mkdir()) {
                System.err.println("文件夹创建失败,无法保存图片");
            }
        }
    }
 
    //////////////////////////////// 图片文件夹 ////////////////////////////////
    // 两个线程池阻塞队列,用于模拟并发业务
 
    private final QueueGeneration savePicService = new QueueGeneration();    // 保存图片异步队列
 
    private final QueueGeneration customerService = new QueueGeneration();   // 业务操作异步队列
 
 
    public FaceReconAnalyzerDataCallBack() {
        savePicService.init();     // 图片任务队列启动
        customerService.init();    // 业务任务队列启动
    }
 
    /**
     * 智能分析回调函数
     * 请特别注意: 此回调函数仅用于获取事件数据,如果要调其他 sdk 接口或执行相对耗时的业务操作请另开线程,务必不要阻塞本函数
     * 在本函数内直接调其他 sdk 接口可能会引发底层死锁;长时间阻塞本函数会导致 sdk 心跳包无法正常发送造成设备掉线
     *
     * @param lAnalyzerHandle 订阅句柄,必然和订阅时获取的句柄相同
     * @param dwAlarmType     事件类型枚举
     * @param pAlarmInfo      事件数据指针
     * @param pBuffer         图片数据指针
     * @param dwBufSize       图片数据长度
     * @param dwUser          用户自定义信息 不建议使用
     * @param nSequence       表示上传的相同图片情况,0表示是第一次出现,为2表示最后一次出现或仅出现一次,为1表示此次之后还有
     * @param reserved        表示当前回调数据的状态, 为0表示当前数据为实时数据,为1表示当前回调数据是离线数据,为2时表示离线数据传送结束
     * @return 0 正常返回
     */
    @Override
    public int invoke(NetSDKLib.LLong lAnalyzerHandle, int dwAlarmType, Pointer pAlarmInfo, Pointer pBuffer, int dwBufSize, Pointer dwUser, int nSequence, Pointer reserved) {
        switch (dwAlarmType) {
            case NetSDKLib.EVENT_IVS_FACERECOGNITION: {
                handleTargetRecognition(pAlarmInfo, pBuffer, dwBufSize, lAnalyzerHandle);
                break;
            }
            default:
                System.out.printf("Get Other Event 0x%x\n", dwAlarmType);
                break;
        }
        return 0;
    }
 
    /**
     * 人像抓拍
     *
     * @param pBuffer         指针内容
     * @param dwBufSize       指针内容大小
     * @param lAnalyzerHandle 布防返回结果
     */
    public void handleTargetRecognition(Pointer pAlarmInfo, Pointer pBuffer, int dwBufSize, NetSDKLib.LLong lAnalyzerHandle) {
        if (pAlarmInfo == Pointer.NULL || pBuffer == Pointer.NULL || dwBufSize <= 0) {
            return;
        }
 
        NetSDKLib.DEV_EVENT_FACERECOGNITION_INFO msg = null;
        NetSDKLib.NET_TIME_EX UTC = null;                              // 抓拍时间
        int channel = -1;                                              // 事件通道
        NetSDKLib.NET_PIC_INFO facePicInfo = null;                     // 人像数据
        byte[] serialUUID = null;                                      // 唯一记录ID
        NetSDKLib.NET_PIC_INFO backgroundPic = null;                   // 大图数据
        NetSDKLib.NET_FACE_DATA faceInfo = null;                       // 人脸数据
        NetSDKLib.CANDIDATE_INFOEX suitableCandidateInfo = null;       // 最佳候选人
 
        //////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////// 从指针获取数据 //////////////////////////////////
        //////////////////////////////////////////////////////////////////////////////////
 
        try {
            msg = faceReconPool.take();     // 从静态池取
            // 抓拍时间
            UTC = (NetSDKLib.NET_TIME_EX) GetSelectedSingleFieldValue("UTC", msg, pAlarmInfo);
            // 通道号
            channel = (int) GetSelectedSingleFieldValue("nChannelID", msg, pAlarmInfo);
            // 唯一记录ID
            serialUUID = (byte[]) GetSelectedSingleFieldValue("szSerialUUID", msg, pAlarmInfo);
            // 人像数据
            facePicInfo = (NetSDKLib.NET_PIC_INFO) GetSelectedSingleFieldValue("stuObject.stPicInfo", msg, pAlarmInfo);
            // 大图数据
            backgroundPic = (NetSDKLib.NET_PIC_INFO) GetSelectedSingleFieldValue("stuGlobalScenePicInfo", msg, pAlarmInfo);
            // 人脸数据
            faceInfo = (NetSDKLib.NET_FACE_DATA) GetSelectedSingleFieldValue("stuFaceData", msg, pAlarmInfo);
            // 最佳候选人数据
            int nCandidateNum = (int) GetSelectedSingleFieldValue("nRetCandidatesExNum", msg, pAlarmInfo); // 读取有效候选人数量
            if (nCandidateNum != 0) {
                for (int i = 0; i < nCandidateNum; i++) {   // 长度不为0,证明存在候选人
                    msg.stuCandidatesEx[i] = (NetSDKLib.CANDIDATE_INFOEX) GetSelectedSingleFieldValue(String.format("stuCandidatesEx[%d]", i), msg, pAlarmInfo);
                }
                // 遍历找出相似度最大的那个人
                int index = 0;
                int bySimilarity = 0;
                for (int j = 0; j < nCandidateNum; j++) {
                    if (msg.stuCandidatesEx[j].bySimilarity > bySimilarity) {
                        index = j;
                        bySimilarity = msg.stuCandidatesEx[j].bySimilarity;
                    }
                }
                suitableCandidateInfo = msg.stuCandidatesEx[index];
            }
        } catch (InterruptedException e) {
            System.err.println("静态池错误: " + e.getLocalizedMessage());
        } catch (Exception e) {
            System.err.println("数据获取异常:" + e.getLocalizedMessage());
        } finally {
            if (msg != null) {
                faceReconPool.offer(msg);    // 重新放回静态池
            }
        }
 
        //////////////////////////////////////////////////////////////////////////////////////
        ////////////////////////////////// 提取数据及业务操作 //////////////////////////////////
        /////////////////////////////////////////////////////////////////////////////////////
 
        try {
            // 订阅句柄
            Long analyzerHandle = lAnalyzerHandle.longValue();
            // 抓拍时间
            String eventTime = UTC == null ? "" : UTC.toStringTime();
 
            String uuid = UUID.randomUUID().toString();
            // --> 人像数据 人脸图片信息(如果需要其他数据请从 facePicInfo 内获取)
            String facePicPath = "";
            if (facePicInfo != null && facePicInfo.dwFileLenth > 0) {
                facePicPath = imageSaveFolder + eventTime.trim().replaceAll("[^0-9]", "-") + "-" + uuid + "-facePic.jpg";
                savePicService.addEvent(new SavePicHandler(pBuffer, facePicInfo.dwOffSet, facePicInfo.dwFileLenth, facePicPath));  // 新开线程保存图片
            }
 
            // --> 大图数据 操作背景图片(如果需要其他数据请从 backgroundPic 内获取)
            String backPicPath = "";
            if (backgroundPic != null && backgroundPic.dwFileLenth > 0) {
                backPicPath = imageSaveFolder + eventTime.trim().replaceAll("[^0-9]", "-") + "-" + uuid + "-scenePic.jpg";
                savePicService.addEvent(new SavePicHandler(pBuffer, backgroundPic.dwOffSet, backgroundPic.dwFileLenth, backPicPath)); // 新开线程保存图片
            }
 
            // --> 人脸数据 (如果需要其他数据请从 faceInfo 内获取)
            int emSex = 0;                              // 性别枚举
            int emMask = 0;                             // 口罩枚举
            int age = 0;                                // 年龄
            String emGlasses = "";                      // 是否带眼镜
            if (faceInfo != null) {
                emSex = faceInfo.emSex;
                emMask = faceInfo.emMask;
                age = faceInfo.nAge;
                for (int i = 0; i < faceInfo.nFeatureValidNum; i++) {
                    if (faceInfo.emFeature[i] == 1) {
                        emGlasses = "戴眼镜";
                    } else if (faceInfo.emFeature[i] == 10) {
                        emGlasses = "没戴眼镜";
                    } else if (faceInfo.emFeature[i] == 14) {
                        emGlasses = "太阳眼镜";
                    }
                }
            }
 
            String uniqueSerialUUID = "";
            if (serialUUID != null)
                uniqueSerialUUID = new String(serialUUID, encode).trim();
 
            // --> 最佳候选人数据 (如果需要其他数据请从 suitableCandidateInfo 内获取)
            String szUID = "";                         // User ID
            String szID = "";                          // User szID
            String szPersonName = "";                  // User name
            String faceDbId = "";                      // DB ID
            int similarity = 0;                        // similarity
            if (suitableCandidateInfo != null) {
                szUID = new String(suitableCandidateInfo.stPersonInfo.szUID, encode).trim();
                szID = new String(suitableCandidateInfo.stPersonInfo.szID, encode).trim();
                szPersonName = new String(suitableCandidateInfo.stPersonInfo.szPersonName, encode).trim();
                faceDbId = new String(suitableCandidateInfo.stPersonInfo.szGroupID).trim();
                similarity = suitableCandidateInfo.bySimilarity;
 
            }
 
            // 打印看一下
            System.out.println("\n\n<<—————————— EVENT_IVS ——————————>>\n" +
                    "   订阅句柄  lAnalyzerHandle:" + analyzerHandle + "\n" +
                    "   时间事件  Time           : " + eventTime + "\n" +
                    "   通道      Channel        : " + channel + "\n" +
                    "   事件识别ID SerialUUID     : " + uniqueSerialUUID + "\n" +
                    "   性别      emSex          : " + emSex + "\n" +
                    "   年龄      age            : " + age + "\n" +
                    "   戴眼镜    emGlasses      : " + emGlasses + "\n" +
                    "   戴口罩    emMask         : " + emMask + "\n" +
                    "   候选人ID  User ID        : " + szUID + "\n" +
                    "   候选人证件 User szID      : " + szID + "\n" +
                    "   候选人姓名 User name      : " + szPersonName + "\n" +
                    "   注册库ID   DB   ID       : " + faceDbId + "\n" +
                    "   相似度     similarity    : " + similarity
            );
 
            // 耗时的业务操作请另开线程
            CustomerServiceModel model = new CustomerServiceModel(
                    analyzerHandle,
                    eventTime, channel,
                    facePicPath, backPicPath,
                    emSex, emMask, age, emGlasses,
                    szUID, szID, szPersonName, faceDbId, similarity
            );
            customerService.addEvent(new CustomerServiceHandler(model));
        } catch (
                UnsupportedEncodingException e) {
            System.err.println("编码错误: " + e.getLocalizedMessage());
        }
    }
}