File size: 7,652 Bytes
0ccc2f4
 
 
 
 
 
 
 
ae8ca5c
 
 
0ccc2f4
6f6782e
0ccc2f4
ae8ca5c
f974716
0ccc2f4
ae8ca5c
 
 
0ccc2f4
 
 
 
 
 
 
 
ae8ca5c
0ccc2f4
 
 
 
 
 
 
55f5674
0ccc2f4
f974716
 
0ccc2f4
 
 
 
55f5674
 
 
0ccc2f4
 
 
 
 
 
f974716
0ccc2f4
 
 
 
 
 
 
 
f974716
0ccc2f4
 
 
 
ae8ca5c
 
 
55f5674
 
 
ae8ca5c
55f5674
ae8ca5c
0ccc2f4
 
 
 
ae8ca5c
 
 
c4729d8
ae8ca5c
0ccc2f4
 
c4729d8
 
ae8ca5c
 
 
 
 
 
 
c4729d8
ae8ca5c
 
f974716
ae8ca5c
 
 
 
 
 
0ccc2f4
 
f974716
ae8ca5c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
f974716
0ccc2f4
 
ae8ca5c
 
 
0ccc2f4
 
 
 
 
 
 
 
 
 
 
ae8ca5c
 
55f5674
0ccc2f4
 
55f5674
f974716
0ccc2f4
 
f974716
0ccc2f4
 
ae8ca5c
f974716
0ccc2f4
55f5674
0ccc2f4
ae8ca5c
f974716
0ccc2f4
 
ae8ca5c
f974716
0ccc2f4
 
f974716
 
0ccc2f4
f974716
0ccc2f4
 
 
 
f974716
0ccc2f4
 
 
 
ae8ca5c
0ccc2f4
 
 
f974716
0ccc2f4
 
 
 
ae8ca5c
0ccc2f4
f974716
 
0ccc2f4
 
f974716
 
0ccc2f4
 
55f5674
0ccc2f4
 
ae8ca5c
0ccc2f4
 
 
 
 
 
 
f974716
0ccc2f4
 
 
 
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
import streamlit as st
import os
import requests
import numpy as np
from PIL import Image, ImageDraw
import io
import random

##############################################################################
# 1. Attempt to import YOLOv10 from the ultralytics package (THU-MIG/yolov10)
##############################################################################
try:
    from ultralytics import YOLOv10
except ImportError:
    st.error("Could not import YOLOv10. Please confirm THU-MIG/yolov10 installation in requirements.txt.")
    st.stop()

##############################################################################
# 2. 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)
    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):
    """
    Encrypt the given image array using a two-layer XOR + pixel shuffling approach.
    """
    h, w, c = img_array.shape
    flat_image = img_array.flatten()

    # First chaotic key
    chaotic_key_1 = generate_key(seed, len(flat_image))
    # XOR-based encryption (first layer)
    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)

    # Shuffle
    shuffled_array, _ = shuffle_pixels(encrypted_array_1, seed)

    # Second chaotic key
    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

##############################################################################
# 3. YOLOv10 License Plate Detection
##############################################################################
@st.cache_data(show_spinner=False)
def load_model(weights_path: str):
    """
    Loads the YOLOv10 model from local .pt weights (Ultralytics style).
    """
    model = YOLOv10(weights_path)  # e.g., 'best.pt'
    return model

def detect_license_plates(model, pil_image):
    """
    Runs YOLOv10 detection on the PIL image using ultralytics-style output:
      results -> list of ultralytics.engine.results.Results
        each Results has .boxes, .masks, .names, etc.

    We'll handle only the first Results object (single image).
    """
    np_image = np.array(pil_image)
    results = model.predict(np_image)

    # 1) Check how many Results objects we have
    if not results:
        print("No results returned by model.")
        return pil_image, []

    # 2) Take the first Results object
    r = results[0]

    # Debug: print the entire Results object
    print("Raw model output (first Results object):", r)

    # 3) If r.boxes is None or empty, we have no detections
    if not hasattr(r, 'boxes') or r.boxes is None or len(r.boxes) == 0:
        print("No boxes found in results[0].")
        return pil_image, []

    # 4) Parse bounding boxes
    bboxes = []
    draw = ImageDraw.Draw(pil_image)

    # r.boxes is an ultralytics.engine.results.Boxes object
    # We can iterate over each box in r.boxes:
    for box in r.boxes:
        # box has .xyxy, .conf, .cls as 1D tensors
        # e.g. box.xyxy[0] is [x1, y1, x2, y2]
        # box.conf[0] is confidence
        # box.cls[0] is class ID
        coords = box.xyxy[0].tolist()  # [x1, y1, x2, y2]
        conf = float(box.conf[0])
        cls_id = int(box.cls[0])

        # If your license plate class is 0:
        if cls_id == 0:
            x1, y1, x2, y2 = map(int, coords)
            bboxes.append((x1, y1, x2, y2))

            # Optional: draw bounding box for visualization
            draw.rectangle([x1, y1, x2, y2], outline="red", width=2)

    return pil_image, bboxes

##############################################################################
# 4. Streamlit App
##############################################################################
def main():
    st.title("YOLOv10 + Chaotic Encryption Demo")
    st.write(
        """
        **Instructions**:
        1. Provide an image (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.
        """
    )

    # A. Model weights path
    default_model_path = "best.pt"
    model_path = st.sidebar.text_input("YOLOv10 Weights (.pt)", value=default_model_path)

    if not os.path.isfile(model_path):
        st.warning(f"Model file '{model_path}' not found. Upload or provide a correct path.")
        st.stop()

    with st.spinner("Loading YOLOv10 model..."):
        model = load_model(model_path)
    st.success("Model loaded successfully!")

    # B. Image input
    st.subheader("Image Input")
    image_url = st.text_input("Image URL (optional)")
    uploaded_file = st.file_uploader("Or upload an image file", type=["jpg", "jpeg", "png"])

    # C. Encryption seed slider
    key_seed = st.slider("Encryption Key Seed (0 < seed < 1)", 0.001, 0.999, 0.5, step=0.001)

    if st.button("Detect & Encrypt"):
        # 1) Load the image from URL or file
        if image_url and not uploaded_file:
            try:
                response = requests.get(image_url, timeout=10)
                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:
            pil_image = Image.open(uploaded_file).convert("RGB")
        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)

        # 2) Detect plates
        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

        # 3) Encrypt bounding box regions
        with st.spinner("Encrypting license plates..."):
            np_img = np.array(pil_image)
            encrypted_np = np_img.copy()
            for (x1, y1, x2, y2) in bboxes:
                plate_region = encrypted_np[y1:y2, x1:x2]
                encrypted_region = encrypt_image(plate_region, key_seed)
                encrypted_np[y1:y2, x1:x2] = encrypted_region

            encrypted_image = Image.fromarray(encrypted_np)

        st.image(encrypted_image, caption="Encrypted Image", use_container_width=True)

        # 4) Download link
        buf = io.BytesIO()
        encrypted_image.save(buf, format="PNG")
        buf.seek(0)
        st.download_button(
            label="Download Encrypted Image",
            data=buf,
            file_name="encrypted_plate.png",
            mime="image/png"
        )

if __name__ == "__main__":
    main()