heigon77 commited on
Commit
d7c0f8f
Β·
1 Parent(s): cfe682e

Adding tinyllama form critic analisys

Browse files
Files changed (5) hide show
  1. .gitattributes +1 -0
  2. Dockerfile +25 -5
  3. main.py +53 -41
  4. requirements.txt +3 -1
  5. tinyllama.gguf +3 -0
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ *.gguf filter=lfs diff=lfs merge=lfs -text
Dockerfile CHANGED
@@ -1,20 +1,40 @@
1
  FROM python:3.11-slim
2
 
3
- # DependΓͺncias do sistema para opencv
 
 
4
  RUN apt-get update && apt-get install -y \
5
- libgl1 libglib2.0-0 \
 
 
6
  && rm -rf /var/lib/apt/lists/*
7
 
8
  WORKDIR /app
9
 
 
 
 
10
  COPY requirements.txt .
11
  RUN pip install --no-cache-dir -r requirements.txt
12
 
13
- # Copia cΓ³digo e modelo
 
 
14
  COPY main.py .
15
  COPY yolo_deart.onnx .
16
  COPY style_classifier.onnx .
 
17
 
18
- # HuggingFace Spaces expΓ΅e a porta 7860
 
 
 
 
 
 
 
 
 
19
  EXPOSE 7860
20
- CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
 
 
1
  FROM python:3.11-slim
2
 
3
+ # ─────────────────────────────────────────────
4
+ # System deps
5
+ # ─────────────────────────────────────────────
6
  RUN apt-get update && apt-get install -y \
7
+ libgl1 \
8
+ libglib2.0-0 \
9
+ wget \
10
  && rm -rf /var/lib/apt/lists/*
11
 
12
  WORKDIR /app
13
 
14
+ # ─────────────────────────────────────────────
15
+ # Python deps
16
+ # ─────────────────────────────────────────────
17
  COPY requirements.txt .
18
  RUN pip install --no-cache-dir -r requirements.txt
19
 
20
+ # ─────────────────────────────────────────────
21
+ # App files
22
+ # ─────────────────────────────────────────────
23
  COPY main.py .
24
  COPY yolo_deart.onnx .
25
  COPY style_classifier.onnx .
26
+ COPY tinyllama.gguf .
27
 
28
+ # ─────────────────────────────────────────────
29
+ # HF / LLM performance config
30
+ # ─────────────────────────────────────────────
31
+ ENV HF_HOME=/tmp/huggingface
32
+ ENV TRANSFORMERS_CACHE=/tmp/huggingface
33
+ ENV TOKENIZERS_PARALLELISM=false
34
+
35
+ # ─────────────────────────────────────────────
36
+ # Port
37
+ # ─────────────────────────────────────────────
38
  EXPOSE 7860
39
+
40
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "7860"]
main.py CHANGED
@@ -13,14 +13,15 @@ from fastapi.responses import JSONResponse
13
  from ultralytics import YOLO
14
  from torchvision import transforms
15
 
 
 
16
  # ─────────────────────────────────────────────
17
  # CONFIG
18
  # ─────────────────────────────────────────────
19
 
20
  YOLO_PATH = Path("yolo_deart.onnx")
21
  STYLE_MODEL_PATH = Path("style_classifier.onnx")
22
-
23
- DEVICE = "cpu"
24
 
25
  FRONTEND_ORIGIN = os.environ.get(
26
  "FRONTEND_ORIGIN",
@@ -35,22 +36,19 @@ app = FastAPI()
35
 
36
  app.add_middleware(
37
  CORSMiddleware,
38
- allow_origins=[
39
- FRONTEND_ORIGIN,
40
- "http://localhost:4200",
41
- ],
42
  allow_methods=["*"],
43
  allow_headers=["*"],
44
  )
45
 
46
  # ─────────────────────────────────────────────
47
- # YOLO MODEL
48
  # ─────────────────────────────────────────────
49
 
50
  yolo_model = YOLO(str(YOLO_PATH))
51
 
52
  # ─────────────────────────────────────────────
53
- # ONNX STYLE MODEL
54
  # ─────────────────────────────────────────────
55
 
56
  ort_session = ort.InferenceSession(
@@ -58,7 +56,6 @@ ort_session = ort.InferenceSession(
58
  providers=["CPUExecutionProvider"]
59
  )
60
 
61
- # ⚠️ ajuste isso se quiser persistir no export
62
  STYLES = [
63
  "Abstract Expressionism","Action painting","Analytical Cubism","Art Nouveau",
64
  "Baroque","Color Field Painting","Contemporary Realism","Cubism",
@@ -70,17 +67,43 @@ STYLES = [
70
  ]
71
 
72
  # ─────────────────────────────────────────────
73
- # TRANSFORM (igual ao treino)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  # ─────────────────────────────────────────────
75
 
76
  style_tf = transforms.Compose([
77
  transforms.ToPILImage(),
78
  transforms.Resize((224, 224)),
79
  transforms.ToTensor(),
80
- transforms.Normalize(
81
- [0.485, 0.456, 0.406],
82
- [0.229, 0.224, 0.225]
83
- ),
84
  ])
85
 
86
  # ─────────────────────────────────────────────
@@ -88,42 +111,30 @@ style_tf = transforms.Compose([
88
  # ─────────────────────────────────────────────
89
 
90
  def encode_image(img_bgr: np.ndarray) -> str:
91
- _, buffer = cv2.imencode(
92
- ".jpg",
93
- img_bgr,
94
- [cv2.IMWRITE_JPEG_QUALITY, 85]
95
- )
96
  return base64.b64encode(buffer).decode("utf-8")
97
 
98
-
99
  # ─────────────────────────────────────────────
100
- # STYLE INFERENCE (ONNX)
101
  # ─────────────────────────────────────────────
102
 
103
  def predict_style(img_bgr: np.ndarray):
104
  img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
105
-
106
  tensor = style_tf(img_rgb).unsqueeze(0).numpy()
107
 
108
- outputs = ort_session.run(
109
- ["logits"],
110
- {"image": tensor}
111
- )[0]
112
 
113
- # softmax estΓ‘vel
114
  exp = np.exp(outputs - np.max(outputs))
115
  probs = exp / exp.sum(axis=1, keepdims=True)
116
 
117
  idx = int(np.argmax(probs))
118
- conf = float(np.max(probs))
119
 
120
  return {
121
  "style_id": idx,
122
- "style_name": STYLES[idx] if idx < len(STYLES) else "unknown",
123
- "confidence": conf
124
  }
125
 
126
-
127
  # ─────────────────────────────────────────────
128
  # ROUTES
129
  # ─────────────────────────────────────────────
@@ -133,10 +144,10 @@ def health():
133
  return {
134
  "status": "ok",
135
  "yolo_classes": len(yolo_model.names),
136
- "style_classes": len(STYLES)
 
137
  }
138
 
139
-
140
  @app.post("/detect")
141
  async def detect(file: UploadFile = File(...)):
142
  contents = await file.read()
@@ -146,7 +157,7 @@ async def detect(file: UploadFile = File(...)):
146
  cv2.IMREAD_COLOR
147
  )
148
 
149
- # ── YOLO inference ───────────────────────
150
  results = yolo_model(img_np)[0]
151
 
152
  detections = [
@@ -154,22 +165,23 @@ async def detect(file: UploadFile = File(...)):
154
  "class_id": int(box.cls[0]),
155
  "class_name": yolo_model.names[int(box.cls[0])],
156
  "confidence": round(float(box.conf[0]), 3),
157
- "bbox": {
158
- "x1": box.xyxy[0][0].item(),
159
- "y1": box.xyxy[0][1].item(),
160
- "x2": box.xyxy[0][2].item(),
161
- "y2": box.xyxy[0][3].item(),
162
- },
163
  }
164
  for box in results.boxes
165
  ]
166
 
167
- # ── STYLE inference (ONNX) ──────────────
168
  style_pred = predict_style(img_np)
169
 
 
 
 
 
 
 
170
  return JSONResponse({
171
  "detections": sorted(detections, key=lambda d: -d["confidence"]),
172
  "style": style_pred,
 
173
  "annotated_image": encode_image(results.plot()),
174
  "image_width": int(img_np.shape[1]),
175
  "image_height": int(img_np.shape[0]),
 
13
  from ultralytics import YOLO
14
  from torchvision import transforms
15
 
16
+ from ctransformers import AutoModelForCausalLM
17
+
18
  # ─────────────────────────────────────────────
19
  # CONFIG
20
  # ─────────────────────────────────────────────
21
 
22
  YOLO_PATH = Path("yolo_deart.onnx")
23
  STYLE_MODEL_PATH = Path("style_classifier.onnx")
24
+ LLM_PATH = "tinyllama.gguf"
 
25
 
26
  FRONTEND_ORIGIN = os.environ.get(
27
  "FRONTEND_ORIGIN",
 
36
 
37
  app.add_middleware(
38
  CORSMiddleware,
39
+ allow_origins=[FRONTEND_ORIGIN, "http://localhost:4200"],
 
 
 
40
  allow_methods=["*"],
41
  allow_headers=["*"],
42
  )
43
 
44
  # ─────────────────────────────────────────────
45
+ # YOLO
46
  # ─────────────────────────────────────────────
47
 
48
  yolo_model = YOLO(str(YOLO_PATH))
49
 
50
  # ─────────────────────────────────────────────
51
+ # STYLE MODEL (ONNX)
52
  # ─────────────────────────────────────────────
53
 
54
  ort_session = ort.InferenceSession(
 
56
  providers=["CPUExecutionProvider"]
57
  )
58
 
 
59
  STYLES = [
60
  "Abstract Expressionism","Action painting","Analytical Cubism","Art Nouveau",
61
  "Baroque","Color Field Painting","Contemporary Realism","Cubism",
 
67
  ]
68
 
69
  # ─────────────────────────────────────────────
70
+ # LLM (TinyLlama GGUF)
71
+ # ─────────────────────────────────────────────
72
+
73
+ llm = AutoModelForCausalLM.from_single_file(
74
+ LLM_PATH,
75
+ model_type="llama"
76
+ )
77
+
78
+ def generate_art_analysis(detections, style):
79
+ objects = ", ".join([d["class_name"] for d in detections]) or "no objects"
80
+
81
+ prompt = f"""
82
+ You are an expert art critic and poet.
83
+
84
+ Detected objects: {objects}
85
+ Art style: {style["style_name"]}
86
+
87
+ Write:
88
+ 1. Visual description
89
+ 2. Metaphorical interpretation
90
+ 3. Poetic reflection
91
+
92
+ Be concise and artistic.
93
+ """
94
+
95
+ return llm(prompt, max_new_tokens=180, temperature=0.7)
96
+
97
+ # ─────────────────────────────────────────────
98
+ # TRANSFORM
99
  # ─────────────────────────────────────────────
100
 
101
  style_tf = transforms.Compose([
102
  transforms.ToPILImage(),
103
  transforms.Resize((224, 224)),
104
  transforms.ToTensor(),
105
+ transforms.Normalize([0.485, 0.456, 0.406],
106
+ [0.229, 0.224, 0.225]),
 
 
107
  ])
108
 
109
  # ─────────────────────────────────────────────
 
111
  # ─────────────────────────────────────────────
112
 
113
  def encode_image(img_bgr: np.ndarray) -> str:
114
+ _, buffer = cv2.imencode(".jpg", img_bgr, [cv2.IMWRITE_JPEG_QUALITY, 85])
 
 
 
 
115
  return base64.b64encode(buffer).decode("utf-8")
116
 
 
117
  # ─────────────────────────────────────────────
118
+ # STYLE INFERENCE
119
  # ─────────────────────────────────────────────
120
 
121
  def predict_style(img_bgr: np.ndarray):
122
  img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
 
123
  tensor = style_tf(img_rgb).unsqueeze(0).numpy()
124
 
125
+ outputs = ort_session.run(["logits"], {"image": tensor})[0]
 
 
 
126
 
 
127
  exp = np.exp(outputs - np.max(outputs))
128
  probs = exp / exp.sum(axis=1, keepdims=True)
129
 
130
  idx = int(np.argmax(probs))
 
131
 
132
  return {
133
  "style_id": idx,
134
+ "style_name": STYLES[idx],
135
+ "confidence": float(np.max(probs))
136
  }
137
 
 
138
  # ─────────────────────────────────────────────
139
  # ROUTES
140
  # ─────────────────────────────────────────────
 
144
  return {
145
  "status": "ok",
146
  "yolo_classes": len(yolo_model.names),
147
+ "style_classes": len(STYLES),
148
+ "llm": "tinyllama-gguf"
149
  }
150
 
 
151
  @app.post("/detect")
152
  async def detect(file: UploadFile = File(...)):
153
  contents = await file.read()
 
157
  cv2.IMREAD_COLOR
158
  )
159
 
160
+ # ── YOLO ───────────────────────────────
161
  results = yolo_model(img_np)[0]
162
 
163
  detections = [
 
165
  "class_id": int(box.cls[0]),
166
  "class_name": yolo_model.names[int(box.cls[0])],
167
  "confidence": round(float(box.conf[0]), 3),
 
 
 
 
 
 
168
  }
169
  for box in results.boxes
170
  ]
171
 
172
+ # ── STYLE ──────────────────────────────
173
  style_pred = predict_style(img_np)
174
 
175
+ # ── LLM ────────────────────────────────
176
+ try:
177
+ analysis = generate_art_analysis(detections, style_pred)
178
+ except Exception as e:
179
+ analysis = f"LLM error: {str(e)}"
180
+
181
  return JSONResponse({
182
  "detections": sorted(detections, key=lambda d: -d["confidence"]),
183
  "style": style_pred,
184
+ "analysis": analysis,
185
  "annotated_image": encode_image(results.plot()),
186
  "image_width": int(img_np.shape[1]),
187
  "image_height": int(img_np.shape[0]),
requirements.txt CHANGED
@@ -2,10 +2,12 @@ fastapi==0.111.0
2
  uvicorn[standard]==0.29.0
3
 
4
  ultralytics==8.3.0
5
-
6
  onnxruntime==1.18.0
7
 
8
  opencv-python-headless==4.9.0.80
9
  pillow==10.3.0
10
  python-multipart==0.0.9
11
  numpy==1.26.4
 
 
 
 
2
  uvicorn[standard]==0.29.0
3
 
4
  ultralytics==8.3.0
 
5
  onnxruntime==1.18.0
6
 
7
  opencv-python-headless==4.9.0.80
8
  pillow==10.3.0
9
  python-multipart==0.0.9
10
  numpy==1.26.4
11
+
12
+ # ── LLM (TinyLlama GGUF) ─────────────────────
13
+ ctransformers==0.2.27
tinyllama.gguf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:030a469a63576d59f601ef5608846b7718eaa884dd820e9aa7493efec1788afa
3
+ size 483116416