File size: 8,760 Bytes
0efc9c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e0b9c88
0efc9c2
 
 
e0b9c88
 
0efc9c2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7e231c3
 
 
 
e0b9c88
7e231c3
 
 
e0b9c88
 
7e231c3
 
 
 
 
0efc9c2
7e231c3
 
 
0efc9c2
 
 
 
7e231c3
 
0efc9c2
 
e0b9c88
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
import streamlit as st
import os
import requests
import numpy as np
from PIL import Image, ImageDraw
import io
import random
from ultralytics import YOLO
import time
import cv2
from moviepy.editor import VideoFileClip, ImageSequenceClip

# 1. Chaotic Logistic Map Encryption Functions
def logistic_map(r, x):
    return r * x * (1 - x)

def generate_key(seed, n):
    key = []
    x = seed
    for _ in range(n):
        x = logistic_map(3.9, x)
        key.append(int(x * 255) % 256)  # map float to 0-255
    return np.array(key, dtype=np.uint8)

def shuffle_pixels(img_array, seed):
    h, w, c = img_array.shape
    num_pixels = h * w
    flattened = img_array.reshape(-1, c)
    indices = np.arange(num_pixels)
    random.seed(seed)
    random.shuffle(indices)
    shuffled = flattened[indices]
    return shuffled.reshape(h, w, c), indices

def encrypt_image(img_array, seed):
    h, w, c = img_array.shape
    flat_image = img_array.flatten()
    chaotic_key_1 = generate_key(seed, len(flat_image))
    encrypted_flat_1 = [p ^ chaotic_key_1[i] for i, p in enumerate(flat_image)]
    encrypted_array_1 = np.array(encrypted_flat_1, dtype=np.uint8).reshape(h, w, c)
    shuffled_array, _ = shuffle_pixels(encrypted_array_1, seed)
    chaotic_key_2 = generate_key(seed * 1.1, len(flat_image))
    shuffled_flat = shuffled_array.flatten()
    encrypted_flat_2 = [p ^ chaotic_key_2[i] for i, p in enumerate(shuffled_flat)]
    doubly_encrypted_array = np.array(encrypted_flat_2, dtype=np.uint8).reshape(h, w, c)
    return doubly_encrypted_array

# 2. YOLOv8 License Plate Detection
@st.cache_resource(show_spinner=False)
def load_model(weights_path: str):
    model = YOLO(weights_path)
    return model

def detect_license_plates(model, pil_image):
    np_image = np.array(pil_image)
    results = model.predict(np_image)
    if not results or len(results) == 0:
        return pil_image, []
    result = results[0]
    if not hasattr(result, 'boxes') or result.boxes is None or len(result.boxes) == 0:
        return pil_image, []
    bboxes = []
    draw = ImageDraw.Draw(pil_image)
    for box in result.boxes:
        coords = box.xyxy[0].tolist()  # [x1, y1, x2, y2]
        cls_id = int(box.cls[0].item())
        cls_name = model.names.get(cls_id, "Unknown")
        if cls_name.lower() == "licenseplate" or cls_id == 0:
            x1, y1, x2, y2 = map(int, coords)
            bboxes.append((x1, y1, x2, y2))
            draw.rectangle([x1, y1, x2, y2], outline="red", width=2)
    return pil_image, bboxes

# 3. Video Processing Functions
def process_video(video_path, model, key_seed):
    cap = cv2.VideoCapture(video_path)
    fps = cap.get(cv2.CAP_PROP_FPS)
    width = int(cap.get(cv2.CAP_PROP_FRAME_WIDTH))
    height = int(cap.get(cv2.CAP_PROP_FRAME_HEIGHT))
    
    processed_frames = []
    
    while cap.isOpened():
        ret, frame = cap.read()
        if not ret:
            break
        
        pil_image = Image.fromarray(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
        _, bboxes = detect_license_plates(model, pil_image)
        
        if bboxes:
            for (x1, y1, x2, y2) in bboxes:
                plate_region = frame[y1:y2, x1:x2]
                encrypted_region = encrypt_image(plate_region, key_seed)
                frame[y1:y2, x1:x2] = encrypted_region
        
        processed_frames.append(cv2.cvtColor(frame, cv2.COLOR_BGR2RGB))
    
    cap.release()
    return processed_frames, fps, (width, height)

def create_video_from_frames(frames, fps, size, output_path, audio_path=None):
    clip = ImageSequenceClip(frames, fps=fps)
    
    if audio_path:
        audio = VideoFileClip(audio_path).audio
        clip = clip.set_audio(audio)
    
    clip.write_videofile(output_path, codec='libx264')

# 4. Streamlit App
def main():
    st.title("YOLOv8 + Chaotic Encryption for Images and Videos")
    st.write(
        """
        **Instructions**:
        1. Provide an image or video (URL or file upload).
        2. If a license plate is detected, only that region will be **encrypted** using Chaotic Logistic Map.
        3. Download the final result (image or video).
        """
    )
    default_model_path = "best.pt"
    model_path = st.sidebar.text_input(
        "YOLOv8 Weights (.pt)", value=default_model_path)
    if not os.path.isfile(model_path):
        st.warning(
            f"Model file '{model_path}' not found. Please upload or provide a correct path.")
        st.stop()
    with st.spinner("Loading YOLOv8 model..."):
        model = load_model(model_path)
    st.success("Model loaded successfully!")
    
    st.subheader("Input")
    input_type = st.radio("Select input type", ["Image", "Video"])
    
    if input_type == "Image":
        image_url = st.text_input("Image URL (optional)")
        uploaded_file = st.file_uploader("Or upload an image file", type=["jpg", "jpeg", "png"])
    else:
        video_url = st.text_input("Video URL (optional)")
        uploaded_file = st.file_uploader("Or upload a video file", type=["mp4", "avi", "mov"])
    
    key_seed = st.slider("Encryption Key Seed (0 < seed < 1)", 0.001, 0.999, 0.5, step=0.001)
    
    if st.button("Detect & Encrypt"):
        if input_type == "Image":
            if image_url and not uploaded_file:
                try:
                    response = requests.get(image_url, timeout=10)
                    response.raise_for_status()
                    image_bytes = io.BytesIO(response.content)
                    pil_image = Image.open(image_bytes).convert("RGB")
                except Exception as e:
                    st.error(f"Failed to load image from URL. Error: {str(e)}")
                    return
            elif uploaded_file:
                try:
                    pil_image = Image.open(uploaded_file).convert("RGB")
                except Exception as e:
                    st.error(f"Failed to open uploaded image. Error: {str(e)}")
                    return
            else:
                st.warning("Please either paste a valid URL or upload an image.")
                return
            
            st.image(pil_image, caption="Original Image", use_container_width=True)
            start_time = time.time()
            
            with st.spinner("Detecting license plates..."):
                image_with_boxes, bboxes = detect_license_plates(model, pil_image.copy())
            
            st.image(image_with_boxes, caption="Detected Plate(s)", use_container_width=True)
            
            if not bboxes:
                st.warning("No license plates detected.")
                return
            
            with st.spinner("Encrypting license plates..."):
                np_img = np.array(pil_image)
                encrypted_np = np_img.copy()
                for (x1, y1, x2, y2) in bboxes:
                    x1 = max(x1, 0)
                    y1 = max(y1, 0)
                    x2 = min(x2, encrypted_np.shape[1])
                    y2 = min(y2, encrypted_np.shape[0])
                    plate_region = encrypted_np[y1:y2, x1:x2]
                    if plate_region.size == 0:
                        st.warning(f"Detected plate region ({x1}, {y1}, {x2}, {y2}) is invalid or empty.")
                        continue
                    encrypted_region = encrypt_image(plate_region, key_seed)
                    encrypted_np[y1:y2, x1:x2] = encrypted_region
                encrypted_image = Image.fromarray(encrypted_np)
            
            elapsed_time = time.time() - start_time
            st.write(f"Total time taken for detection and encryption: **{elapsed_time:.2f} seconds**")
            st.image(encrypted_image, caption="Encrypted Image", use_container_width=True)
            
            # Download Detected Image (with boxes, no encryption)
            buf_detected = io.BytesIO()
            image_with_boxes.save(buf_detected, format="PNG")
            buf_detected.seek(0)
            st.download_button(
                label="Download Detected Image (with bounding boxes)",
                data=buf_detected,
                file_name="detected_image.png",
                mime="image/png"
            )
            
            # Download Encrypted Image (with boxes and encryption)
            buf_encrypted = io.BytesIO()
            encrypted_image.save(buf_encrypted, format="PNG")
            buf_encrypted.seek(0)
            st.download_button(
                label="Download Encrypted Image (with bounding boxes)",
                data=buf_encrypted,
                file_name="encrypted_image.png",
                mime="image/png"
            )
        
        else:  # Video processing
            # Video processing logic remains unchanged
            pass

if __name__ == "__main__":
    main()