File size: 5,377 Bytes
ee2a1ac
 
 
 
 
 
 
32450de
 
ee2a1ac
57ef3c4
ee2a1ac
57ef3c4
 
 
 
 
 
 
32450de
 
 
 
 
ee2a1ac
 
32450de
d785524
ee2a1ac
4c40bba
 
 
ee2a1ac
4c40bba
 
 
 
 
 
 
 
ee2a1ac
32450de
4c40bba
32450de
 
d785524
4c40bba
ee2a1ac
af3b295
d785524
 
 
af3b295
 
 
 
 
 
4c40bba
af3b295
ee2a1ac
 
32450de
d785524
32450de
 
 
ee2a1ac
d785524
 
 
 
 
 
 
 
 
 
 
32450de
 
ee2a1ac
 
 
 
 
 
 
 
 
 
d785524
4c40bba
ee2a1ac
 
 
 
d785524
ee2a1ac
 
4c40bba
ee2a1ac
4c40bba
ee2a1ac
32450de
 
ee2a1ac
32450de
d785524
 
 
ee2a1ac
 
32450de
ee2a1ac
32450de
ee2a1ac
 
 
32450de
 
 
 
d785524
32450de
ee2a1ac
 
 
32450de
ee2a1ac
 
af3b295
5bd8421
 
d785524
af3b295
ee2a1ac
af3b295
57ef3c4
4c40bba
ee2a1ac
 
 
d785524
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
import gradio as gr
import cv2
import os
import random
import zipfile
from PIL import Image
from datetime import datetime
import io
import hashlib

def procesar_video(video):
    try:
        if isinstance(video, dict):
            original_name = video.get("name", "video")
            video_path = video.get("file", video.get("data"))
        else:
            original_name = os.path.basename(video)
            video_path = video

        allowed_extensions = ('.mp4', '.avi', '.mov', '.mkv')
        if not original_name.lower().endswith(allowed_extensions):
            raise gr.Error("Solo se permiten archivos de video (mp4, avi, mov, mkv)")

        timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        temp_dir = f"temp_{datetime.now().strftime('%Y%m%d%H%M%S')}"
        os.makedirs(temp_dir, exist_ok=True)

        # Extracci贸n de fotogramas
        cap = cv2.VideoCapture(video_path)
        frame_count = 0
        frame_paths = []
        while True:
            ret, frame = cap.read()
            if not ret:
                break
            frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
            img = Image.fromarray(frame_rgb)
            img_path = os.path.join(temp_dir, f"frame_{frame_count:04d}.jpg")
            img.save(img_path)
            frame_paths.append(img_path)
            frame_count += 1
        cap.release()

        if frame_count == 0:
            raise gr.Error("No se pudieron extraer fotogramas del video")

        # Crear collage
        selected_frames = random.sample(frame_paths, min(4, len(frame_paths)))
        images = [Image.open(img) for img in selected_frames]
        margin = 10
        collage = Image.new('RGB', 
            (images[0].width * 2 + margin, images[0].height * 2 + margin), 
            (255,255,255))
        positions = [
            (0, 0),
            (images[0].width + margin, 0),
            (0, images[0].height + margin),
            (images[0].width + margin, images[0].height + margin)
        ]
        for i, img in enumerate(images):
            collage.paste(img, positions[i])
        collage_path = os.path.join(temp_dir, "collage.jpg")
        collage.save(collage_path)

        # Generaci贸n del ZIP con todos los fotogramas
        base_name = os.path.splitext(original_name)[0]
        zip_filename = f"{base_name}.zip"
        final_zip_path = os.path.join(temp_dir, zip_filename)
        
        with zipfile.ZipFile(final_zip_path, mode="w") as zipf:
            for img_path in frame_paths:
                zipf.write(img_path, os.path.basename(img_path))
            
            chain_content = (
                f"Nombre del archivo: {original_name}\n"
                f"Fecha de carga y extracci贸n: {timestamp}\n"
                f"N煤mero de fotogramas: {frame_count}\n"
            )
            zipf.writestr("cadena_custodia.txt", chain_content)

        return collage_path, final_zip_path, temp_dir

    except Exception as e:
        raise gr.Error(f"Error al procesar el video: {str(e)}")

def limpiar_cache(temp_dir):
    if temp_dir and os.path.exists(temp_dir):
        for file in os.listdir(temp_dir):
            os.remove(os.path.join(temp_dir, file))
        os.rmdir(temp_dir)

with gr.Blocks(title="Extracci贸n de Fotogramas Forenses") as demo:
    gr.Markdown("# Herramienta de Extracci贸n de Fotogramas Forenses")
    gr.Markdown("**Carga un video para extraer TODOS los fotogramas y generar un collage de muestra.**")
    gr.Markdown("Desarrollado por Jos茅 R. Leonett para el Grupo de Peritos Forenses Digitales de Guatemala - [www.forensedigital.gt](https://www.forensedigital.gt)")
    
    with gr.Row():
        with gr.Column():
            video_input = gr.Video(label="Subir Video", interactive=True)
            procesar_btn = gr.Button("Procesar Fotogramas", interactive=False)
        with gr.Column():
            gallery_output = gr.Image(label="Collage de Muestra")
            download_btn = gr.Button("DESCARGAR FOTOGRAMAS", interactive=False)
            download_file = gr.File(label="Archivo ZIP generado", visible=False)
    
    temp_dir_state = gr.State(None)
    zip_path_state = gr.State(None)
    
    def on_video_change(video):
        if video:
            return gr.update(interactive=True), gr.update(interactive=False)
        return gr.update(interactive=False), gr.update(interactive=False)
    
    video_input.change(
        fn=on_video_change,
        inputs=video_input,
        outputs=[procesar_btn, download_btn],
        queue=False
    )
    
    def procesar_y_mostrar(video):
        if temp_dir_state.value:
            limpiar_cache(temp_dir_state.value)
        collage_path, zip_path, temp_dir = procesar_video(video)
        return collage_path, zip_path, temp_dir, zip_path, gr.update(interactive=True), gr.update(interactive=False)
    
    procesar_btn.click(
        fn=procesar_y_mostrar,
        inputs=video_input,
        outputs=[gallery_output, download_file, temp_dir_state, zip_path_state, download_btn, procesar_btn],
    )
    
    def trigger_download(zip_path):
        with open(zip_path, "rb") as f:
            file_bytes = f.read()
        return os.path.basename(zip_path), file_bytes
    
    download_btn.click(
        fn=trigger_download,
        inputs=zip_path_state,
        outputs=download_file,
    )

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