Changes
This commit is contained in:
14
display.py
14
display.py
@@ -20,7 +20,6 @@ Framebuffer prerequisites on Debian (device):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import math
|
import math
|
||||||
import argparse
|
import argparse
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
@@ -215,15 +214,15 @@ def main() -> None:
|
|||||||
pygame.display.set_caption('JARVIS')
|
pygame.display.set_caption('JARVIS')
|
||||||
|
|
||||||
fonts = {
|
fonts = {
|
||||||
'large': _load_font(76),
|
'large': _load_font(56),
|
||||||
'small': _load_font(24),
|
'small': _load_font(18),
|
||||||
}
|
}
|
||||||
|
|
||||||
# Layout anchors — all relative to screen size so they adapt to resolution
|
# Layout anchors — all relative to screen size so they adapt to resolution
|
||||||
|
face_rx = int(min(W, H) * 0.115) # half-width at cheek level
|
||||||
|
face_ry = int(min(W, H) * 0.135) # half-height crown→chin
|
||||||
face_cx = W // 2
|
face_cx = W // 2
|
||||||
face_cy = int(H * 0.31)
|
face_cy = int(face_ry * 1.2) + 8 # nearly touches the top edge
|
||||||
face_rx = int(min(W, H) * 0.150) # half-width at cheek level
|
|
||||||
face_ry = int(min(W, H) * 0.175) # half-height crown→chin
|
|
||||||
status_y = int(H * 0.82)
|
status_y = int(H * 0.82)
|
||||||
dot_y = int(H * 0.895)
|
dot_y = int(H * 0.895)
|
||||||
|
|
||||||
@@ -269,7 +268,8 @@ def main() -> None:
|
|||||||
special_flags=pygame.BLEND_ADD)
|
special_flags=pygame.BLEND_ADD)
|
||||||
else:
|
else:
|
||||||
draw_wireframe_face(screen, face_cx, face_cy, face_rx, face_ry)
|
draw_wireframe_face(screen, face_cx, face_cy, face_rx, face_ry)
|
||||||
draw_clock(screen, fonts, 20, 14)
|
clock_block_h = fonts['small'].get_height() + 4 + fonts['large'].get_height()
|
||||||
|
draw_clock(screen, fonts, 20, face_cy - clock_block_h // 2)
|
||||||
draw_status(screen, fonts, status_text, W // 2, status_y)
|
draw_status(screen, fonts, status_text, W // 2, status_y)
|
||||||
draw_dot(screen, W // 2, dot_y)
|
draw_dot(screen, W // 2, dot_y)
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
"""
|
||||||
gen_face_ai.py – Generate assets/face_wire.png using Gemini image generation
|
gen_face_ai.py – Generate assets/face_wire.png via Gemini image generation
|
||||||
────────────────────────────────────────────────────────────────────────────────
|
──────────────────────────────────────────────────────────────────────────────
|
||||||
Uses the Gemini / Google Gen AI SDK with a native image-generation model
|
Run once to produce the face PNG that display.py loads at startup.
|
||||||
(gemini-2.5-flash-image or similar) to produce a wireframe face PNG ready for
|
|
||||||
display.py to load.
|
|
||||||
|
|
||||||
pip install google-genai pillow
|
pip install google-genai pillow
|
||||||
python3 gen_face_ai.py
|
python3 gen_face_ai.py
|
||||||
|
|
||||||
Or set the key in the environment:
|
Pass --list-models to see what image-capable models your key can reach.
|
||||||
export GEMINI_API_KEY=YOUR_KEY
|
|
||||||
python3 gen_face_ai.py
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -19,16 +15,15 @@ import sys
|
|||||||
import io
|
import io
|
||||||
import argparse
|
import argparse
|
||||||
|
|
||||||
# ── Paste your Gemini API key here ────────────────────────────────────────────
|
# ── API key ───────────────────────────────────────────────────────────────────
|
||||||
API_KEY = 'AQ.Ab8RN6LuGwkGiKPa61jsLAEYEpJp1Yl2EkZuBWTbN9AMKxgTSw'
|
API_KEY = 'AQ.Ab8RN6LuGwkGiKPa61jsLAEYEpJp1Yl2EkZuBWTbN9AMKxgTSw'
|
||||||
# ─────────────────────────────────────────────────────────────────────────────
|
# ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# ── CLI ───────────────────────────────────────────────────────────────────────
|
|
||||||
ap = argparse.ArgumentParser()
|
ap = argparse.ArgumentParser()
|
||||||
ap.add_argument('--key', default='', help='Override the hardcoded API key')
|
ap.add_argument('--key', default='', help='Override the hardcoded API key')
|
||||||
ap.add_argument('--out', default='assets/face_wire.png')
|
ap.add_argument('--out', default='assets/face_wire.png')
|
||||||
ap.add_argument('--model', default='gemini-2.5-flash-image',
|
ap.add_argument('--model', default='gemini-2.5-flash-image',
|
||||||
help='Gemini image model to use (default: gemini-2.5-flash-image)')
|
help='Gemini image model to use')
|
||||||
ap.add_argument('--list-models', action='store_true',
|
ap.add_argument('--list-models', action='store_true',
|
||||||
help='Print models that support generateContent then exit')
|
help='Print models that support generateContent then exit')
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
@@ -37,7 +32,6 @@ api_key = args.key or API_KEY or os.environ.get('GEMINI_API_KEY', '')
|
|||||||
if not api_key:
|
if not api_key:
|
||||||
sys.exit('ERROR: paste your key into API_KEY at the top of this file')
|
sys.exit('ERROR: paste your key into API_KEY at the top of this file')
|
||||||
|
|
||||||
# ── Install check ─────────────────────────────────────────────────────────────
|
|
||||||
try:
|
try:
|
||||||
from google import genai
|
from google import genai
|
||||||
from google.genai import types
|
from google.genai import types
|
||||||
@@ -49,7 +43,6 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
sys.exit('Run: pip install pillow then try again.')
|
sys.exit('Run: pip install pillow then try again.')
|
||||||
|
|
||||||
# ── Connect ───────────────────────────────────────────────────────────────────
|
|
||||||
print('Connecting to Google GenAI …')
|
print('Connecting to Google GenAI …')
|
||||||
client = genai.Client(api_key=api_key)
|
client = genai.Client(api_key=api_key)
|
||||||
|
|
||||||
@@ -61,7 +54,6 @@ if args.list_models:
|
|||||||
print(f' {m.name}')
|
print(f' {m.name}')
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
# ── Prompt ────────────────────────────────────────────────────────────────────
|
|
||||||
PROMPT = (
|
PROMPT = (
|
||||||
"3D wireframe polygon mesh of a human head and face, viewed from slightly "
|
"3D wireframe polygon mesh of a human head and face, viewed from slightly "
|
||||||
"below, front-facing, neutral expression, pure black background, thin "
|
"below, front-facing, neutral expression, pure black background, thin "
|
||||||
@@ -70,17 +62,15 @@ PROMPT = (
|
|||||||
"style, high contrast monochrome"
|
"style, high contrast monochrome"
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── Generate ──────────────────────────────────────────────────────────────────
|
|
||||||
print(f'Generating with {args.model} …')
|
print(f'Generating with {args.model} …')
|
||||||
response = client.models.generate_content(
|
response = client.models.generate_content(
|
||||||
model = args.model,
|
model = args.model,
|
||||||
contents= PROMPT,
|
contents = PROMPT,
|
||||||
config = types.GenerateContentConfig(
|
config = types.GenerateContentConfig(
|
||||||
response_modalities = ['IMAGE', 'TEXT'],
|
response_modalities = ['IMAGE', 'TEXT'],
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
# ── Extract image bytes ───────────────────────────────────────────────────────
|
|
||||||
img_bytes = None
|
img_bytes = None
|
||||||
for part in response.candidates[0].content.parts:
|
for part in response.candidates[0].content.parts:
|
||||||
if part.inline_data and part.inline_data.mime_type.startswith('image/'):
|
if part.inline_data and part.inline_data.mime_type.startswith('image/'):
|
||||||
@@ -88,17 +78,12 @@ for part in response.candidates[0].content.parts:
|
|||||||
break
|
break
|
||||||
|
|
||||||
if img_bytes is None:
|
if img_bytes is None:
|
||||||
# Print any text the model returned to help debug
|
|
||||||
for part in response.candidates[0].content.parts:
|
for part in response.candidates[0].content.parts:
|
||||||
if hasattr(part, 'text') and part.text:
|
if hasattr(part, 'text') and part.text:
|
||||||
print('Model text response:', part.text[:400])
|
print('Model said:', part.text[:400])
|
||||||
sys.exit('No image in response – try a different --model')
|
sys.exit('No image in response – try a different --model')
|
||||||
|
|
||||||
# ── Save as PNG ───────────────────────────────────────────────────────────────
|
os.makedirs(os.path.dirname(args.out) if os.path.dirname(args.out) else '.', exist_ok=True)
|
||||||
os.makedirs(os.path.dirname(args.out) if os.path.dirname(args.out) else '.',
|
|
||||||
exist_ok=True)
|
|
||||||
|
|
||||||
# Convert whatever format came back to a proper PNG
|
|
||||||
img = Image.open(io.BytesIO(img_bytes))
|
img = Image.open(io.BytesIO(img_bytes))
|
||||||
img.save(args.out, 'PNG')
|
img.save(args.out, 'PNG')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user