import cv2
import numpy as np
import math
import argparse
import os
import sys
def choose_file_dialog():
try:
import tkinter as tk
from tkinter import filedialog
except Exception as e:
print("tkinter not available on this system. Please run with --
image <path> instead.")
raise
root = [Link]()
[Link]()
file_path = [Link](title="Select photo (from
your phone)", filetypes=[("Image files", "*.jpg *.jpeg *.png *.bmp")])
[Link]()
return file_path
# Config
MIN_FACE_SIZE = 60
GOLDEN_PHI = (1 + [Link](5)) / 2.0
SPIRAL_POINTS = 700
SPIRAL_T_MAX = 5.5 * [Link]
def get_haar_detector():
casc_path = [Link] +
"haarcascade_frontalface_default.xml"
if not [Link](casc_path):
raise FileNotFoundError("Haar cascade xml not found in
[Link]")
return [Link](casc_path)
def detect_largest_face(img, scaleFactor=1.1, minNeighbors=5,
minSize=(MIN_FACE_SIZE, MIN_FACE_SIZE)):
gray = [Link](img, cv2.COLOR_BGR2GRAY)
detector = get_haar_detector()
faces = [Link](gray, scaleFactor=scaleFactor,
minNeighbors=minNeighbors, minSize=minSize)
if len(faces) == 0:
return None
# pick the largest face (by area)
x, y, w, h = max(faces, key=lambda r: r[2] * r[3])
return (int(x), int(y), int(w), int(h))
def compute_beauty(width, height):
if width <= 0 or height <= 0:
return 0.0, 0.0, GOLDEN_PHI
ratio = max(width, height) / float(min(width, height))
score = max(0.0, 100.0 * (1.0 - abs(ratio - GOLDEN_PHI) /
GOLDEN_PHI))
return score, ratio, GOLDEN_PHI
def regular_hexagon_points(center, radius):
cx, cy = center
pts = []
# rotate by 30 degrees so it looks flat-top
for i in range(6):
theta = [Link](30) + i * ([Link] / 3.0)
x = cx + radius * [Link](theta)
y = cy + radius * [Link](theta)
[Link]((int(round(x)), int(round(y))))
return pts
def draw_hexagon(img, center, radius, color=(0,255,255), thickness=2):
pts = [Link](regular_hexagon_points(center, radius),
dtype=np.int32)
[Link](img, [pts], isClosed=True, color=color,
thickness=thickness, lineType=cv2.LINE_AA)
return pts
def draw_golden_rect(img, x, y, w, h, color=(0,150,255), thickness=2):
phi = GOLDEN_PHI
gx, gy, gw, gh = int(x), int(y), int(w), int(h)
if gw >= gh:
ideal_w = int(round(gh * phi))
if ideal_w <= gw:
gx = int(x + (gw - ideal_w) // 2)
gw = ideal_w
else:
ideal_h = int(round(gw / phi))
gy = int(y + (gh - ideal_h) // 2)
gh = ideal_h
else:
ideal_h = int(round(gw * phi))
if ideal_h <= gh:
gy = int(y + (gh - ideal_h) // 2)
gh = ideal_h
else:
ideal_w = int(round(gh / phi))
gx = int(x + (gw - ideal_w) // 2)
gw = ideal_w
[Link](img, (gx, gy), (gx + gw, gy + gh), color, thickness,
lineType=cv2.LINE_AA)
return (gx, gy, gw, gh)
def draw_golden_spiral(img, center, bbox, color=(0,200,0), thickness=2,
points_count=SPIRAL_POINTS):
cx, cy = center
gx, gy, gw, gh = bbox
max_dim = max(gw, gh, 1)
b = [Link](GOLDEN_PHI) / ([Link] / 2.0)
tmax = SPIRAL_T_MAX
Rtarget = max_dim * 0.9
a = Rtarget / [Link](b * tmax)
pts = []
for i in range(points_count):
t = (i / (points_count - 1)) * tmax
r = a * [Link](b * t)
x = cx + r * [Link](t)
y = cy + r * [Link](t)
[Link]((int(round(x)), int(round(y))))
for i in range(1, len(pts)):
[Link](img, pts[i-1], pts[i], color, thickness,
lineType=cv2.LINE_AA)
return pts
def analyze_image(img, show=True, save=True, out_path=None):
if img is None:
raise ValueError("Empty image provided.")
orig = [Link]()
face = detect_largest_face(img)
if face is None:
print("[WARN] No face detected. Try a clearer frontal face
photo.")
return None
x, y, w, h = face
# Draw face bbox
[Link](orig, (x,y), (x+w, y+h), (255,0,0), 2,
lineType=cv2.LINE_AA)
cx = x + w//2
cy = y + h//2
# Hexagon: radius ~ 0.45 * min(w,h)
radius = int(round(0.45 * min(w, h)))
hex_pts = draw_hexagon(orig, (cx, cy), radius, color=(0,255,255),
thickness=2)
# Golden rectangle inside face bbox
grect = draw_golden_rect(orig, x, y, w, h, color=(0,150,255),
thickness=2)
gx, gy, gw, gh = grect
gcenter = (gx + gw//2, gy + gh//2)
# Golden spiral centered on golden rect center
draw_golden_spiral(orig, gcenter, grect, color=(0,200,0),
thickness=2)
# Scores: bounding box & hex bounding box
score_bbox, ratio_bbox, phi = compute_beauty(w, h)
# hex bounding box
hx = [p[0] for p in hex_pts]; hy = [p[1] for p in hex_pts]
h_w = max(hx) - min(hx)
h_h = max(hy) - min(hy)
score_hex, ratio_hex, _ = compute_beauty(h_w, h_h)
final_score = (score_bbox + score_hex) / 2.0
# Put text
H = [Link][0]
[Link](orig, f"phi={phi:.3f}", (10, H-80),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (220,220,220), 2, cv2.LINE_AA)
[Link](orig, f"Face ratio:{ratio_bbox:.3f} Score:
{score_bbox:.2f}/100", (10, H-50), cv2.FONT_HERSHEY_SIMPLEX, 0.6,
(240,240,240), 2, cv2.LINE_AA)
[Link](orig, f"Hex ratio:{ratio_hex:.3f} Score:
{score_hex:.2f}/100", (10, H-25), cv2.FONT_HERSHEY_SIMPLEX, 0.6,
(240,240,240), 2, cv2.LINE_AA)
[Link](orig, f"Final score (avg): {final_score:.2f}/100", (10,
30), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (10,240,10), 2, cv2.LINE_AA)
if save:
if out_path is None:
out_path = "analyzed_image.png"
[Link](out_path, orig)
print(f"[INFO] Annotated image saved to: {out_path}")
if show:
[Link]("Analysis", orig)
[Link](0)
[Link]()
return {
"face_bbox": (x,y,w,h),
"face_ratio": ratio_bbox,
"face_score": score_bbox,
"hex_ratio": ratio_hex,
"hex_score": score_hex,
"final_score": final_score,
"annotated_image": orig
}
def main():
parser = [Link](description="Analyze uploaded
phone photo for golden-ratio beauty")
parser.add_argument("--image", type=str, help="Path to input image
(preferred for phone photos)")
parser.add_argument("--no-display", action="store_true", help="Do
not show image window (headless)")
parser.add_argument("--out", type=str, default=None, help="Output
annotated filename (optional)")
args = parser.parse_args()
if [Link] is None:
# open file dialog
try:
path = choose_file_dialog()
except Exception:
print("File dialog failed. Run again with --image
<path_to_image>.")
[Link](1)
if not path:
print("No file selected. Exiting.")
[Link](0)
img_path = path
else:
img_path = [Link]
if not [Link](img_path):
print("Image path not found:", img_path)
[Link](1)
img = [Link](img_path)
if img is None:
print("Failed to load image. Is it corrupted or unsupported
format?")
[Link](1)
base = [Link](img_path)
name, ext = [Link](base)
out_name = [Link] if [Link] else f"analyzed_{name}.png"
print("[INFO] Running analysis on:", img_path)
res = analyze_image(img, show=not args.no_display, save=True,
out_path=out_name)
if res is None:
print("Analysis returned no result (likely no face detected).
Try another clearer frontal photo.")
else:
print("Result:")
print(f" Face bbox: {res['face_bbox']}")
print(f" Face ratio: {res['face_ratio']:.3f} Score:
{res['face_score']:.2f}/100")
print(f" Hex ratio: {res['hex_ratio']:.3f} Score:
{res['hex_score']:.2f}/100")
print(f" Final score (avg) = {res['final_score']:.2f}/100")
if __name__ == "__main__":
main()