File size: 6,608 Bytes
37170d6
9316eb4
 
37170d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9316eb4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37170d6
9316eb4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
37170d6
2a190c0
37170d6
 
 
 
 
 
 
9316eb4
 
 
37170d6
 
 
 
 
 
9316eb4
 
 
 
 
 
37170d6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9316eb4
 
 
 
37170d6
 
9316eb4
 
 
 
 
37170d6
9316eb4
 
 
 
 
37170d6
9316eb4
 
 
 
 
 
37170d6
 
 
 
 
 
 
 
9316eb4
 
 
 
37170d6
 
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
import gradio as gr
import cv2
import os
from core.chessboard_detector import ChessboardDetector

detector = ChessboardDetector(
    det_model_path="onnx/det/v1.onnx", 
    pose_model_path="onnx/pose/v1.onnx",
    full_classifier_model_path="onnx/layout_recognition/v1.onnx"
)

# 数据集路径
dict_cate_names = {
    '.': '.',
    'x': 'x',
    '红帅': 'K',
    '红士': 'A',
    '红相': 'B',
    '红马': 'N',
    '红车': 'R',
    '红炮': 'C',
    '红兵': 'P',

    '黑将': 'k',
    '黑仕': 'a',
    '黑象': 'b',
    '黑傌': 'n',
    '黑車': 'r',
    '黑砲': 'c',
    '黑卒': 'p',
}

dict_cate_names_reverse = {v: k for k, v in dict_cate_names.items()}


### 构建 examples 

def build_examples():
    examples = []
    # 读取 examples 目录下的所有图片
    for file in os.listdir("examples"):
        if file.endswith(".jpg"):
            image_path = os.path.join("examples", file)
            examples.append([image_path, None])
        
        elif file.endswith(".mp4"):
            video_path = os.path.join("examples", file)
            examples.append([None, video_path])

    return examples


full_examples = build_examples()


def get_video_frame_with_processs(video_data, process: str = '00:00') -> cv2.UMat:
    """
    获取视频指定位置的帧
    """

    # 读取视频
    cap = cv2.VideoCapture(video_data)
    if not cap.isOpened():
        gr.Warning("无法打开视频")
        return None
    
    # 获取视频的帧率
    fps = cap.get(cv2.CAP_PROP_FPS)

    # process 是 00:00
    process_time = process.split(":")
    minutes = int(process_time[0])
    seconds = float(process_time[1])

    # 计算总秒数
    target_seconds = minutes * 60 + seconds

    # 计算当前帧
    current_frame = int(target_seconds * fps)

    # 设置到指定帧
    cap.set(cv2.CAP_PROP_POS_FRAMES, current_frame)

    # 读取当前帧
    ret, frame = cap.read()
    cap.release()

    if not ret:
        gr.Warning("无法读取视频帧")
        return None
    
    frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)

    return frame_rgb




with gr.Blocks(
    js="""
        async () => {
            document.addEventListener('timeupdate', function(e) {
                // 检查事件源是否是视频元素
                if (e.target.matches('#video_player video')) {
                    const video = e.target;
                    const currentTime = video.currentTime;
                    // 转换成 00:00 格式
                    let minutes = Math.floor(currentTime / 60);
                    let seconds = Math.floor(currentTime % 60);
                    let formattedTime = `${minutes.toString().padStart(2,'0')}:${seconds.toString().padStart(2,'0')}`;
                    
                    // 更新输入框值
                    let processInput = document.querySelector("#video_process textarea");
                    if(processInput) {
                        processInput.value = formattedTime;
                        processInput.text = formattedTime;

                        processInput.dispatchEvent(new Event("input"));
                    }

                }
            }, true);  // 使用捕获阶段
        }
    """,
    css="""
        .image img {
            max-height: 512px;
        }
    """
) as demo:
    gr.Markdown("""
                ## 棋盘检测, 棋子识别

                x 表示 有遮挡位置   
                . 表示 棋盘上的普通交叉点  

                步骤:  
                    1. 流程分成两步,第一步检测边缘  
                    2. 对整个棋盘画面进行棋子分类预测
                """
    )

    with gr.Row():
        with gr.Column():
            video_input = gr.Video(label="上传视频", interactive=True, elem_id="video_player", height=356)
            video_process = gr.Textbox(label="当前时间", interactive=True, elem_id="video_process",  value="00:00")
            extract_frame_btn = gr.Button("从视频提取当前帧")

    with gr.Row():
        with gr.Column():
            image_input = gr.Image(label="上传棋盘图片", type="numpy", elem_classes="image")

        with gr.Column():
            original_image_with_keypoints = gr.Image(
                label="step1: 原图带关键点",
                interactive=False,
                visible=True,
                elem_classes="image"
            )


    with gr.Row():
        with gr.Column():   
            transformed_image = gr.Image(
                label="step2: 拉伸棋盘",
                interactive=False,
                visible=True,
                elem_classes="image"
            )

        with gr.Column():
            use_time = gr.Textbox(
                label="用时",
                interactive=False,
                visible=True,
            )
            layout_pred_info = gr.Dataframe(
                label="棋子识别",
                interactive=False,
                visible=True,
            )

    with gr.Row():
        with gr.Column():
            gr.Examples(full_examples, inputs=[image_input, video_input], label="示例视频、图片")


    def detect_chessboard(image):
        if image is None:
            return None, None, None, None
        
        try:
            original_image_with_keypoints, transformed_image, cells_labels_str, scores, time_info = detector.pred_detect_board_and_classifier(image)

            # 将 cells_labels 转换为 DataFrame
            # cells_labels 通过  \n 分割
            annotation_10_rows = [item for item in cells_labels_str.split("\n")]
            # 将 annotation_10_rows 转换成为 10 行 9 列的二维数组
            annotation_arr_10_9 = [list(item) for item in annotation_10_rows]

            # 将 棋子类别 转换为 中文
            annotation_arr_10_9 = [[dict_cate_names_reverse[item] for item in row] for row in annotation_arr_10_9]

        except Exception as e:
            gr.Warning(f"检测失败 图片或者视频布局错误")
            return None, None, None, None


        return original_image_with_keypoints, transformed_image, annotation_arr_10_9, time_info

    image_input.change(fn=detect_chessboard, 
                       inputs=[image_input], 
                       outputs=[original_image_with_keypoints, transformed_image, layout_pred_info, use_time])

    extract_frame_btn.click(fn=get_video_frame_with_processs, 
                            inputs=[video_input, video_process], 
                            outputs=[image_input])

if __name__ == "__main__":
    demo.launch()