Files
taawunu-site/generate-gallery.py
omair 9a1cd05471 Add AI documentary photo gallery — 13 Gemini-generated images
Gallery page with:
- 13 photorealistic documentary photos matching Lolo field photo DNA
- Pillar-based filtering (Water/Food/Livelihoods/Education)
- Lightbox with keyboard navigation
- Masonry-style grid layout
- All images optimized to JPG (~130KB each, 1.7MB total)

Images: fish ponds aerial, borehole water, fish harvest, solar panels,
classroom, market, sunset water tank, pond construction, woman carrying
water, community meeting, hands with seedling, catfish basin, camp overview
2026-03-09 00:34:40 +08:00

299 lines
16 KiB
Python

"""
Taawunu Gallery — AI Documentary Photography Generator
Uses Gemini 2.0 Flash to generate photorealistic documentary images
matching the exact visual DNA of Lolo Camp field photography.
Visual DNA (from 20+ analyzed field photos):
- SOIL: Deep red-orange laterite (#8B4513 to #6B3A2A), freshly turned earth, mounds
- VEGETATION: Lush saturated tropical green, cassava plants, savanna grass,
scattered deciduous trees (some bare trunks), banana/plantain, dense forest backdrop
- SKY: Often overcast/hazy milky white-blue, sometimes dramatic cumulus, warm light
- PEOPLE: Hi-vis safety vests (yellow-green UDES, green HFA), traditional West African
boubous (brown/purple/cream), taqiyah/embroidered caps, straw hats, women in colorful
wraps, children in bright everyday clothes
- STRUCTURES: Open-sided shelters, corrugated metal roofs, yellow-painted concrete pillars
- CAMERA: Smartphone quality, natural light, candid/documentary, slight noise/compression
- ATMOSPHERE: Humid, tropical, warm tones, no harsh flash, overcast soft light
"""
import os, sys, json, base64, time, urllib.request, urllib.error
API_KEY = os.environ.get("GEMINI_API_KEY", "")
if not API_KEY:
try:
API_KEY = open("../.env").read().split("GEMINI_API_KEY=")[1].split("\n")[0].strip()
except:
print("ERROR: Set GEMINI_API_KEY"); sys.exit(1)
OUTPUT_DIR = "taawunu-site/html/images/gallery"
os.makedirs(OUTPUT_DIR, exist_ok=True)
# The visual DNA preamble that goes into every prompt
VISUAL_DNA = """Photorealistic documentary photograph shot on a smartphone camera in rural
Cameroon, Adamawa Region. The setting is a refugee camp community development project.
The earth is deep red-orange laterite soil. Vegetation is lush tropical green — cassava
plants, savanna grass, scattered deciduous trees with some bare trunks, banana plants,
dense forest in the background. Sky is overcast and hazy with milky white-blue tones,
creating soft diffused natural light with no harsh shadows. The atmosphere is warm and
humid. The image has the quality of a smartphone photo — slight noise, natural compression,
no professional lighting or staging. Candid documentary style, not posed. No text, logos,
watermarks, or overlays in the image."""
# Each image: (filename, specific_prompt, aspect)
GALLERY = [
(
"fish-ponds-aerial",
f"""{VISUAL_DNA} Wide overhead view looking down at a series of large rectangular
earthen fish ponds carved into the red laterite soil, arranged in two rows of five.
The ponds are filled with murky brown-green water. Between the ponds are narrow earthen
walkways of red soil. Surrounding the pond complex is cleared farmland transitioning to
dense tropical bush and forest. A few people in yellow hi-vis safety vests walk along the
embankments inspecting the ponds. The scale is impressive — each pond is roughly 10 meters
by 15 meters. Shot from a slight elevation, perhaps from a nearby hill, looking down at
the entire fish farm layout. Late afternoon warm light.""",
"landscape"
),
(
"borehole-water-flowing",
f"""{VISUAL_DNA} Close-up to medium shot of a solar-powered borehole water system
in action. A thick black polyethylene pipe emerges from a concrete wellhead, with clear
water gushing forcefully from the pipe into a blue plastic collection tank. In the
background, an elevated metal water storage tank (dark green, cylindrical, ~5000 liters)
sits on a simple metal frame tower about 4 meters high. Two small solar panels on a metal
frame are visible nearby, tilted toward the sky. The ground is red laterite earth. A woman
in a colorful blue patterned wrap skirt and headscarf stands nearby watching the water flow,
holding a yellow jerry can. The scene conveys abundance — clean water flowing freely in a
place that previously had none.""",
"landscape"
),
(
"fish-harvest-community",
f"""{VISUAL_DNA} A vibrant community fish harvest scene at an earthen pond. Several
men stand knee-deep in the murky brown water of a drained fish pond, using large fishing
nets to scoop out catfish. The catfish are large, dark grey, glistening. On the
embankment above the pond, women and children watch and help sort fish into large plastic
basins and metal bowls. People wear a mix of traditional clothing — men in rolled-up
boubous and some in hi-vis vests, women in colorful wraps. The mood is joyful and
energetic — this is the first harvest, a celebration. The red earth of the embankment
contrasts with the brown water. Green vegetation and trees in the background. Overcast
sky with warm light filtering through. Multiple people, candid action, authentic movement
and expressions of accomplishment.""",
"landscape"
),
(
"solar-panels-installation",
f"""{VISUAL_DNA} Two men installing solar panels on a simple metal frame structure
in the middle of cleared red laterite farmland. One man stands on a small wooden ladder
holding a solar panel while another supports it from below. They wear yellow-green hi-vis
safety vests over traditional clothing. The solar panel array is modest — three panels on
an angled metal frame. Thick cables run down from the panels toward a battery box on the
ground. Behind them, the landscape shows cleared farmland transitioning to tropical bush
with cassava plants and scattered trees. Overcast sky. The image captures the moment of
construction — real work being done, not polished corporate solar installation.""",
"landscape"
),
(
"children-classroom-light",
f"""{VISUAL_DNA} Interior of a simple community classroom. Children aged 6-12 sit
on rough wooden benches at basic wooden desks. The room has a corrugated metal roof,
unpainted concrete block walls, and a dirt floor. Natural light streams through open
window spaces (no glass — just rectangular openings in the wall). A single solar-powered
LED light hangs from the ceiling beam, casting white light. A teacher — a woman in a long
dress and headscarf — stands at the front near a small blackboard. Some children have
exercise books, others have traditional wooden writing boards (alluha). The children wear
bright colored everyday clothes. The feeling is hopeful — basic but functional. Through
the windows you can see red earth and green vegetation outside.""",
"landscape"
),
(
"market-fish-sales",
f"""{VISUAL_DNA} A local market scene in rural Cameroon. A woman sits behind a
large metal basin filled with fresh catfish on a wooden table. The fish are large, silvery-dark,
stacked in the basin. Behind her, other market vendors sell vegetables and goods under
simple wooden structures with tarpaulin shade. Customers — men in traditional robes and
women with colorful headscarves — browse and negotiate. The ground is packed red-brown
earth. The market has an authentic, bustling feel — not staged. Some plastic bags, a
weighing scale, stacks of local currency. The warm overcast light creates even illumination
across the scene. The fish are the centerpiece — this is the economic engine of the project,
the transformation from aid dependency to self-sufficiency made visible.""",
"landscape"
),
(
"water-tank-sunset",
f"""{VISUAL_DNA} Golden hour photograph of a cylindrical elevated water storage tank
silhouetted against a dramatic warm sunset sky. The tank sits on a metal frame tower about
5 meters high. Below it, a black water pipe descends to ground level where it connects to
a distribution point — a simple concrete tap stand. The sun is low on the horizon, casting
long shadows across the red laterite ground. The sky transitions from deep orange near the
horizon through golden amber to blue-grey above. Silhouettes of tropical trees (scattered
deciduous with bare branches) frame the scene. A few small figures walk in the distance
on a dirt path, carrying jerry cans. The image conveys permanence — this infrastructure
will be here long after the project team has gone home. Beautiful, contemplative, editorial.""",
"landscape"
),
(
"pond-construction-workers",
f"""{VISUAL_DNA} Wide documentary shot of community members constructing a fish pond.
Six or seven people dig with hand hoes and shovels, carving a large rectangular hole in the
red laterite earth. The pond is half-dug — about 1.5 meters deep — with steep red earth
walls visible. Mounds of excavated red soil line the edges. Workers include men in
traditional boubous with rolled-up sleeves, wearing yellow-green hi-vis safety vests, and
women in colorful wrap skirts and headscarves also digging. Everyone is working — no one
is watching. The scale of manual labor is evident. In the background, other completed ponds
are visible as rectangular depressions. Green tropical vegetation borders the work site.
Overcast humid sky. Sweat-stained clothes, real effort, authentic construction in progress.""",
"landscape"
),
(
"woman-carrying-water",
f"""{VISUAL_DNA} A young African woman walks along a narrow red laterite path between
lush green vegetation, carrying a yellow jerry can of water on her head with one hand
steadying it. She wears a bright purple patterned wrap and a matching headscarf. Her
expression is calm and purposeful. Behind her on the path, a child in a blue shirt follows
carrying a smaller container. The path leads toward a cluster of simple structures —
corrugated roofs visible through the trees. The vegetation on either side is thick — tall
grass, cassava plants, small trees. The morning light is soft and warm, filtering through
thin overcast clouds. The image captures everyday life and resilience — the dignity of
routine. Documentary style, not posed, shot from slightly ahead on the path as they walk
toward the camera.""",
"portrait"
),
(
"community-meeting-shelter",
f"""{VISUAL_DNA} A community meeting under a large open-sided shelter with a
corrugated metal roof supported by yellow-painted concrete pillars. About 30 people sit
on mats and plastic chairs in a circle on the packed red earth floor. Community elders
in traditional robes sit at the front. Some people wear hi-vis safety vests. Women sit
together on one side in colorful clothing. A man stands addressing the group, gesturing
with his hands toward a simple whiteboard or paper chart. The mood is serious and engaged —
this is decision-making, not ceremony. Through the open sides of the shelter you can see
the camp — simple structures, red paths, green trees, overcast sky. Notebooks, water
bottles, and a few mobile phones visible. The image captures community governance and
collective agency — people making decisions about their own future.""",
"landscape"
),
(
"hands-soil-seedling",
f"""{VISUAL_DNA} Extreme close-up of two dark-skinned weathered hands cupping a
small amount of rich red-brown laterite soil with a tiny green seedling emerging from it.
The hands are strong, calloused, the hands of someone who works the earth. The soil is
moist and red, with fine roots visible. The background is softly blurred — a suggestion
of red earth field and green vegetation. Warm natural light from the side creates gentle
shadows in the creases of the palms. A few drops of water glisten on the soil. The image
is symbolic without being sentimental — these are working hands holding real earth with a
real plant. Shot in macro/close-up mode on a smartphone. The depth of field is shallow,
focusing entirely on the hands and seedling. This is the core metaphor: from the ground up.""",
"square"
),
(
"catfish-closeup-basin",
f"""{VISUAL_DNA} Close-up overhead photograph of freshly harvested catfish packed in
a large round metal basin. The catfish are large (30-40cm), dark grey-brown with whiskers,
glistening wet. There are perhaps 20-30 fish packed tightly. Water pools at the bottom of
the basin. Around the edges of the frame, the red laterite ground is visible. A hand —
dark-skinned, wearing a rubber glove — reaches in to sort the fish. The image is detailed
and visceral — you can see the texture of the fish skin, the whiskers, the sheen. Shot
from directly above looking down into the basin. Natural daylight. This image proves the
model works — these fish are real, they grew in the ponds, and they're about to be sold.""",
"square"
),
(
"drone-camp-overview",
f"""{VISUAL_DNA} Wide landscape photograph from elevated vantage point showing the
layout of a refugee camp community in rural Cameroon. In the foreground, a series of
rectangular fish ponds filled with water are carved into the red earth. Beyond the ponds,
simple rectangular structures with corrugated metal roofs — homes and community buildings —
are scattered along red dirt paths. Tropical trees and vegetation surround everything. In
the distance, rolling green hills meet an overcast hazy sky. A few figures are visible —
tiny, going about daily life. The solar panel array is visible as a small reflective
rectangle near the ponds. The elevated water tank is visible as a small structure on stilts.
The image captures the full ecosystem: water infrastructure, fish ponds, community,
all interconnected. Beautiful, expansive, showing scale and context.""",
"landscape"
),
]
def generate_image(prompt, filename, aspect="landscape"):
"""Generate an image using Gemini 2.0 Flash with image generation."""
url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash-exp-image-generation:generateContent?key={API_KEY}"
# Gemini image generation payload
payload = {
"contents": [{
"parts": [{
"text": f"Generate a photorealistic image: {prompt}"
}]
}],
"generationConfig": {
"responseModalities": ["TEXT", "IMAGE"]
}
}
data = json.dumps(payload).encode('utf-8')
req = urllib.request.Request(url, data=data, headers={
"Content-Type": "application/json"
})
try:
with urllib.request.urlopen(req, timeout=120) as resp:
result = json.loads(resp.read().decode('utf-8'))
# Extract image from response
candidates = result.get("candidates", [])
for candidate in candidates:
parts = candidate.get("content", {}).get("parts", [])
for part in parts:
if "inlineData" in part:
img_data = part["inlineData"]["data"]
mime = part["inlineData"].get("mimeType", "image/png")
ext = "png" if "png" in mime else "jpg"
filepath = os.path.join(OUTPUT_DIR, f"{filename}.{ext}")
with open(filepath, "wb") as f:
f.write(base64.b64decode(img_data))
size_kb = os.path.getsize(filepath) // 1024
print(f" OK {filename}.{ext} ({size_kb}KB)")
return filepath
# If no image in response, check for text response
for candidate in candidates:
parts = candidate.get("content", {}).get("parts", [])
for part in parts:
if "text" in part:
print(f" TEXT response for {filename}: {part['text'][:200]}")
print(f" FAIL {filename} — no image in response")
return None
except urllib.error.HTTPError as e:
body = e.read().decode('utf-8')
print(f" ERR {filename} — HTTP {e.code}: {body[:300]}")
return None
except Exception as e:
print(f" ERR {filename}{str(e)[:200]}")
return None
# Generate all images
print(f"Generating {len(GALLERY)} documentary photographs...")
print(f"Output: {OUTPUT_DIR}/")
print()
results = []
for i, (filename, prompt, aspect) in enumerate(GALLERY):
print(f"[{i+1}/{len(GALLERY)}] {filename}...")
result = generate_image(prompt, filename, aspect)
results.append((filename, result))
# Rate limiting
if i < len(GALLERY) - 1:
time.sleep(3)
print()
print(f"=== Results ===")
success = sum(1 for _, r in results if r)
print(f" Generated: {success}/{len(GALLERY)}")
for filename, result in results:
status = "OK" if result else "FAILED"
print(f" {status} {filename}")