File size: 7,616 Bytes
e9790f2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
import random
import torch
from transformers import BlipProcessor, BlipForConditionalGeneration
import gradio as gr
from PIL import Image


device = "cuda" if torch.cuda.is_available() else "cpu"

# Load the fine tuned model and processor
processor_trained = BlipProcessor.from_pretrained("saarah005/blip-finetuned-holmes")
model_trained = BlipForConditionalGeneration.from_pretrained("saarah005/blip-finetuned-holmes").to(device)


def holmesify(caption):
    # Small dictionary of style replacements
    replacements = {
    # ---- People ----
    "dog": "hound",
    "dogs": "hounds",
    "cat": "feline companion",
    "cats": "feline companions",
    "bird": "avian companion",
    "birds": "avian companions",
    "man": "gentleman",
    "men": "gentlemen",
    "woman": "lady",
    "women": "ladies",
    "boys": "young lads",
    "boy": "young lad",
    "girl": "young maiden",
    "girls": "young maids",
    "child": "youth",
    "children": "young ones",
    "person": "individual of interest",
    "people": "various passersby",
    "crowd": "assembly of onlookers",

    # ---- Actions / Verbs ----
    "running": "in swift pursuit",
    "walking": "taking measured strides",
    "standing": "stationed with deliberate poise",
    "sitting": "resting in quiet contemplation",
    "talking": "engaged in hushed discourse",
    "laughing": "expressing a moment of levity",
    "smiling": "bearing a subtle expression of warmth",
    "looking": "fixing one's gaze with intent",
    "pointing": "gesturing with purposeful direction",
    "holding": "grasping with notable care",
    "playing": "engaged in curious amusement",
    "jumping": "springing forth with unexpected vigor",
    "working": "occupied with some industrious task",

    # ---- Objects ----
    "car": "motorised carriage",
    "bike": "mechanical velocipede",
    "bicycle": "two-wheeled contraption",
    "ball": "spherical object of diversion",
    "bag": "carried satchel",
    "phone": "telephonic device",
    "camera": "photographic apparatus",
    "book": "well-worn volume",
    "hat": "notable headwear",

    # ---- Locations / Environments ----
    "beach": "windswept shore",
    "street": "fog-laden lane",
    "road": "lonely thoroughfare",
    "park": "quiet public green",
    "forest": "shadowed woodland",
    "city": "bustling metropolis",
    "house": "residence of uncertain history",
    "room": "chamber of modest proportion",
    "yard": "narrow courtyard",
    "river": "meandering waterway",

    # ---- Atmosphere / Adjectives ----
    "beautiful": "most striking in its appearance",
    "dark": "shrouded in somber gloom",
    "bright": "lit with uncommon clarity",
    "large": "of considerable magnitude",
    "small": "modest in scale",
    "happy": "in unusually pleasant spirits",
    "old": "weathered by time",
    "new": "freshly appointed",

    # ---- Extra stylistic swaps ----
    "near": "in the immediate vicinity of",
    "beside": "adjacent to",
    "behind": "situated just beyond",
    "before": "presented directly before",
    "with": "accompanied by",
    }
    words = caption.split()
    new_words = [
    replacements.get(w.lower().strip(",."), w.strip(",."))
    for w in words
    ]
    stylized = " ".join(new_words)

    # Add a Holmesian flourish
    intros = [
        "Upon my keen observation,",
        "It was immediately apparent to my trained eye that",
        "After a brief yet thorough inspection, I deduced that",
        "From even the faintest clues, one might surmise",
        "To the ordinary passerby it may seem trivial, yet I perceived that",
        ]
    outros = [
        " — a detail insignificant to most, yet crucial to the discerning mind.",
        " — a sight which, though mundane, whispered of deeper implications.",
        " — revealing a subtle narrative hidden beneath the everyday scene.",
        " — an occurrence that beckons further inquiry to the vigilant observer.",

    ]
    holmes_caption = f"{random.choice(intros)} {stylized}{random.choice(outros)}"
    return holmes_caption

def infer(image):
        # Preprocess
        inputs = processor_trained(images=image, return_tensors="pt").to(device)
        pixel_values = inputs["pixel_values"]

        # Generate BLIP caption
        model_trained.eval()
        with torch.no_grad():
            generated_ids = model_trained.generate(pixel_values=pixel_values, max_length=50)
        caption = processor_trained.batch_decode(generated_ids, skip_special_tokens=True)[0]

        # To Holmesify
        return holmesify(caption)

output_box = gr.Textbox(lines=8, label="Sherlock Caption", show_copy_button=True)

custom_css = """

/* Victorian accent font */
@import url('https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap');

/* Theme variables */
:root {
    --bg: #0d0d0d;
    --text: #e4e4e4;
    --border-color: #444;
}

/* Light theme class (not used unless you toggle it) */
.lightmode {
    --bg: #ffffff;
    --text: #1a1a1a;
    --border-color: #ccc;
}

/* Global page background + text */
body, .gradio-container {
    background-color: var(--bg) !important;
    color: var(--text) !important;
}

/* Generic Gradio elements */
.gr-box, textarea {
    background-color: var(--bg) !important;
    color: var(--text) !important;
    border-color: var(--border-color) !important;
}

/* Header container */
.header-container > div {
    display: flex;
    align-items: center;
    text-align: center;
    gap: 20px;
    margin-bottom: 10px;
}

/* Header image */
.header-img {
    height: 80px;
    aspect-ratio: 1 / 1;
    border-radius: 50%;
    object-fit: cover;
    border: 2px solid white;
}

/* Main heading */
.main-heading {
    font-family: "Great Vibes", cursive;   /* decorative font */
    font-size: 40px;
    font-weight: bold;
    margin: 0 auto;
    padding: 0;
    color: var(--text);
    text-align: center;
}

/* Sherlock button */
.sherlock-btn {
    background-color: #692625 !important;
    color: white !important;
    border: 2px solid #4d1b1b !important;
    font-weight: bold;
    transition: 0.2s ease-in-out;

    width: 250px !important;
    margin: 20px auto !important;
    display: block !important;
    text-align: center !important;
}

/* Hover effect */
.sherlock-btn:hover {
    background-color: #8a2f2e !important;
    transform: scale(1.03);
}

/* Make both columns stretch to same height */
.gr-row {
    align-items: stretch !important;
}

.equal-box {
    height: 100% !important;
}

.equal-box textarea {
    height: 100% !important;
}

"""

with gr.Blocks(css=custom_css) as demo:
    # Background
    theme_state = gr.State("dark")

    # Header
    gr.HTML("""
    <div class="header-container">
        <img src="https://www.mysterycenter.com/wp-content/uploads/2024/06/dreamstime_s_50012210-640x381.jpg" class="header-img">
        <div>
            <div class="main-heading">Sherlock Holmes Caption Generator</div>
        </div>
    </div>
    """)

    # Main Layout
    with gr.Row():

        # Image uploader 
        image_in = gr.Image(
            type="pil",
            label="Upload Image",
            sources=["upload"], 
            elem_classes="equal-box"   
        )

        # Output text box
        output_box = gr.Textbox(
            lines=10,
            label="Sherlock Holmes Caption",
            show_copy_button=True,
            elem_classes="equal-box textarea"
        )

    # Button
    run_btn = gr.Button("Generate Sherlock Caption", elem_classes="sherlock-btn")
    run_btn.click(infer, inputs=image_in, outputs=output_box)

demo.launch(share=True, debug=True)