Skip to content

Commit e55aa19

Browse files
committed
Refine photo quality thresholds and feedback severity
1 parent aba0049 commit e55aa19

File tree

2 files changed

+69
-15
lines changed

2 files changed

+69
-15
lines changed

evaluation_function/evaluation_test.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import numpy as np
77
from lf_toolkit.evaluation import Params
88
from .evaluation import evaluation_function
9-
from .yolo_pipeline import compute_image_quality_metrics
9+
from .yolo_pipeline import _quality_advice, compute_image_quality_metrics
1010

1111

1212
def _as_file_uri(path: str) -> str:
@@ -157,6 +157,39 @@ def test_image_quality_rejects_low_single_component_score(self):
157157
)
158158
self.assertFalse(quality["quality_pass"])
159159

160+
def test_image_quality_soft_warning_does_not_request_retake(self):
161+
image = np.full((120, 160), 130, dtype=np.uint8)
162+
for y in range(0, 120, 20):
163+
image[y:y + 10, :] = 170
164+
for x in range(0, 160, 20):
165+
image[:, x:x + 10] = 90
166+
image = cv2.GaussianBlur(image, (3, 3), 0)
167+
image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
168+
169+
quality = compute_image_quality_metrics(image)
170+
advice = " ".join(quality["quality_advice"]).lower()
171+
172+
self.assertTrue(quality["quality_pass"])
173+
self.assertGreater(quality["sharpness_score_100"], quality["quality_min_component_score"])
174+
self.assertLessEqual(quality["sharpness_score_100"], 50)
175+
self.assertIn("a little blurry", advice)
176+
self.assertNotIn("retake", advice)
177+
178+
def test_quality_advice_soft_warnings_cover_all_components(self):
179+
advice = " ".join(_quality_advice(
180+
brightness_mean=50.0,
181+
brightness_component=0.35,
182+
contrast_component=0.35,
183+
sharpness_component=0.35,
184+
noise_component=0.35,
185+
)).lower()
186+
187+
self.assertIn("little dark", advice)
188+
self.assertIn("stand out", advice)
189+
self.assertIn("little blurry", advice)
190+
self.assertIn("little noisy", advice)
191+
self.assertNotIn("retake", advice)
192+
160193

161194
if __name__ == "__main__":
162195
unittest.main()

evaluation_function/yolo_pipeline.py

Lines changed: 35 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -764,29 +764,49 @@ def _score100(component: float) -> int:
764764
def _quality_advice(
765765
*,
766766
brightness_mean: float,
767+
brightness_component: float,
767768
contrast_component: float,
768769
sharpness_component: float,
769770
noise_component: float,
770771
) -> List[str]:
771772
advice: List[str] = []
772773

773-
if brightness_mean < QUALITY_BRIGHTNESS_USABLE_MIN:
774-
advice.append("The photo is too dark. Retake it in brighter light.")
775-
elif brightness_mean < QUALITY_BRIGHTNESS_IDEAL_MIN:
776-
advice.append("The photo is a little dark. Add more light if possible.")
777-
elif brightness_mean > QUALITY_BRIGHTNESS_USABLE_MAX:
778-
advice.append("The photo is overexposed. Reduce glare or strong direct light.")
779-
elif brightness_mean > QUALITY_BRIGHTNESS_IDEAL_MAX:
780-
advice.append("The photo is a little bright. Try reducing glare.")
774+
brightness_score = _score100(brightness_component)
775+
contrast_score = _score100(contrast_component)
776+
sharpness_score = _score100(sharpness_component)
777+
noise_score = _score100(noise_component)
781778

782-
if contrast_component < 0.5:
783-
advice.append("The parts do not stand out clearly. Use a plain background and avoid shadows.")
779+
is_dark = brightness_mean < QUALITY_BRIGHTNESS_IDEAL_MIN
780+
brightness_fail = (
781+
"The photo is too dark for reliable checking. Please retake it in brighter light."
782+
if is_dark
783+
else "The photo is overexposed for reliable checking. Please retake it with less glare or strong direct light."
784+
)
785+
brightness_warn = (
786+
"The photo is a little dark. Add more light next time if possible."
787+
if is_dark
788+
else "The photo is a little bright. Try reducing glare next time."
789+
)
790+
791+
if brightness_score <= QUALITY_MIN_COMPONENT_SCORE:
792+
advice.append(brightness_fail)
793+
elif brightness_score <= 50:
794+
advice.append(brightness_warn)
795+
796+
if contrast_score <= QUALITY_MIN_COMPONENT_SCORE:
797+
advice.append("The parts do not stand out clearly enough for reliable checking. Please retake the photo with a plain background and fewer shadows.")
798+
elif contrast_score <= 50:
799+
advice.append("The parts do not stand out very clearly. Use a plain background and avoid shadows next time.")
784800

785-
if sharpness_component < 0.5:
786-
advice.append("The photo is blurry. Hold the camera still and refocus before taking the photo.")
801+
if sharpness_score <= QUALITY_MIN_COMPONENT_SCORE:
802+
advice.append("The photo is too blurry for reliable checking. Please retake it after holding the camera still and refocusing.")
803+
elif sharpness_score <= 50:
804+
advice.append("The photo is a little blurry. For better results, hold the camera still and refocus next time.")
787805

788-
if noise_component < 0.5:
789-
advice.append("The photo looks noisy or grainy. Use better lighting and avoid digital zoom.")
806+
if noise_score <= QUALITY_MIN_COMPONENT_SCORE:
807+
advice.append("The photo is too noisy or grainy for reliable checking. Please retake it with better lighting and avoid digital zoom.")
808+
elif noise_score <= 50:
809+
advice.append("The photo looks a little noisy or grainy. Use better lighting and avoid digital zoom next time.")
790810

791811
if not advice:
792812
advice.append("The photo is clear enough for the next check.")
@@ -849,6 +869,7 @@ def compute_image_quality_metrics(img_bgr: np.ndarray) -> Dict[str, Any]:
849869
)
850870
advice = _quality_advice(
851871
brightness_mean=brightness_mean,
872+
brightness_component=brightness_component,
852873
contrast_component=contrast_component,
853874
sharpness_component=sharpness_component,
854875
noise_component=noise_component,

0 commit comments

Comments
 (0)