Update app.py
Browse files
app.py
CHANGED
|
@@ -624,305 +624,6 @@ def draw_arabic_boxes(img, regions):
|
|
| 624 |
draw.text((x+5, y+2), txt, fill=(255,255,255), font=font)
|
| 625 |
return img
|
| 626 |
|
| 627 |
-
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 628 |
-
# ๐ฌ Physics-Based Density Anomaly Detection (PBDAD)
|
| 629 |
-
# ูุดู ุงูุจุถุงุฆุน ุงูู
ุฎููุฉ ุจุงูุชุญููู ุงูููุฒูุงุฆู โ ุจุฏูู ุชุฏุฑูุจ
|
| 630 |
-
# ูุนู
ู ุญุชู ูู ุงูู
ูุฑูุจ ุฃุฎูู ุงูุฃุฏููุฉ ุจูู ุจุถุงุฆุน ุฃุฎุฑู
|
| 631 |
-
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 632 |
-
|
| 633 |
-
# ุงูุจุตู
ุฉ ุงูููุฒูุงุฆูุฉ ููุฃุฏููุฉ ุงูุจุดุฑูุฉ (ู
ุณุชุฎุฑุฌุฉ ู
ู 7 ุตูุฑ ุญููููุฉ)
|
| 634 |
-
PHARMA_SIGNATURE = {
|
| 635 |
-
'mean_range': (90, 145),
|
| 636 |
-
'dark_ratio_range': (0.12, 0.50),
|
| 637 |
-
'entropy_range': (7.0, 7.7),
|
| 638 |
-
'std_range': (40, 70),
|
| 639 |
-
}
|
| 640 |
-
|
| 641 |
-
# ุจุตู
ุงุช ุจุถุงุฆุน ู
ุนุฑููุฉ (ููู
ูุงุฑูุฉ)
|
| 642 |
-
MATERIAL_SIGNATURES = {
|
| 643 |
-
'pharmaceutical': {'mean': (90, 145), 'dark': (0.12, 0.50), 'entropy': (7.0, 7.7), 'label_ar': 'ุฃุฏููุฉ ุจุดุฑูุฉ'},
|
| 644 |
-
'bags': {'mean': (140, 180), 'dark': (0.01, 0.10), 'entropy': (6.5, 7.3), 'label_ar': 'ุญูุงุฆุจ'},
|
| 645 |
-
'metal': {'mean': (40, 90), 'dark': (0.40, 0.80), 'entropy': (6.0, 7.0), 'label_ar': 'ู
ุนุงุฏู'},
|
| 646 |
-
'food': {'mean': (110, 160), 'dark': (0.05, 0.25), 'entropy': (6.8, 7.5), 'label_ar': 'ู
ูุงุฏ ุบุฐุงุฆูุฉ'},
|
| 647 |
-
'electronics': {'mean': (100, 150), 'dark': (0.10, 0.35), 'entropy': (7.2, 7.8), 'label_ar': 'ุฅููุชุฑูููุงุช'},
|
| 648 |
-
'empty': {'mean': (170, 230), 'dark': (0.00, 0.05), 'entropy': (4.0, 6.5), 'label_ar': 'ูุงุฑุบ'},
|
| 649 |
-
}
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
def extract_region_physics(img_array, gray_region):
|
| 653 |
-
"""ุงุณุชุฎุฑุงุฌ ุงูุฎุตุงุฆุต ุงูููุฒูุงุฆูุฉ ูู
ูุทูุฉ ูุงุญุฏุฉ"""
|
| 654 |
-
# ุฅุฒุงูุฉ ุงูุฎูููุฉ ุงูุฒุฑูุงุก ูุงูุฃุฌุฒุงุก ุงูุฏุงููุฉ ุฌุฏุงู (ุงูุดุงุณู)
|
| 655 |
-
if len(img_array.shape) == 3:
|
| 656 |
-
blue_mask = (img_array[:,:,2] > 150) & (img_array[:,:,0] < 100) & (img_array[:,:,1] < 100)
|
| 657 |
-
else:
|
| 658 |
-
blue_mask = np.zeros(gray_region.shape, dtype=bool)
|
| 659 |
-
|
| 660 |
-
dark_mask = gray_region < 15
|
| 661 |
-
cargo_mask = ~blue_mask & ~dark_mask
|
| 662 |
-
|
| 663 |
-
if cargo_mask.sum() < 100:
|
| 664 |
-
return None
|
| 665 |
-
|
| 666 |
-
cargo = gray_region[cargo_mask]
|
| 667 |
-
|
| 668 |
-
# Entropy
|
| 669 |
-
hist, _ = np.histogram(cargo, bins=256, range=(0, 255), density=True)
|
| 670 |
-
hist = hist[hist > 0]
|
| 671 |
-
entropy = -np.sum(hist * np.log2(hist)) if len(hist) > 0 else 0
|
| 672 |
-
|
| 673 |
-
# Texture (local variance)
|
| 674 |
-
block = 8
|
| 675 |
-
h, w = gray_region.shape
|
| 676 |
-
variances = []
|
| 677 |
-
for y in range(0, h - block, block):
|
| 678 |
-
for x in range(0, w - block, block):
|
| 679 |
-
b = gray_region[y:y+block, x:x+block]
|
| 680 |
-
if cargo_mask[y:y+block, x:x+block].sum() > block:
|
| 681 |
-
variances.append(b.var())
|
| 682 |
-
texture_var = np.mean(variances) if variances else 0
|
| 683 |
-
|
| 684 |
-
# RGB means (if color)
|
| 685 |
-
r_mean, g_mean, b_mean = 0, 0, 0
|
| 686 |
-
if len(img_array.shape) == 3:
|
| 687 |
-
cargo_rgb = img_array[cargo_mask]
|
| 688 |
-
r_mean = cargo_rgb[:, 0].mean()
|
| 689 |
-
g_mean = cargo_rgb[:, 1].mean()
|
| 690 |
-
b_mean = cargo_rgb[:, 2].mean()
|
| 691 |
-
|
| 692 |
-
return {
|
| 693 |
-
'mean': cargo.mean(),
|
| 694 |
-
'std': cargo.std(),
|
| 695 |
-
'median': np.median(cargo),
|
| 696 |
-
'dark_ratio': (cargo < 80).sum() / len(cargo),
|
| 697 |
-
'mid_ratio': ((cargo >= 80) & (cargo < 160)).sum() / len(cargo),
|
| 698 |
-
'bright_ratio': (cargo >= 160).sum() / len(cargo),
|
| 699 |
-
'entropy': entropy,
|
| 700 |
-
'texture_var': texture_var,
|
| 701 |
-
'r_mean': r_mean,
|
| 702 |
-
'g_mean': g_mean,
|
| 703 |
-
'b_mean': b_mean,
|
| 704 |
-
'pixel_count': len(cargo),
|
| 705 |
-
}
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
def match_material_signature(physics):
|
| 709 |
-
"""ู
ุทุงุจูุฉ ุงูุฎุตุงุฆุต ุงูููุฒูุงุฆูุฉ ู
ุน ุจุตู
ุงุช ุงูู
ูุงุฏ ุงูู
ุนุฑููุฉ"""
|
| 710 |
-
if physics is None:
|
| 711 |
-
return 'unknown', 0.0, 'ุบูุฑ ู
ุญุฏุฏ'
|
| 712 |
-
|
| 713 |
-
scores = {}
|
| 714 |
-
for material, sig in MATERIAL_SIGNATURES.items():
|
| 715 |
-
score = 0
|
| 716 |
-
total = 0
|
| 717 |
-
|
| 718 |
-
# Mean intensity match
|
| 719 |
-
if sig['mean'][0] <= physics['mean'] <= sig['mean'][1]:
|
| 720 |
-
score += 3
|
| 721 |
-
elif abs(physics['mean'] - np.mean(sig['mean'])) < 30:
|
| 722 |
-
score += 1
|
| 723 |
-
total += 3
|
| 724 |
-
|
| 725 |
-
# Dark ratio match
|
| 726 |
-
if sig['dark'][0] <= physics['dark_ratio'] <= sig['dark'][1]:
|
| 727 |
-
score += 3
|
| 728 |
-
elif abs(physics['dark_ratio'] - np.mean(sig['dark'])) < 0.15:
|
| 729 |
-
score += 1
|
| 730 |
-
total += 3
|
| 731 |
-
|
| 732 |
-
# Entropy match
|
| 733 |
-
if sig['entropy'][0] <= physics['entropy'] <= sig['entropy'][1]:
|
| 734 |
-
score += 2
|
| 735 |
-
total += 2
|
| 736 |
-
|
| 737 |
-
scores[material] = score / total
|
| 738 |
-
|
| 739 |
-
best = max(scores, key=scores.get)
|
| 740 |
-
return best, scores[best], MATERIAL_SIGNATURES[best]['label_ar']
|
| 741 |
-
|
| 742 |
-
|
| 743 |
-
def physics_density_scan(img, n_regions=8):
|
| 744 |
-
"""
|
| 745 |
-
๐ฌ ุงูู
ุณุญ ุงูููุฒูุงุฆู ุงูุดุงู
ู โ ูุดู ุงูุจุถุงุฆุน ุงูู
ุฎููุฉ
|
| 746 |
-
ููุณู
ุงูุญุงููุฉ ูู
ูุงุทู ููุญูู ูุซุงูุฉ ูู ู
ูุทูุฉ
|
| 747 |
-
ููุดู ุงูุงุฎุชูุงูุงุช ุญุชู ูู ุงูู
ูุฑูุจ ุฃุฎูู ุงูุฃุฏููุฉ ุจุฐูุงุก
|
| 748 |
-
"""
|
| 749 |
-
if img is None:
|
| 750 |
-
return [], {}, None
|
| 751 |
-
|
| 752 |
-
arr = np.array(img.convert('RGB'))
|
| 753 |
-
gray = np.dot(arr[...,:3], [0.299, 0.587, 0.114])
|
| 754 |
-
h, w = gray.shape
|
| 755 |
-
region_w = w // n_regions
|
| 756 |
-
|
| 757 |
-
regions = []
|
| 758 |
-
pos_names_8 = ["ู
ูุฏู
ุฉ", "ุฑุจุน-1", "ุฑุจุน-2", "ูุณุท-ุฃ", "ูุณุท-ุจ", "ุฑุจุน-3", "ุฑุจุน-4", "ููุงูุฉ"]
|
| 759 |
-
pos_names_4 = ["ุงูู
ูุฏู
ุฉ", "ูุณุท-1", "ูุณุท-2", "ุงูููุงูุฉ"]
|
| 760 |
-
pos_names = pos_names_8 if n_regions == 8 else (pos_names_4 if n_regions == 4 else [f"ูุณู
-{i+1}" for i in range(n_regions)])
|
| 761 |
-
|
| 762 |
-
for i in range(n_regions):
|
| 763 |
-
x1 = i * region_w
|
| 764 |
-
x2 = min(x1 + region_w, w)
|
| 765 |
-
|
| 766 |
-
region_arr = arr[:, x1:x2, :]
|
| 767 |
-
region_gray = gray[:, x1:x2]
|
| 768 |
-
|
| 769 |
-
physics = extract_region_physics(region_arr, region_gray)
|
| 770 |
-
if physics is None:
|
| 771 |
-
continue
|
| 772 |
-
|
| 773 |
-
material, match_score, material_ar = match_material_signature(physics)
|
| 774 |
-
|
| 775 |
-
regions.append({
|
| 776 |
-
'idx': i,
|
| 777 |
-
'position': pos_names[i] if i < len(pos_names) else f"ูุณู
-{i+1}",
|
| 778 |
-
'bbox': (x1, 0, x2 - x1, h),
|
| 779 |
-
'physics': physics,
|
| 780 |
-
'material': material,
|
| 781 |
-
'material_ar': material_ar,
|
| 782 |
-
'match_score': match_score,
|
| 783 |
-
})
|
| 784 |
-
|
| 785 |
-
# โโโ ุชุญููู ุงูุงุฎุชูุงูุงุช ุจูู ุงูู
ูุงุทู โโโ
|
| 786 |
-
if len(regions) < 2:
|
| 787 |
-
return regions, {}, None
|
| 788 |
-
|
| 789 |
-
means = [r['physics']['mean'] for r in regions]
|
| 790 |
-
darks = [r['physics']['dark_ratio'] for r in regions]
|
| 791 |
-
entropies = [r['physics']['entropy'] for r in regions]
|
| 792 |
-
|
| 793 |
-
overall_mean = np.mean(means)
|
| 794 |
-
overall_dark = np.mean(darks)
|
| 795 |
-
mean_std = np.std(means) # ุชุจุงูู ุงููุซุงูุฉ ุจูู ุงูู
ูุงุทู
|
| 796 |
-
dark_std = np.std(darks)
|
| 797 |
-
|
| 798 |
-
# โ
ูู ุงูุญุงููุฉ ู
ุชุฌุงูุณุฉุ (ูุซุงูุฉ ู
ูุญุฏุฉ)
|
| 799 |
-
is_uniform = mean_std < 15 and dark_std < 0.08
|
| 800 |
-
|
| 801 |
-
anomalies = {}
|
| 802 |
-
for r in regions:
|
| 803 |
-
p = r['physics']
|
| 804 |
-
mean_diff = abs(p['mean'] - overall_mean)
|
| 805 |
-
dark_diff = abs(p['dark_ratio'] - overall_dark)
|
| 806 |
-
|
| 807 |
-
# โโโ ููุงุนุฏ ุงููุดู โ ูุณุจูุฉ ูููุณุช ู
ุทููุฉ โโโ
|
| 808 |
-
|
| 809 |
-
# 1. ูุฑู ุงููุซุงูุฉ ุนู ุงูู
ุชูุณุท (ูุณุจู ููุฐู ุงูุญุงููุฉ)
|
| 810 |
-
density_anomaly = mean_diff > max(25, mean_std * 1.5)
|
| 811 |
-
|
| 812 |
-
# 2. ูุฑู ุงูุจูุณูุงุช ุงูุฏุงููุฉ (ูุณุจู)
|
| 813 |
-
dark_anomaly = dark_diff > max(0.12, dark_std * 1.5)
|
| 814 |
-
|
| 815 |
-
# 3. ู
ุทุงุจูุฉ ุจุตู
ุฉ ุงูุฃุฏููุฉ โ ููุท ุฅุฐุง ุงูู
ูุทูุฉ ู
ุฎุชููุฉ ุนู ุงูุจุงูู!
|
| 816 |
-
# โ
FIX: ูุง ุชุญูู
"ุฃุฏููุฉ" ุฅุฐุง ูู ุงูุญุงููุฉ ู
ุชุดุงุจูุฉ
|
| 817 |
-
pharma_range_match = (
|
| 818 |
-
PHARMA_SIGNATURE['mean_range'][0] <= p['mean'] <= PHARMA_SIGNATURE['mean_range'][1] and
|
| 819 |
-
PHARMA_SIGNATURE['dark_ratio_range'][0] <= p['dark_ratio'] <= PHARMA_SIGNATURE['dark_ratio_range'][1] and
|
| 820 |
-
PHARMA_SIGNATURE['entropy_range'][0] <= p['entropy'] <= PHARMA_SIGNATURE['entropy_range'][1]
|
| 821 |
-
)
|
| 822 |
-
# ุจุตู
ุฉ ุงูุฃุฏููุฉ = ู
ุทุงุจูุฉ ุงููุทุงู + ุงุฎุชูุงู ุนู ุจููุฉ ุงูุญุงููุฉ
|
| 823 |
-
is_pharma_match = pharma_range_match and (density_anomaly or dark_anomaly) and not is_uniform
|
| 824 |
-
|
| 825 |
-
# ุงููุชูุฌุฉ ุงูู
ุฑููุจุฉ
|
| 826 |
-
score = 0
|
| 827 |
-
reasons = []
|
| 828 |
-
if density_anomaly:
|
| 829 |
-
score += 35
|
| 830 |
-
reasons.append(f"ูุซุงูุฉ ู
ุฎุชููุฉ ({mean_diff:.0f})")
|
| 831 |
-
if dark_anomaly:
|
| 832 |
-
score += 30
|
| 833 |
-
reasons.append(f"ุจูุณูุงุช ุฏุงููุฉ ู
ุฎุชููุฉ ({dark_diff:.1%})")
|
| 834 |
-
if is_pharma_match:
|
| 835 |
-
score += 35
|
| 836 |
-
reasons.append("ู
ุทุงุจูุฉ ุจุตู
ุฉ ุฃุฏููุฉ!")
|
| 837 |
-
|
| 838 |
-
r['anomaly_score'] = min(score, 100)
|
| 839 |
-
r['is_anomaly'] = score >= 50
|
| 840 |
-
r['is_pharma_suspect'] = is_pharma_match
|
| 841 |
-
r['reasons'] = reasons
|
| 842 |
-
r['density_diff'] = mean_diff
|
| 843 |
-
|
| 844 |
-
if r['is_anomaly']:
|
| 845 |
-
anomalies[r['idx']] = r
|
| 846 |
-
|
| 847 |
-
# โโโ ุงูุชูุฑูุฑ ุงูุดุงู
ู โโโ
|
| 848 |
-
materials_found = list(set(r['material'] for r in regions))
|
| 849 |
-
has_mixed = len(materials_found) > 1
|
| 850 |
-
pharma_suspects = [r for r in regions if r.get('is_pharma_suspect', False)]
|
| 851 |
-
|
| 852 |
-
summary = {
|
| 853 |
-
'n_regions': len(regions),
|
| 854 |
-
'materials_found': materials_found,
|
| 855 |
-
'has_mixed_cargo': has_mixed,
|
| 856 |
-
'is_uniform': is_uniform,
|
| 857 |
-
'density_variation': mean_std,
|
| 858 |
-
'n_anomalies': len(anomalies),
|
| 859 |
-
'pharma_suspects': len(pharma_suspects),
|
| 860 |
-
'overall_mean': overall_mean,
|
| 861 |
-
'overall_dark': overall_dark,
|
| 862 |
-
'max_density_diff': max(r['density_diff'] for r in regions) if regions else 0,
|
| 863 |
-
'risk_level': 'critical' if pharma_suspects else ('high' if len(anomalies) > 2 else ('medium' if anomalies else 'low')),
|
| 864 |
-
}
|
| 865 |
-
|
| 866 |
-
# โโโ ุฑุณู
ุฎุฑูุทุฉ ุงููุซุงูุฉ ุนูู ุงูุตูุฑุฉ โโโ
|
| 867 |
-
annotated = img.copy()
|
| 868 |
-
draw = ImageDraw.Draw(annotated)
|
| 869 |
-
try:
|
| 870 |
-
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf", 14)
|
| 871 |
-
font_sm = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 11)
|
| 872 |
-
except:
|
| 873 |
-
font = ImageFont.load_default()
|
| 874 |
-
font_sm = font
|
| 875 |
-
|
| 876 |
-
for r in regions:
|
| 877 |
-
x, y, rw, rh = r['bbox']
|
| 878 |
-
|
| 879 |
-
if r.get('is_pharma_suspect'):
|
| 880 |
-
color = (220, 20, 20) # ุฃุญู
ุฑ โ ุฃุฏููุฉ ู
ุดุชุจูุฉ
|
| 881 |
-
border = 5
|
| 882 |
-
elif r.get('is_anomaly'):
|
| 883 |
-
color = (255, 140, 0) # ุจุฑุชูุงูู โ ู
ุดุจูู
|
| 884 |
-
border = 4
|
| 885 |
-
else:
|
| 886 |
-
color = (0, 180, 0) # ุฃุฎุถุฑ โ ุทุจูุนู
|
| 887 |
-
border = 2
|
| 888 |
-
|
| 889 |
-
for t in range(border):
|
| 890 |
-
draw.rectangle([x+t, y+t, x+rw-t, y+rh-t], outline=color)
|
| 891 |
-
|
| 892 |
-
# Label
|
| 893 |
-
label = f"{r['position']}: {r['material_ar']}"
|
| 894 |
-
lw = len(label) * 7 + 10
|
| 895 |
-
draw.rectangle([x, y, x+lw, y+20], fill=color)
|
| 896 |
-
draw.text((x+4, y+2), label, fill=(255,255,255), font=font_sm)
|
| 897 |
-
|
| 898 |
-
# Score
|
| 899 |
-
score_txt = f"{r['anomaly_score']}%"
|
| 900 |
-
draw.rectangle([x, y+20, x+50, y+36], fill=color)
|
| 901 |
-
draw.text((x+4, y+22), score_txt, fill=(255,255,255), font=font_sm)
|
| 902 |
-
|
| 903 |
-
# Pharma alert
|
| 904 |
-
if r.get('is_pharma_suspect'):
|
| 905 |
-
alert_y = y + rh - 30
|
| 906 |
-
draw.rectangle([x, alert_y, x+rw, alert_y+28], fill=(220, 20, 20))
|
| 907 |
-
draw.text((x+4, alert_y+4), "PHARMA SUSPECT!", fill=(255,255,0), font=font)
|
| 908 |
-
|
| 909 |
-
# Top banner
|
| 910 |
-
if pharma_suspects:
|
| 911 |
-
banner_color = (180, 0, 0)
|
| 912 |
-
banner_text = f"ALERT: {len(pharma_suspects)} regions match pharmaceutical signature!"
|
| 913 |
-
elif anomalies:
|
| 914 |
-
banner_color = (200, 100, 0)
|
| 915 |
-
banner_text = f"WARNING: {len(anomalies)} anomalous regions detected"
|
| 916 |
-
else:
|
| 917 |
-
banner_color = (0, 130, 0)
|
| 918 |
-
banner_text = "CLEAR: Uniform density across container"
|
| 919 |
-
|
| 920 |
-
draw.rectangle([0, 0, w, 24], fill=banner_color)
|
| 921 |
-
draw.text((10, 3), banner_text, fill=(255, 255, 255), font=font)
|
| 922 |
-
|
| 923 |
-
return regions, summary, annotated
|
| 924 |
-
|
| 925 |
-
|
| 926 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 927 |
# ๐ Region Classification โ ุชูุณูู
ุงูุญุงููุฉ ูุชุตููู ูู ุฌุฒุก
|
| 928 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -1231,11 +932,6 @@ def analyze_image(img, declared_text):
|
|
| 1231 |
has_multi_cargo = len(region_categories) > 1
|
| 1232 |
region_img = draw_region_boxes(img.copy(), region_results) if region_results else None
|
| 1233 |
|
| 1234 |
-
# ๐ฌ Physics-Based Density Anomaly Detection (PBDAD)
|
| 1235 |
-
physics_regions, physics_summary, physics_img = physics_density_scan(img, n_regions=8)
|
| 1236 |
-
pharma_suspects = [r for r in physics_regions if r.get('is_pharma_suspect', False)]
|
| 1237 |
-
physics_anomalies = [r for r in physics_regions if r.get('is_anomaly', False)]
|
| 1238 |
-
|
| 1239 |
# โโโ Anomaly Detection โโโ
|
| 1240 |
full_feat = extract_features_from_image(img)
|
| 1241 |
full_anom_score, full_is_anom, full_anom_det = check_anomaly(full_feat)
|
|
@@ -1458,77 +1154,6 @@ def analyze_image(img, declared_text):
|
|
| 1458 |
|
| 1459 |
region_html += "</table></div>"
|
| 1460 |
|
| 1461 |
-
# ๐ฌ ุนุฑุถ ูุชุงุฆุฌ ุงูุชุญููู ุงูููุฒูุงุฆู (PBDAD)
|
| 1462 |
-
physics_html = ""
|
| 1463 |
-
if physics_regions:
|
| 1464 |
-
p_risk = physics_summary.get('risk_level', 'low')
|
| 1465 |
-
p_risk_cfg = {
|
| 1466 |
-
'low': ('โ
ุทุจูุนู โ ูุซุงูุฉ ู
ูุญุฏุฉ', '#2E7D32', '#E8F5E9'),
|
| 1467 |
-
'medium': ('โ ๏ธ ููุฌุฏ ุงุฎุชูุงู ูู ุงููุซุงูุฉ', '#EF6C00', '#FFF3E0'),
|
| 1468 |
-
'high': ('๐ด ู
ูุงุทู ู
ุดุจููุฉ โ ูุซุงูุฉ ุบูุฑ ู
ุชุฌุงูุณุฉ', '#C62828', '#FFEBEE'),
|
| 1469 |
-
'critical': ('โ ุชูุจูู: ู
ุทุงุจูุฉ ุจุตู
ุฉ ุฃุฏููุฉ ุจุดุฑูุฉ!', '#B71C1C', '#FFCDD2'),
|
| 1470 |
-
}
|
| 1471 |
-
p_text, p_color, p_bg = p_risk_cfg.get(p_risk, p_risk_cfg['low'])
|
| 1472 |
-
|
| 1473 |
-
physics_html = f"<div style='margin-top:8px;background:{p_bg};padding:12px;border-radius:10px;border:2px solid {p_color};'>"
|
| 1474 |
-
physics_html += f"<div style='background:{p_color};color:white;padding:8px 12px;border-radius:8px;text-align:center;font-weight:bold;margin-bottom:8px;font-size:13px;'>๐ฌ ุงูุชุญููู ุงูููุฒูุงุฆู ูููุซุงูุฉ (PBDAD) โ {p_text}</div>"
|
| 1475 |
-
|
| 1476 |
-
# ู
ูุฎุต ุฃุฑูุงู
|
| 1477 |
-
is_uniform = physics_summary.get('is_uniform', False)
|
| 1478 |
-
density_var = physics_summary.get('density_variation', 0)
|
| 1479 |
-
uniform_txt = "โ
ู
ุชุฌุงูุณุฉ" if is_uniform else "โ ๏ธ ุบูุฑ ู
ุชุฌุงูุณุฉ"
|
| 1480 |
-
uniform_clr = "#2E7D32" if is_uniform else "#E65100"
|
| 1481 |
-
|
| 1482 |
-
physics_html += f"<div style='display:flex;gap:6px;margin-bottom:8px;flex-wrap:wrap;justify-content:center;'>"
|
| 1483 |
-
physics_html += f"<div style='background:white;padding:6px 12px;border-radius:8px;border:1px solid #ddd;text-align:center;'><div style='font-size:18px;font-weight:bold;color:#1565C0;'>{physics_summary.get('n_regions',0)}</div><div style='font-size:10px;color:#777;'>ู
ูุงุทู</div></div>"
|
| 1484 |
-
physics_html += f"<div style='background:white;padding:6px 12px;border-radius:8px;border:1px solid {uniform_clr};text-align:center;'><div style='font-size:14px;font-weight:bold;color:{uniform_clr};'>{uniform_txt}</div><div style='font-size:10px;color:#777;'>ุชุจุงูู: {density_var:.1f}</div></div>"
|
| 1485 |
-
physics_html += f"<div style='background:white;padding:6px 12px;border-radius:8px;border:1px solid #ddd;text-align:center;'><div style='font-size:18px;font-weight:bold;color:#E65100;'>{len(physics_anomalies)}</div><div style='font-size:10px;color:#777;'>ู
ุดุจููุฉ</div></div>"
|
| 1486 |
-
physics_html += f"<div style='background:white;padding:6px 12px;border-radius:8px;border:1px solid #ddd;text-align:center;'><div style='font-size:18px;font-weight:bold;color:#C62828;'>{len(pharma_suspects)}</div><div style='font-size:10px;color:#777;'>ุจุตู
ุฉ ุฃุฏููุฉ</div></div>"
|
| 1487 |
-
physics_html += f"<div style='background:white;padding:6px 12px;border-radius:8px;border:1px solid #ddd;text-align:center;'><div style='font-size:18px;font-weight:bold;color:#6A1B9A;'>{physics_summary.get('max_density_diff',0):.0f}</div><div style='font-size:10px;color:#777;'>ูุฑู ูุซุงูุฉ</div></div>"
|
| 1488 |
-
physics_html += "</div>"
|
| 1489 |
-
|
| 1490 |
-
# ุฌุฏูู ุงูู
ูุงุทู
|
| 1491 |
-
physics_html += "<table style='width:100%;border-collapse:collapse;font-size:11px;'>"
|
| 1492 |
-
physics_html += "<tr style='background:#37474F;color:white;'><th style='padding:5px;'>ุงูู
ูุทูุฉ</th><th style='padding:5px;'>ุงูู
ุงุฏุฉ ุงูู
ุญุชู
ูุฉ</th><th style='padding:5px;'>ุงููุซุงูุฉ</th><th style='padding:5px;'>ุงูุฏุงูู%</th><th style='padding:5px;'>Entropy</th><th style='padding:5px;'>ุงููุชูุฌุฉ</th></tr>"
|
| 1493 |
-
|
| 1494 |
-
for r in physics_regions:
|
| 1495 |
-
p = r['physics']
|
| 1496 |
-
is_pharma = r.get('is_pharma_suspect', False)
|
| 1497 |
-
is_anom = r.get('is_anomaly', False)
|
| 1498 |
-
|
| 1499 |
-
if is_pharma:
|
| 1500 |
-
row_bg = "#FFCDD2"
|
| 1501 |
-
status = f"โ {r['anomaly_score']}%"
|
| 1502 |
-
elif is_anom:
|
| 1503 |
-
row_bg = "#FFF3E0"
|
| 1504 |
-
status = f"โ ๏ธ {r['anomaly_score']}%"
|
| 1505 |
-
else:
|
| 1506 |
-
row_bg = "#E8F5E9"
|
| 1507 |
-
status = f"โ
{r['anomaly_score']}%"
|
| 1508 |
-
|
| 1509 |
-
physics_html += f"<tr style='background:{row_bg};border-bottom:1px solid #ddd;'>"
|
| 1510 |
-
physics_html += f"<td style='padding:4px 6px;font-weight:bold;'>๐ {r['position']}</td>"
|
| 1511 |
-
physics_html += f"<td style='padding:4px;color:{'#C62828' if is_pharma else '#333'};font-weight:{'bold' if is_pharma else 'normal'};'>{r['material_ar']}{' โ' if is_pharma else ''}</td>"
|
| 1512 |
-
physics_html += f"<td style='padding:4px;text-align:center;font-family:monospace;'>{p['mean']:.0f}</td>"
|
| 1513 |
-
physics_html += f"<td style='padding:4px;text-align:center;'>{p['dark_ratio']:.0%}</td>"
|
| 1514 |
-
physics_html += f"<td style='padding:4px;text-align:center;'>{p['entropy']:.1f}</td>"
|
| 1515 |
-
physics_html += f"<td style='padding:4px;text-align:center;font-weight:bold;'>{status}</td>"
|
| 1516 |
-
physics_html += "</tr>"
|
| 1517 |
-
|
| 1518 |
-
physics_html += "</table>"
|
| 1519 |
-
|
| 1520 |
-
# ุชูุงุตูู ุงูู
ูุงุทู ุงูู
ุดุจููุฉ
|
| 1521 |
-
if pharma_suspects or physics_anomalies:
|
| 1522 |
-
physics_html += f"<div style='margin-top:6px;padding:8px;background:white;border-radius:8px;border:1px solid {p_color};'>"
|
| 1523 |
-
physics_html += f"<div style='font-weight:bold;color:{p_color};margin-bottom:4px;font-size:12px;'>๐ ุชูุงุตูู ุงูุงุดุชุจุงู:</div>"
|
| 1524 |
-
for r in (pharma_suspects + [a for a in physics_anomalies if a not in pharma_suspects]):
|
| 1525 |
-
reasons_txt = " | ".join(r.get('reasons', []))
|
| 1526 |
-
icon = "โ" if r.get('is_pharma_suspect') else "โ ๏ธ"
|
| 1527 |
-
physics_html += f"<div style='font-size:11px;padding:2px 0;color:#333;'>{icon} <b>{r['position']}</b>: {reasons_txt}</div>"
|
| 1528 |
-
physics_html += "</div>"
|
| 1529 |
-
|
| 1530 |
-
physics_html += "</div>"
|
| 1531 |
-
|
| 1532 |
det_html = ""
|
| 1533 |
if detections:
|
| 1534 |
det_html = "<div style='margin-top:6px;display:flex;gap:6px;flex-wrap:wrap;'>"
|
|
@@ -1568,7 +1193,6 @@ def analyze_image(img, declared_text):
|
|
| 1568 |
{model_html}
|
| 1569 |
{ai_models_html}
|
| 1570 |
{region_html}
|
| 1571 |
-
{physics_html}
|
| 1572 |
{det_html}
|
| 1573 |
{anomaly_html}
|
| 1574 |
<div style='background:#fff;padding:14px;border-radius:10px;border:1px solid #e0e0e0;margin-top:8px;margin-bottom:8px;'>
|
|
@@ -1580,17 +1204,13 @@ def analyze_image(img, declared_text):
|
|
| 1580 |
<div style='background:#FFF8E1;padding:8px 12px;border-radius:8px;font-size:13px;color:#333;'>{ar_desc}</div>
|
| 1581 |
</div>
|
| 1582 |
</div>"""
|
| 1583 |
-
# โ
ุงุฎุชูุงุฑ ุงูุตูุฑุฉ ุงูู
ุฎุฑุฌุฉ โ ุงูุฃููููุฉ:
|
| 1584 |
-
if
|
| 1585 |
-
output_img =
|
| 1586 |
-
elif physics_anomalies and physics_img:
|
| 1587 |
-
output_img = physics_img # ๐ฌ ุชุญููู ููุฒูุงุฆู ู
ุดุจูู
|
| 1588 |
-
elif has_multi_cargo and region_img:
|
| 1589 |
-
output_img = region_img
|
| 1590 |
elif (suspicious or full_is_anom) and anomaly_img:
|
| 1591 |
output_img = anomaly_img
|
| 1592 |
elif region_img:
|
| 1593 |
-
output_img = region_img
|
| 1594 |
else:
|
| 1595 |
output_img = annotated_img
|
| 1596 |
return html, df, output_img
|
|
@@ -1850,7 +1470,7 @@ with gr.Blocks(title=f"SONAR-AI v{VERSION}") as app:
|
|
| 1850 |
d_refresh.click(refresh_dashboard, outputs=[d_stats, d_tbl])
|
| 1851 |
|
| 1852 |
with gr.Tab("๐ฌ ุชุญููู ุงูุตูุฑ"):
|
| 1853 |
-
gr.HTML("<div style='background:#E3F2FD;padding:10px 14px;border-radius:10px;margin-bottom:8px;border-right:4px solid #1565C0;'><b style='color:#1565C0;'>๐ท Classification + Detection +
|
| 1854 |
with gr.Row():
|
| 1855 |
with gr.Column(scale=1):
|
| 1856 |
a_img = gr.Image(label="๐ท ุตูุฑุฉ ุงูุฃุดุนุฉ", type="pil", height=200)
|
|
@@ -1875,13 +1495,11 @@ with gr.Blocks(title=f"SONAR-AI v{VERSION}") as app:
|
|
| 1875 |
btn_contrast = gr.Button("๐", elem_id="btn_contrast", size="sm", min_width=30)
|
| 1876 |
btn_bright = gr.Button("โ", elem_id="btn_bright", size="sm", min_width=30)
|
| 1877 |
a_dec = gr.Textbox(label="๐ ุงูุจุถุงุนุฉ ุงูู
ุตุฑูุญ ุจูุง", placeholder="SHOES + FABRIC + ...", lines=1)
|
|
|
|
| 1878 |
with gr.Column(scale=1):
|
| 1879 |
-
|
| 1880 |
-
|
| 1881 |
-
|
| 1882 |
-
# โ
ุงููุชุงุฆุฌ ุฃุณูู ุงูุฒุฑ ุจุนุฑุถ ูุงู
ู
|
| 1883 |
-
a_res = gr.HTML("<div style='text-align:center;padding:30px;color:#999;direction:rtl;'>๐ท ุงุฑูุน ุตูุฑุฉ ููุจุฏุก</div>")
|
| 1884 |
-
a_tbl = gr.Dataframe(label="๐ ุงูุฃุตูุงู + HS + ุงูุฑุณูู
", value=pd.DataFrame(), wrap=True)
|
| 1885 |
a_btn.click(analyze_image, inputs=[a_img, a_dec], outputs=[a_res, a_tbl, a_det], api_name="analyze")
|
| 1886 |
btn_gray.click(apply_grayscale, inputs=[a_img], outputs=[a_img])
|
| 1887 |
btn_inv.click(apply_invert, inputs=[a_img], outputs=[a_img])
|
|
|
|
| 624 |
draw.text((x+5, y+2), txt, fill=(255,255,255), font=font)
|
| 625 |
return img
|
| 626 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 627 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 628 |
# ๐ Region Classification โ ุชูุณูู
ุงูุญุงููุฉ ูุชุตููู ูู ุฌุฒุก
|
| 629 |
# โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
|
|
| 932 |
has_multi_cargo = len(region_categories) > 1
|
| 933 |
region_img = draw_region_boxes(img.copy(), region_results) if region_results else None
|
| 934 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 935 |
# โโโ Anomaly Detection โโโ
|
| 936 |
full_feat = extract_features_from_image(img)
|
| 937 |
full_anom_score, full_is_anom, full_anom_det = check_anomaly(full_feat)
|
|
|
|
| 1154 |
|
| 1155 |
region_html += "</table></div>"
|
| 1156 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1157 |
det_html = ""
|
| 1158 |
if detections:
|
| 1159 |
det_html = "<div style='margin-top:6px;display:flex;gap:6px;flex-wrap:wrap;'>"
|
|
|
|
| 1193 |
{model_html}
|
| 1194 |
{ai_models_html}
|
| 1195 |
{region_html}
|
|
|
|
| 1196 |
{det_html}
|
| 1197 |
{anomaly_html}
|
| 1198 |
<div style='background:#fff;padding:14px;border-radius:10px;border:1px solid #e0e0e0;margin-top:8px;margin-bottom:8px;'>
|
|
|
|
| 1204 |
<div style='background:#FFF8E1;padding:8px 12px;border-radius:8px;font-size:13px;color:#333;'>{ar_desc}</div>
|
| 1205 |
</div>
|
| 1206 |
</div>"""
|
| 1207 |
+
# โ
ุงุฎุชูุงุฑ ุงูุตูุฑุฉ ุงูู
ุฎุฑุฌุฉ โ ุงูุฃููููุฉ: ู
ูุงุทู > anomaly > detection
|
| 1208 |
+
if has_multi_cargo and region_img:
|
| 1209 |
+
output_img = region_img # โ
ุนุฑุถ ู
ุฑุจุนุงุช ุงูุฃุตูุงู ุงูู
ุชุนุฏุฏุฉ
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1210 |
elif (suspicious or full_is_anom) and anomaly_img:
|
| 1211 |
output_img = anomaly_img
|
| 1212 |
elif region_img:
|
| 1213 |
+
output_img = region_img # ุนุฑุถ ู
ุฑุจุนุงุช ุงูู
ูุงุทู ุญุชู ูู ุตูู ูุงุญุฏ
|
| 1214 |
else:
|
| 1215 |
output_img = annotated_img
|
| 1216 |
return html, df, output_img
|
|
|
|
| 1470 |
d_refresh.click(refresh_dashboard, outputs=[d_stats, d_tbl])
|
| 1471 |
|
| 1472 |
with gr.Tab("๐ฌ ุชุญููู ุงูุตูุฑ"):
|
| 1473 |
+
gr.HTML("<div style='background:#E3F2FD;padding:10px 14px;border-radius:10px;margin-bottom:8px;border-right:4px solid #1565C0;'><b style='color:#1565C0;'>๐ท Classification + Detection + HS Codes</b></div>")
|
| 1474 |
with gr.Row():
|
| 1475 |
with gr.Column(scale=1):
|
| 1476 |
a_img = gr.Image(label="๐ท ุตูุฑุฉ ุงูุฃุดุนุฉ", type="pil", height=200)
|
|
|
|
| 1495 |
btn_contrast = gr.Button("๐", elem_id="btn_contrast", size="sm", min_width=30)
|
| 1496 |
btn_bright = gr.Button("โ", elem_id="btn_bright", size="sm", min_width=30)
|
| 1497 |
a_dec = gr.Textbox(label="๐ ุงูุจุถุงุนุฉ ุงูู
ุตุฑูุญ ุจูุง", placeholder="SHOES + FABRIC + ...", lines=1)
|
| 1498 |
+
a_btn = gr.Button("๐ ุชุญููู ุดุงู
ู", variant="primary", size="lg")
|
| 1499 |
with gr.Column(scale=1):
|
| 1500 |
+
a_res = gr.HTML("<div style='text-align:center;padding:30px;color:#999;direction:rtl;'>๐ท ุงุฑูุน ุตูุฑุฉ ููุจุฏุก</div>")
|
| 1501 |
+
a_det = gr.Image(label="๐ Detection / Anomaly", type="pil", height=200)
|
| 1502 |
+
a_tbl = gr.Dataframe(label="๐ ุงูุฃุตูุงู + HS", value=pd.DataFrame(), wrap=True)
|
|
|
|
|
|
|
|
|
|
| 1503 |
a_btn.click(analyze_image, inputs=[a_img, a_dec], outputs=[a_res, a_tbl, a_det], api_name="analyze")
|
| 1504 |
btn_gray.click(apply_grayscale, inputs=[a_img], outputs=[a_img])
|
| 1505 |
btn_inv.click(apply_invert, inputs=[a_img], outputs=[a_img])
|