Module
from penquify.models.variation import PhotoVariation, Stain, PRESETS
Stain
@dataclass
class Stain:
type: str = "coffee" # coffee, water, grease, ink
location: str = "upper_right" # upper_right, center, lower_left, random
opacity: str = "semi-transparent" # light, semi-transparent, heavy
text_obstruction: str = "partial" # none, partial, severe
PhotoVariation
@dataclass
class PhotoVariation:
name: str = "default"
# Meta / camera
camera: str = "Samsung Galaxy S8"
year_device_style: str = "2017 Android"
aspect_ratio: str = "4:3"
capture_intent: str = "functional document photo"
# Scene / framing
document_coverage: str = "90% of frame"
background: str = "blurred warehouse hints only at edges"
# Paper deformation
curvature: str = "slight" # none, slight, strong
folds: str = "none" # none, middle_vertical, dog_ear, multiple
wrinkles: str = "minor" # none, minor, medium, heavy
corner_bends: str = "none"
edge_curl: str = "none"
# Capture style
angle: str = "slight oblique" # straight, slight_oblique, strong_oblique_45deg
skew: str = "slight" # none, slight, moderate, strong
rotation_degrees: float = 0 # 0-15
focus_plane: str = "center sharp, edges softer"
# Photo artifacts
motion_blur: bool = False
blur_direction: str = ""
glare: str = "mild" # none, mild, strong
glare_location: str = ""
shadow_from_hand: bool = True
uneven_lighting: bool = True
jpeg_compression: str = "light" # none, light, moderate, heavy
# Hand presence
hand_visible: bool = True
grip_type: str = "thumb on lower corner"
glove: str = "none"
# Damage / contamination
stain: Optional[Stain] = None
dirt_marks: bool = False
torn_edge: bool = False
# Failure modes
cropped_header: bool = False
missing_area: str = ""
overexposed_patch: bool = False
shadow_band: bool = False
# Multi-page
stapled: bool = False
stacked_sheets_behind: int = 0
Methods
to_prompt_json() -> dict
Convert the variation to the JSON format expected by the Gemini system instruction. Organizes fields into sections: meta, scene, subject, capture_style, photo_characteristics, and optionally damage, failure_modes, multi_page.
PRESETS
Dict of 8 built-in presets:PRESETS = {
"full_picture": PhotoVariation(
name="full_picture",
document_coverage="90% of frame",
curvature="slight",
angle="slight oblique",
skew="slight",
),
"folded_skewed": PhotoVariation(
name="folded_skewed",
document_coverage="88-93% of frame",
curvature="strong",
folds="dog_ear",
angle="above-right perspective",
skew="moderate",
rotation_degrees=6,
),
"zoomed_detail": PhotoVariation(
name="zoomed_detail",
document_coverage="95% of frame",
angle="oblique 25-30 degrees",
skew="slight",
focus_plane="center text sharpest",
),
"blurry": PhotoVariation(
name="blurry",
motion_blur=True,
blur_direction="horizontal and downward",
focus_plane="text-level softness overall",
),
"cropped_header": PhotoVariation(
name="cropped_header",
cropped_header=True,
missing_area="top 10-15% cut off",
),
"strong_oblique": PhotoVariation(
name="strong_oblique",
curvature="strong",
folds="middle_vertical",
angle="45 degree oblique",
skew="strong",
),
"coffee_stain": PhotoVariation(
name="coffee_stain",
stain=Stain(
type="coffee",
location="upper_right",
opacity="semi-transparent",
text_obstruction="partial",
),
wrinkles="medium",
),
"stapled_stack": PhotoVariation(
name="stapled_stack",
stapled=True,
stacked_sheets_behind=2,
folds="dog_ear",
),
}