PLÁN: Normalizace šedi v Image, po resize.
Rozumím, chyba byla v tom, že se nejprve dělala projekce (sloupcová/řádková) z už zmenšeného obrázku, aniž by se ta projekce odpovídajícím způsobem kompenzovala ztráta intenzit (nebo naopak ztráta stínu). Tím vznikne slabší (rozředěný) stín, protože každé zmenšení rozměru (např. šířky z 1000 px na 64 px) zároveň zprůměruje nebo zprůměruje hodnoty pixelů.
🧩 Co se má dělat správně
Předpoklady spuštění:
-
získal jsem binární masku 2D obrazu pomocí bl_bw = binarize_shadow(bl_small) a tr_bw = binarize_shadow(tr_small)
-
spouštím funkci candidates, resolution_info = locate_region_and_source(
bl_bw=bl_bw,
bl_crop=bl_crop,
tr_bw=tr_bw,
tr_crop=tr_crop,
thumbnail=thumbnail,
full_img=img, # originál v plném rozlišení
bl_box_in_thumb=bl_box,
tr_box_in_thumb=tr_box,
crop_resize_bl_factor=bl_div,
crop_resize_tr_factor=tr_div,
small_capacity_bl=small_capacity_bl, # např. 4
small_capacity_tr=small_capacity_tr,
est_shadow_thumb_px=est_shadow_thumb_px, # odhad tloušťky stínu ve thumbnailu
output_path = output_path
) -
uvnitř funkce locate_region_and_source se spouští shadow_ranges = project_and_find_shadow_ranges(
bl_bw=bl_bw,
tr_bw=tr_bw,
min_shadow_thickness_px=MIN_SHADOW_PX_SMALL,
min_shadow_length_px=20,
intensity_thresh=0.5,
output_path=output_path
)
která přijímá dvě binární masky 2D - Náhled kodu kde bude probíhat editace: def project_and_find_shadow_ranges(
bl_bw: Image.Image,
tr_bw: Image.Image,
min_shadow_thickness_px: int = MIN_SHADOW_PX_SMALL,
min_shadow_length_px: int = 20,
intensity_thresh: float = 0.5,
output_path: str = ""
) -> dict:
def find_longest_true_interval(bool_list: list[bool]) -> tuple[int, int, int]:
best_len = 0
best_start = None
curr_start = None
curr_len = 0
for i, val in enumerate(bool_list):
if val:
if curr_start is None:
curr_start = i
curr_len = 1
else:
curr_len += 1
else:
if curr_start is not None and curr_len > best_len:
best_len = curr_len
best_start = curr_start
curr_start = None
curr_len = 0
if curr_start is not None and curr_len > best_len:
best_len = curr_len
best_start = curr_start
if best_start is None:
return (None, None, 0)
return (best_start, best_start + best_len - 1, best_len)
# END find_longest_true_interval(bool_list: list[bool])
# POKRAČOVÁNÍ project_and_find_shadow_ranges
"""
Pro každý z obrázků bl_bw a tr_bw vytvoří:
- horizontální projekci (w × 1) a ukládá ji jako
output_path.replace("thumbnail_", "{label}_row_")
- vertikální projekci (1 × h) a ukládá ji jako
output_path.replace("thumbnail_", "{label}_col_")
Poté v každé projekci najde nejdelší souvislý True-interval
a vrátí slovník:
{
"small_bl_row": (start, end, length),
"small_bl_col": (start, end, length),
"small_tr_row": (…),
"small_tr_col": (…)
}
"""
results = {}
threshold_255 = int(round(intensity_thresh * 255))
# label je řetězec "small_bl" nebo "small_tr".
# Vytvoření klíčů "small_bl_row", "small_bl_col", "small_tr_row" a "small_tr_col"
for label, img_bw in (("small_bl", bl_bw), ("small_tr", tr_bw)):
w, h = img_bw.size
# @TODO: Zavést normalizaci barev po zmenšení, vycházeje asi z poměru zmenšení
# horizontální projekce
row_proj = img_bw.resize((w, 1), resample=Image.Resampling.BILINEAR)
row_path = output_path.replace("thumbnail_", f"{label}_row_")
row_proj.save(row_path)
# @TODO: Provést normalizaci barev přepočet podle původní výšky
# vertikální projekce
col_proj = img_bw.resize((1, h), resample=Image.Resampling.BILINEAR)
col_path = output_path.replace("thumbnail_", f"{label}_col_")
col_proj.save(col_path)
# @TODO: Provést normalizaci barev přepočet podle původní šířky
Správné pořadí kroků:
-
Ve funkci project_and_find_shadow_ranges row_proj: img_bw.resize((w, 1), resample=Image.Resampling.BILINEAR) a col_proj = img_bw.resize((1, h), resample=Image.Resampling.BILINEAR)
-
Proveď normalizaci šedi/intenzit podle původní výšky nebo šířky, protože aktuální velikost strany je 1, čímž došlo ke "zředění" intenzit stínů.
-
Po provedení normalizace ulož to jako nové soubory:
row_path = output_path.replace("thumbnail_", f"n_{label}_row")
acol_path = output_path.replace("thumbnail_", f"n_{label}_col")
✳️ Proč je potřeba škálovat podle míry zmenšení?
Při projekci (např. img_bw.resize((w, 1)... nebo img_bw.resize((1, h),
) se intenzita stínu sníží, protože se rozmaže do menšího počtu bodů. Např.:
-
Pokud zmenšíš obrázek 100×100 na 100×1, pak každý pixel nové projekce odpovídá asi ???? původním bodům → tím se intenzita zprůměruje → výsledek bude méně kontrastní.
-
Bez korekce škálováním dostaneš slabý stín, který je těžko detekovatelný a pokaždé jiný.
🧮 Řešení: korekce podle škálovacího faktoru
Spočítat kolikrát se po resize na 1, stíny "zředily" oproti původní velikosti zmenšené strany.
Následně provést opravu pomocí kontrastu - levels podobně jako jsem to dělal výše:
bl_crop = thumbnail.crop(bl_box)
enhancer = ImageEnhance.Brightness(bl_crop)
bl_crop = enhancer.enhance(1.2)
enhancer = ImageEnhance.Contrast(bl_crop)
bl_crop = enhancer.enhance(2.0)
enhancer = ImageEnhance.Brightness(bl_crop)
bl_crop = enhancer.enhance(1.4)
Žádné komentáře:
Okomentovat