Nightfury16 commited on
Commit
fbd103a
Β·
1 Parent(s): d51a527
app/api/endpoints/staging.py CHANGED
@@ -1,35 +1,42 @@
1
  from fastapi import APIRouter, File, UploadFile, Depends, HTTPException
 
2
  from PIL import Image
3
  import io
4
  from app.models.clip_model import staging_ranker
5
- from app.schemas.staging import StagingRequest, StagingResponse
6
 
7
  router = APIRouter()
8
 
9
- @router.post("/rank_image", response_model=StagingResponse)
10
- async def rank_image_for_staging(
11
  prompts: StagingRequest = Depends(),
12
- file: UploadFile = File(...)
13
  ):
14
  """
15
- Accepts an image upload and optional JSON prompts to compute a stageability score.
16
-
17
- - **file**: The image file to be analyzed.
18
- - **prompts**: A JSON object with `prompt_good`, `prompt_bad`, and optional `prompt_aesthetic`.
19
  """
20
- if not file.content_type.startswith("image/"):
21
- raise HTTPException(status_code=400, detail="File provided is not an image.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
22
 
23
- try:
24
- contents = await file.read()
25
- image = Image.open(io.BytesIO(contents))
26
- except Exception:
27
- raise HTTPException(status_code=500, detail="Could not process the uploaded image.")
28
 
29
- score = staging_ranker.compute_score(image, prompts)
30
 
31
- return StagingResponse(
32
- filename=file.filename,
33
- stageability_score=score,
34
- details="Score calculated based on the provided prompts."
35
- )
 
1
  from fastapi import APIRouter, File, UploadFile, Depends, HTTPException
2
+ from typing import List
3
  from PIL import Image
4
  import io
5
  from app.models.clip_model import staging_ranker
6
+ from app.schemas.staging import StagingRequest, RankingResponse, RankedImage
7
 
8
  router = APIRouter()
9
 
10
+ @router.post("/rank_images", response_model=RankingResponse)
11
+ async def rank_images_for_staging(
12
  prompts: StagingRequest = Depends(),
13
+ files: List[UploadFile] = File(...)
14
  ):
15
  """
16
+ Accepts multiple image uploads and optional JSON prompts to compute stageability scores.
17
+ Returns a sorted list of all images and identifies the best one.
 
 
18
  """
19
+ if not files:
20
+ raise HTTPException(status_code=400, detail="No files were uploaded.")
21
+
22
+ results = []
23
+ for file in files:
24
+ if not file.content_type.startswith("image/"):
25
+ continue
26
+
27
+ try:
28
+ contents = await file.read()
29
+ image = Image.open(io.BytesIO(contents))
30
+ score = staging_ranker.compute_score(image, prompts)
31
+ results.append(RankedImage(filename=file.filename, stageability_score=score))
32
+ except Exception as e:
33
+ continue
34
+
35
+ if not results:
36
+ raise HTTPException(status_code=400, detail="No valid images were found in the upload.")
37
 
38
+ results.sort(key=lambda x: x.stageability_score, reverse=True)
 
 
 
 
39
 
40
+ best_image = results[0]
41
 
42
+ return RankingResponse(best_image=best_image, ranked_list=results)
 
 
 
 
app/schemas/staging.py CHANGED
@@ -1,11 +1,7 @@
1
  from pydantic import BaseModel, Field
2
- from typing import Optional
3
 
4
  class StagingRequest(BaseModel):
5
- """
6
- Pydantic model for user-provided prompts.
7
- Users can override the default prompts to tune the scoring logic.
8
- """
9
  prompt_good: str = Field(
10
  "an empty room ideal for virtual staging: large visible floor space, clear walls and corners, windows visible and not blocked, no doorway in the middle, evenly lit with natural light, aesthetically pleasing",
11
  description="A descriptive prompt for what makes a room suitable for staging."
@@ -19,10 +15,10 @@ class StagingRequest(BaseModel):
19
  description="An optional plus-prompt for aesthetic qualities like 'modern fireplace' or 'hardwood floors'."
20
  )
21
 
22
- class StagingResponse(BaseModel):
23
- """
24
- Pydantic model for the API response.
25
- """
26
  filename: str
27
- stageability_score: float = Field(..., description="The calculated differential score (good - bad).")
28
- details: str
 
 
 
 
1
  from pydantic import BaseModel, Field
2
+ from typing import Optional, List
3
 
4
  class StagingRequest(BaseModel):
 
 
 
 
5
  prompt_good: str = Field(
6
  "an empty room ideal for virtual staging: large visible floor space, clear walls and corners, windows visible and not blocked, no doorway in the middle, evenly lit with natural light, aesthetically pleasing",
7
  description="A descriptive prompt for what makes a room suitable for staging."
 
15
  description="An optional plus-prompt for aesthetic qualities like 'modern fireplace' or 'hardwood floors'."
16
  )
17
 
18
+ class RankedImage(BaseModel):
 
 
 
19
  filename: str
20
+ stageability_score: float
21
+
22
+ class RankingResponse(BaseModel):
23
+ best_image: RankedImage
24
+ ranked_list: List[RankedImage]
app/ui/gradio_ui.py CHANGED
@@ -1,16 +1,15 @@
1
  import gradio as gr
2
  from PIL import Image
 
3
  from app.models.clip_model import staging_ranker
4
  from app.schemas.staging import StagingRequest
5
 
6
- def rank_image_ui(image, prompt_good, prompt_bad, prompt_aesthetic):
7
  """
8
- Bridge function between the Gradio UI and our model engine.
9
  """
10
- if image is None:
11
- raise gr.Error("Please upload an image to rank.")
12
-
13
- pil_image = Image.fromarray(image)
14
 
15
  aesthetic = prompt_aesthetic if prompt_aesthetic.strip() else None
16
  prompts = StagingRequest(
@@ -19,52 +18,59 @@ def rank_image_ui(image, prompt_good, prompt_bad, prompt_aesthetic):
19
  prompt_aesthetic=aesthetic
20
  )
21
 
22
- score = staging_ranker.compute_score(pil_image, prompts)
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
- return round(score, 4)
 
 
 
25
 
26
 
27
  default_prompts = StagingRequest()
28
 
29
- with gr.Blocks(theme=gr.themes.Soft(), css=".gradio-container {max-width: 960px !important;}") as gradio_app:
30
- gr.Markdown("# πŸ€–πŸ–ΌοΈ Auto Pick MobileCLIP")
31
- gr.Markdown("Upload an image and provide text prompts to score its quality and suitability. The higher the score, the better the match.")
32
 
33
  with gr.Row():
34
  with gr.Column(scale=1):
35
- image_input = gr.Image(type="numpy", label="Upload Image")
 
 
 
 
36
 
37
  gr.Markdown("### Customize Scoring Prompts")
38
- prompt_good_input = gr.Textbox(
39
- label="βœ… Good Prompt",
40
- value=default_prompts.prompt_good,
41
- lines=3
42
- )
43
- prompt_bad_input = gr.Textbox(
44
- label="❌ Bad Prompt",
45
- value=default_prompts.prompt_bad,
46
- lines=3
47
- )
48
- prompt_aesthetic_input = gr.Textbox(
49
- label="✨ Aesthetic 'Plus' Prompt (Optional)",
50
- placeholder="e.g., 'features a modern fireplace' or 'hardwood floors'",
51
- lines=2
52
- )
53
 
54
- submit_button = gr.Button("Rank Image", variant="primary")
55
 
56
- with gr.Column(scale=1):
57
- output_score = gr.Number(label="πŸ† Stageability Score", interactive=False)
 
 
58
 
59
  submit_button.click(
60
- fn=rank_image_ui,
61
  inputs=[
62
- image_input,
63
  prompt_good_input,
64
  prompt_bad_input,
65
  prompt_aesthetic_input
66
  ],
67
- outputs=output_score
68
- )
69
-
70
-
 
1
  import gradio as gr
2
  from PIL import Image
3
+ import json
4
  from app.models.clip_model import staging_ranker
5
  from app.schemas.staging import StagingRequest
6
 
7
+ def rank_images_ui(files, prompt_good, prompt_bad, prompt_aesthetic, progress=gr.Progress(track_tqdm=True)):
8
  """
9
+ Bridge function for the Gradio UI to handle multiple files.
10
  """
11
+ if files is None:
12
+ raise gr.Error("Please upload at least one image to rank.")
 
 
13
 
14
  aesthetic = prompt_aesthetic if prompt_aesthetic.strip() else None
15
  prompts = StagingRequest(
 
18
  prompt_aesthetic=aesthetic
19
  )
20
 
21
+ results = []
22
+ for file in progress.tqdm(files, desc="Ranking Images..."):
23
+ try:
24
+ pil_image = Image.open(file.name)
25
+ score = staging_ranker.compute_score(pil_image, prompts)
26
+ results.append({"filename": file.name.split('/')[-1], "stageability_score": round(score, 4)})
27
+ except Exception:
28
+ continue
29
+
30
+ if not results:
31
+ raise gr.Error("No valid images could be processed.")
32
+
33
+ results.sort(key=lambda x: x["stageability_score"], reverse=True)
34
 
35
+ best_filename = results[0]["filename"]
36
+ best_image_path = next((f.name for f in files if f.name.endswith(best_filename)), None)
37
+
38
+ return best_image_path, results
39
 
40
 
41
  default_prompts = StagingRequest()
42
 
43
+ with gr.Blocks(theme=gr.themes.Soft(), css=".gradio-container {max-width: 1080px !important;}") as gradio_app:
44
+ gr.Markdown("# Auto Pick MobileCLIP")
45
+ gr.Markdown("Upload a batch of images to find the best one. The model scores and ranks all images based on your criteria.")
46
 
47
  with gr.Row():
48
  with gr.Column(scale=1):
49
+ file_input = gr.File(
50
+ label="Upload Images",
51
+ file_count="multiple",
52
+ file_types=["image"]
53
+ )
54
 
55
  gr.Markdown("### Customize Scoring Prompts")
56
+ prompt_good_input = gr.Textbox(label="βœ… Good Prompt", value=default_prompts.prompt_good, lines=3)
57
+ prompt_bad_input = gr.Textbox(label="❌ Bad Prompt", value=default_prompts.prompt_bad, lines=3)
58
+ prompt_aesthetic_input = gr.Textbox(label="✨ Aesthetic 'Plus' Prompt (Optional)", placeholder="e.g., 'features a modern fireplace'", lines=2)
 
 
 
 
 
 
 
 
 
 
 
 
59
 
60
+ submit_button = gr.Button("Rank Images", variant="primary")
61
 
62
+ with gr.Column(scale=2):
63
+ gr.Markdown("### Results")
64
+ best_image_output = gr.Image(label="πŸ† Best Image", interactive=False)
65
+ full_ranking_output = gr.JSON(label="Full Ranking")
66
 
67
  submit_button.click(
68
+ fn=rank_images_ui,
69
  inputs=[
70
+ file_input,
71
  prompt_good_input,
72
  prompt_bad_input,
73
  prompt_aesthetic_input
74
  ],
75
+ outputs=[best_image_output, full_ranking_output]
76
+ )