shikeying
2022-09-23 eb440e88db0f1a2c405a1e256d33df39f091404d
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
package com.iplatform.recvideo;
 
import com.iplatform.model.po.Rc_video_t1;
import com.iplatform.recvideo.util.VideoFileUtils;
import com.walker.infrastructure.utils.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
 
import java.io.File;
import java.util.List;
 
/**
 * 视频相似度结果计算以及写入执行器。
 * <pre>
 *     1)该对象为'有状态',在每次完成一个采集过程后,需要重新创建。
 * </pre>
 * @author 时克英
 * @date 2022-09-23
 */
public abstract class SimilarExecutor {
 
    protected final transient Logger logger = LoggerFactory.getLogger(this.getClass());
 
    private String videoDataFolder = null;
 
    private String batchId = null;
 
    private List<VideoFolderInfo> videoFolderInfoList = null;
 
    // 记录当前执行到(该批次)哪个视频文件对应的第几个图片
    private int currentVideoFolderIndex = -1;
    private int currentImageIndex = -1;
//    private VideoFolderInfo currentVideoFolderInfo = null;
//    private ImageInfo currentImageInfo = null;
 
    //
    private boolean pythonLoadVideoDone = false;
 
    /**
     * 初始化对象调用一次
     * @param videoDataFolder
     * @param batchId
     */
    public void startup(String videoDataFolder, String batchId){
        if(StringUtils.isEmpty(videoDataFolder)){
            throw new IllegalArgumentException("视频文件夹根目录必须设置!");
        }
        if(StringUtils.isEmpty(batchId)){
            throw new IllegalArgumentException("处理批次(时间)必须设置!");
        }
        this.videoDataFolder = videoDataFolder;
        this.batchId = batchId;
//        this.videoFolderInfoList = VideoFileUtils.getBatchVideoFolderInfo(this.videoDataFolder, batchId);
    }
 
    /**
     * 在每次调度时钟周期执行一次。例如: 10秒一次。<p></p>
     * 注意:该方法英确保每次调用不会重复数据。
     */
    public void execute(){
        if(!this.pythonLoadVideoDone){
            logger.debug("当前 pythonLoadVideoDone = false, 需要查询数据库是否已加载视频");
            this.pythonLoadVideoDone = this.pythonLoadVideoDone(this.batchId, videoDataFolder + File.separator + batchId);
        }
 
        // 1: 如果视频还未加载,则先加载视频
        if(!this.pythonLoadVideoDone){
            try{
                String error = this.requestStartPythonLoadVideo(this.batchId);
                if(StringUtils.isNotEmpty(error)){
                    // 终止调用,等待下次调度继续尝试执行
                    logger.error("python调用加载视频返回错误:" + error);
                    return;
                }
                this.pythonLoadVideoDone = true;
 
            } catch (Exception ex){
                logger.error("python调用加载视频异常:" + this.batchId, ex);
                return;
            }
        }
 
        // 2: 加载完视频,需要查询每个图片相似度结果,并存储到数据库
        //    这里注意,程序必须和AI服务器部署在一起,方便检索视频分析文件夹(本地)
        if(this.videoFolderInfoList == null){
            this.videoFolderInfoList = VideoFileUtils.getBatchVideoFolderInfo(this.videoDataFolder, batchId);
        }
        if(StringUtils.isEmptyList(this.videoFolderInfoList)){
            logger.warn("视频分析文件夹内容为空,无法继续查询相似度结果! videoFolderInfoList = null");
            return;
        }
 
        if(this.isSearchWriteDone()){
            logger.info("已经完成批次相似结果写入数据库,不再往下处理。batch = " + this.batchId);
            return;
        }
 
        // 开始检索相似度
        if(this.currentVideoFolderIndex == -1){
            this.currentVideoFolderIndex ++;
        }
 
    }
 
    private void processOneSearchAndWrite() throws Exception{
        if(this.currentVideoFolderIndex >= this.videoFolderInfoList.size()){
            throw new IllegalArgumentException("currentVideoFolderIndex 越界: " + this.currentVideoFolderIndex);
        }
        VideoFolderInfo currentVideoFolderInfo = this.videoFolderInfoList.get(this.currentVideoFolderIndex);
 
        if(this.currentImageIndex == -1){
            this.currentImageIndex ++;
        }
        ImageInfo imageInfo = currentVideoFolderInfo.getImageInfoList().get(this.currentImageIndex);
 
        this.acquirePythonSearchSimilarOnce(imageInfo.getImagePath(), "30");
 
        if((this.currentImageIndex + 1) >= currentVideoFolderInfo.getImageInfoSize()){
            if((this.currentVideoFolderIndex + 1) < this.videoFolderInfoList.size()){
                logger.debug("一个视频图像集合检索处理完毕,切换到下一个,currentImageIndex = " + this.currentImageIndex);
                this.currentVideoFolderIndex ++;
                this.currentImageIndex = -1;
            } else {
                //
                logger.debug("所有视频包含的所有图像处理完毕,currentVideoFolderIndex = " + this.currentVideoFolderIndex);
            }
            return;
        }
 
        this.currentImageIndex ++;
    }
 
    /**
     * 判断是否已经全部把图片相似度结果写入到数据库中。(针对该批次)
     * @return
     */
    private boolean isSearchWriteDone(){
        if(this.currentVideoFolderIndex == -1 || this.currentImageIndex == -1){
            return false;
        }
        if((this.currentVideoFolderIndex+1) == this.videoFolderInfoList.size()){
            VideoFolderInfo lastVideo = this.videoFolderInfoList.get(this.currentVideoFolderIndex);
            // 如果最后一个视频处理图片数量超过已有数量,判断肯定处理完毕
            if((this.currentImageIndex+1) > lastVideo.getImageInfoSize()){
                return true;
            }
        }
        return false;
    }
 
    /**
     * 查询数据库,检查是否已经完成本次批次视频加载。读这个表: milvus_video_status
     * @param batchId
     * @param batchFolder 批次所在文件夹全路径,如: /opt/ai/video/20220921
     * @return
     */
    protected abstract boolean pythonLoadVideoDone(String batchId, String batchFolder);
 
    /**
     * 请求AI服务,开始一个批次视频数据导入。
     * @param batchId
     * @return
     */
    protected abstract String requestStartPythonLoadVideo(String batchId) throws Exception;
 
    /**
     * 请求AI服务,检索给定图片的相似度结果集合。
     * @return
     */
    protected abstract List<Rc_video_t1> acquirePythonSearchSimilarOnce(String imagePath, String topN);
}