Spaces:
Running
on
Zero
Running
on
Zero
File size: 36,759 Bytes
3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 c0fe80d 3172319 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 |
import numpy as np
import cv2
from typing import Dict, Any, Optional
class LightingAnalyzer:
"""
分析圖像的光照條件,提供增強的室內or室外判斷和光照類型分類,並專注於光照分析。
"""
def __init__(self, config: Optional[Dict[str, Any]] = None):
"""
初始化光照分析器。
Args:
config: 可選的配置字典,用於自定義分析參數
"""
self.config = config or self._get_default_config()
def analyze(self, image):
"""
分析圖像的光照條件。
主要分析入口點,計算基本特徵,判斷室內/室外,確定光照條件。
Args:
image: 輸入圖像 (numpy array 或 PIL Image)
Returns:
Dict: 包含光照分析結果的字典
"""
try:
# 轉換圖像格式
if not isinstance(image, np.ndarray):
image_np = np.array(image)
else:
image_np = image.copy()
# 確保 RGB 格式
if image_np.shape[2] == 3 and isinstance(image_np, np.ndarray):
image_rgb = cv2.cvtColor(image_np, cv2.COLOR_BGR2RGB)
else:
image_rgb = image_np
# 計算基本特徵
features = self._compute_basic_features(image_rgb)
# 分析室內or室外
indoor_result = self._analyze_indoor_outdoor(features)
is_indoor = indoor_result["is_indoor"]
indoor_probability = indoor_result["indoor_probability"]
# 確定光照條件
lighting_conditions = self._determine_lighting_conditions(features, is_indoor)
# 整合結果
result = {
"time_of_day": lighting_conditions["time_of_day"],
"confidence": float(lighting_conditions["confidence"]),
"is_indoor": is_indoor,
"indoor_probability": float(indoor_probability),
"brightness": {
"average": float(features["avg_brightness"]),
"std_dev": float(features["brightness_std"]),
"dark_ratio": float(features["dark_pixel_ratio"])
},
"color_info": {
"blue_ratio": float(features["blue_ratio"]),
"yellow_orange_ratio": float(features["yellow_orange_ratio"]),
"gray_ratio": float(features["gray_ratio"]),
"avg_saturation": float(features["avg_saturation"]),
"sky_brightness": float(features["sky_brightness"]),
"color_atmosphere": features["color_atmosphere"],
"warm_ratio": float(features["warm_ratio"]),
"cool_ratio": float(features["cool_ratio"])
}
}
# 添加診斷信息
if self.config["include_diagnostics"]:
result["diagnostics"] = {
"feature_contributions": indoor_result.get("feature_contributions", {}),
"lighting_diagnostics": lighting_conditions.get("diagnostics", {})
}
return result
except Exception as e:
print(f"Error in lighting analysis: {str(e)}")
import traceback
traceback.print_exc()
return {
"time_of_day": "unknown",
"confidence": 0,
"error": str(e)
}
def _compute_basic_features(self, image_rgb):
"""
計算圖像的基本光照特徵(徹底優化版本)。
Args:
image_rgb: RGB 格式的圖像 (numpy array)
Returns:
Dict: 包含計算出的特徵值
"""
# 獲取圖像尺寸
height, width = image_rgb.shape[:2]
# 根據圖像大小自適應縮放因子
base_scale = 4
scale_factor = base_scale + min(8, max(0, int((height * width) / (1000 * 1000))))
# 創建縮小的圖像以加速處理
small_rgb = cv2.resize(image_rgb, (width//scale_factor, height//scale_factor))
# 一次性轉換所有顏色空間,避免重複計算
hsv_img = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2HSV)
gray_img = cv2.cvtColor(image_rgb, cv2.COLOR_RGB2GRAY)
small_gray = cv2.resize(gray_img, (width//scale_factor, height//scale_factor))
# 分離HSV通道
h_channel = hsv_img[:,:,0]
s_channel = hsv_img[:,:,1]
v_channel = hsv_img[:,:,2]
# 基本亮度特徵
avg_brightness = np.mean(v_channel)
brightness_std = np.std(v_channel)
dark_pixel_ratio = np.sum(v_channel < 50) / (height * width)
# 顏色特徵
yellow_orange_mask = ((h_channel >= 15) & (h_channel <= 40))
yellow_orange_ratio = np.sum(yellow_orange_mask) / (height * width)
blue_mask = ((h_channel >= 90) & (h_channel <= 130))
blue_ratio = np.sum(blue_mask) / (height * width)
# 特別檢查圖像上部區域,尋找藍天特徵
upper_region_h = h_channel[:height//4, :]
upper_region_s = s_channel[:height//4, :]
upper_region_v = v_channel[:height//4, :]
# 藍天通常具有高飽和度的藍色
sky_blue_mask = ((upper_region_h >= 90) & (upper_region_h <= 130) &
(upper_region_s > 70) & (upper_region_v > 150))
sky_blue_ratio = np.sum(sky_blue_mask) / max(1, upper_region_h.size)
gray_mask = (s_channel < 50) & (v_channel > 100)
gray_ratio = np.sum(gray_mask) / (height * width)
avg_saturation = np.mean(s_channel)
# 天空亮度
upper_half = v_channel[:height//2, :]
sky_brightness = np.mean(upper_half)
# 色調分析
warm_colors = ((h_channel >= 0) & (h_channel <= 60)) | (h_channel >= 300)
warm_ratio = np.sum(warm_colors) / (height * width)
cool_colors = (h_channel >= 180) & (h_channel <= 270)
cool_ratio = np.sum(cool_colors) / (height * width)
# 確定色彩氛圍
if warm_ratio > 0.4:
color_atmosphere = "warm"
elif cool_ratio > 0.4:
color_atmosphere = "cool"
else:
color_atmosphere = "neutral"
# 只在縮小的圖像上計算梯度,大幅提高效能
gx = cv2.Sobel(small_gray, cv2.CV_32F, 1, 0, ksize=3)
gy = cv2.Sobel(small_gray, cv2.CV_32F, 0, 1, ksize=3)
vertical_strength = np.mean(np.abs(gy))
horizontal_strength = np.mean(np.abs(gx))
gradient_ratio = vertical_strength / max(horizontal_strength, 1e-5)
# -- 亮度均勻性 --
brightness_uniformity = 1 - min(1, brightness_std / max(avg_brightness, 1e-5))
# -- 高效的天花板分析 --
# 使用更大的下採樣率分析頂部區域
top_scale = scale_factor * 2 # 更積極的下採樣
top_region = v_channel[:height//4:top_scale, ::top_scale]
top_region_std = np.std(top_region)
ceiling_uniformity = 1.0 - min(1, top_region_std / max(np.mean(top_region), 1e-5))
# 使用更簡單的方法檢測上部水平線
top_gradients = np.abs(gy[:small_gray.shape[0]//4, :])
horizontal_lines_strength = np.mean(top_gradients)
# 標準化
horizontal_line_ratio = min(1, horizontal_lines_strength / 40)
# 極簡的亮點檢測
sampled_v = v_channel[::scale_factor*2, ::scale_factor*2]
light_threshold = min(220, avg_brightness + 2*brightness_std)
is_bright = sampled_v > light_threshold
bright_spot_count = np.sum(is_bright)
# 圓形光源分析的簡化替代方法
circular_light_score = 0
indoor_light_score = 0
light_distribution_uniformity = 0.5
# 只有當檢測到亮點,且不是大量亮點時(可能是室外光反射)才進行光源分析
if 1 < bright_spot_count < 20:
# 簡單統計亮點分布
bright_y, bright_x = np.where(is_bright)
if len(bright_y) > 1:
# 檢查亮點是否成組出現 - 室內照明常見模式
mean_x = np.mean(bright_x)
mean_y = np.mean(bright_y)
dist_from_center = np.sqrt((bright_x - mean_x)**2 + (bright_y - mean_y)**2)
# 如果亮點分布較集中,可能是燈具
if np.std(dist_from_center) < np.mean(dist_from_center):
circular_light_score = min(3, len(bright_y) // 2)
light_distribution_uniformity = 0.7
# 評估亮點是否位於上部區域,常見於室內頂燈
if np.mean(bright_y) < sampled_v.shape[0] / 2:
indoor_light_score = 0.6
else:
indoor_light_score = 0.3
# 使用邊緣區域梯度來快速估計邊界
edge_scale = scale_factor * 2
# 只採樣圖像邊緣部分進行分析
left_edge = small_gray[:, :small_gray.shape[1]//6]
right_edge = small_gray[:, 5*small_gray.shape[1]//6:]
top_edge = small_gray[:small_gray.shape[0]//6, :]
# 計算每個邊緣區域的梯度強度
left_gradient = np.mean(np.abs(cv2.Sobel(left_edge, cv2.CV_32F, 1, 0, ksize=3)))
right_gradient = np.mean(np.abs(cv2.Sobel(right_edge, cv2.CV_32F, 1, 0, ksize=3)))
top_gradient = np.mean(np.abs(cv2.Sobel(top_edge, cv2.CV_32F, 0, 1, ksize=3)))
# 標準化
left_edge_density = min(1.0, left_gradient / 50)
right_edge_density = min(1.0, right_gradient / 50)
top_edge_density = min(1.0, top_gradient / 50)
# 封閉環境通常在圖像邊緣有較強的梯度
boundary_edge_score = (left_edge_density + right_edge_density + top_edge_density) / 3
# 簡單估計整體邊緣密度
edges_density = min(1, (np.mean(np.abs(gx)) + np.mean(np.abs(gy))) / 100)
street_line_score = 0
# 檢查下半部分是否有強烈的垂直線條
bottom_half = small_gray[small_gray.shape[0]//2:, :]
bottom_vert_gradient = cv2.Sobel(bottom_half, cv2.CV_32F, 0, 1, ksize=3)
strong_vert_lines = np.abs(bottom_vert_gradient) > 50
if np.sum(strong_vert_lines) > (bottom_half.size * 0.05): # 如果超過5%的像素是強垂直線
street_line_score = 0.7
# 整合所有特徵
features = {
# 基本亮度和顏色特徵
"avg_brightness": avg_brightness,
"brightness_std": brightness_std,
"dark_pixel_ratio": dark_pixel_ratio,
"yellow_orange_ratio": yellow_orange_ratio,
"blue_ratio": blue_ratio,
"sky_blue_ratio": sky_blue_ratio,
"gray_ratio": gray_ratio,
"avg_saturation": avg_saturation,
"sky_brightness": sky_brightness,
"color_atmosphere": color_atmosphere,
"warm_ratio": warm_ratio,
"cool_ratio": cool_ratio,
# 結構特徵
"gradient_ratio": gradient_ratio,
"brightness_uniformity": brightness_uniformity,
"bright_spot_count": bright_spot_count,
"vertical_strength": vertical_strength,
"horizontal_strength": horizontal_strength,
# 室內/室外判斷特徵
"ceiling_uniformity": ceiling_uniformity,
"horizontal_line_ratio": horizontal_line_ratio,
"indoor_light_score": indoor_light_score,
"circular_light_count": circular_light_score,
"light_distribution_uniformity": light_distribution_uniformity,
"boundary_edge_score": boundary_edge_score,
"top_region_std": top_region_std,
"edges_density": edges_density,
# 新增:室外特定特徵
"street_line_score": street_line_score
}
return features
def _analyze_indoor_outdoor(self, features):
"""
使用多特徵融合進行室內/室外判斷
Args:
features: 特徵字典
Returns:
Dict: 室內/室外判斷結果
"""
# 獲取配置中的特徵權重
weights = self.config["indoor_outdoor_weights"]
# 初始概率值 - 開始時中性評估
indoor_score = 0
feature_contributions = {}
diagnostics = {}
# 1. 藍色區域(天空)特徵 - 藍色區域多通常表示室外
if features.get("blue_ratio", 0) > 0.2:
# 檢查是否有室內指標,如果有明顯的室內特徵,則減少藍色的負面影響
if (features.get("ceiling_uniformity", 0) > 0.5 or
features.get("boundary_edge_score", 0) > 0.3 or
features.get("indoor_light_score", 0) > 0.2 or
features.get("bright_spot_count", 0) > 0):
blue_score = -weights["blue_ratio"] * features["blue_ratio"] * 8
else:
blue_score = -weights["blue_ratio"] * features["blue_ratio"] * 15
else:
blue_score = -weights["blue_ratio"] * features["blue_ratio"] * 15
indoor_score += blue_score
feature_contributions["blue_ratio"] = blue_score
# 判斷視角 - 如果上部有藍天而上下亮度差異大,可能是仰視室外建築
if (features.get("sky_blue_ratio", 0) > 0.01 and
features["sky_brightness"] > features["avg_brightness"] * 1.1):
viewpoint_outdoor_score = -1.8 # 強烈的室外指標
indoor_score += viewpoint_outdoor_score
feature_contributions["outdoor_viewpoint"] = viewpoint_outdoor_score
# 2. 亮度均勻性特徵 - 室內通常光照更均勻
uniformity_score = weights["brightness_uniformity"] * features["brightness_uniformity"]
indoor_score += uniformity_score
feature_contributions["brightness_uniformity"] = uniformity_score
# 3. 天花板特徵 - 強化天花板檢測的權重
ceiling_contribution = 0
if "ceiling_uniformity" in features:
ceiling_uniformity = features["ceiling_uniformity"]
horizontal_line_ratio = features.get("horizontal_line_ratio", 0)
# 增強天花板檢測的影響
if ceiling_uniformity > 0.5:
ceiling_weight = 3
ceiling_contribution = weights.get("ceiling_features", 1.5) * ceiling_weight
if horizontal_line_ratio > 0.2: # 如果有水平線條,進一步增強
ceiling_contribution *= 1.5
elif ceiling_uniformity > 0.4:
ceiling_contribution = weights.get("ceiling_features", 1.5) * 1.2
indoor_score += ceiling_contribution
feature_contributions["ceiling_features"] = ceiling_contribution
# 4. 強化吊燈的檢測
light_contribution = 0
if "indoor_light_score" in features:
indoor_light_score = features["indoor_light_score"]
circular_light_count = features.get("circular_light_count", 0)
# 加強對特定類型光源的檢測
if circular_light_count >= 1: # 即便只有一個圓形光源也很可能是室內
light_contribution = weights.get("light_features", 1.2) * 2.0
elif indoor_light_score > 0.3:
light_contribution = weights.get("light_features", 1.2) * 1.0
indoor_score += light_contribution
feature_contributions["light_features"] = light_contribution
# 5. 環境封閉度特徵
boundary_contribution = 0
if "boundary_edge_score" in features:
boundary_edge_score = features["boundary_edge_score"]
edges_density = features.get("edges_density", 0)
# 高邊界評分暗示封閉環境(室內)
if boundary_edge_score > 0.3:
boundary_contribution = weights.get("boundary_features", 1.2) * 2
elif boundary_edge_score > 0.2:
boundary_contribution = weights.get("boundary_features", 1.2) * 1.2
indoor_score += boundary_contribution
feature_contributions["boundary_features"] = boundary_contribution
if (features.get("edges_density", 0) > 0.2 and
features.get("bright_spot_count", 0) > 5 and
features.get("vertical_strength", 0) > features.get("horizontal_strength", 0) * 1.5):
# 商業街道特徵:高邊緣密度 + 多亮點 + 強垂直特徵
street_feature_score = -weights.get("street_features", 1.2) * 1.5
indoor_score += street_feature_score
feature_contributions["street_features"] = street_feature_score
# 添加對亞洲商業街道的專門檢測
if (features.get("edges_density", 0) > 0.25 and # 高邊緣密度
features.get("vertical_strength", 0) > features.get("horizontal_strength", 0) * 1.8 and # 更強的垂直結構
features.get("brightness_uniformity", 0) < 0.6): # 較低的亮度均勻性(招牌、燈光等造成)
asian_street_score = -2.2 # 非常強的室外代表性特徵
indoor_score += asian_street_score
feature_contributions["asian_commercial_street"] = asian_street_score
# 6. 垂直/水平梯度比率
gradient_contribution = 0
if features["gradient_ratio"] > 2.0:
combined_uniformity = (features["brightness_uniformity"] +
features.get("ceiling_uniformity", 0)) / 2
if combined_uniformity > 0.5:
gradient_contribution = weights["gradient_ratio"] * 0.7
else:
gradient_contribution = -weights["gradient_ratio"] * 0.3
indoor_score += gradient_contribution
feature_contributions["gradient_ratio"] = gradient_contribution
# 7. 亮點檢測(光源)
bright_spot_contribution = 0
bright_spot_count = features["bright_spot_count"]
circular_light_count = features.get("circular_light_count", 0)
# 調整亮點分析邏輯
if circular_light_count >= 1: # 即使只有一個圓形光源
bright_spot_contribution = weights["bright_spots"] * 1.5
elif bright_spot_count < 5: # 適當放寬閾值
bright_spot_contribution = weights["bright_spots"] * 0.5
elif bright_spot_count > 15: # 大量亮點比較有可能為室外
bright_spot_contribution = -weights["bright_spots"] * 0.4
indoor_score += bright_spot_contribution
feature_contributions["bright_spots"] = bright_spot_contribution
# 8. 色調分析
yellow_contribution = 0
if features["avg_brightness"] < 150 and features["yellow_orange_ratio"] > 0.15:
if features.get("indoor_light_score", 0) > 0.2:
yellow_contribution = weights["color_tone"] * 0.8
else:
yellow_contribution = weights["color_tone"] * 0.5
indoor_score += yellow_contribution
feature_contributions["yellow_tone"] = yellow_contribution
if features.get("blue_ratio", 0) > 0.7:
# 檢查是否有室內指標,如果有明顯的室內特徵,則減少藍色的負面影響
if (features.get("ceiling_uniformity", 0) > 0.6 or
features.get("boundary_edge_score", 0) > 0.3 or
features.get("indoor_light_score", 0) > 0):
blue_score = -weights["blue_ratio"] * features["blue_ratio"] * 10
else:
blue_score = -weights["blue_ratio"] * features["blue_ratio"] * 18
else:
blue_score = -weights["blue_ratio"] * features["blue_ratio"] * 18
# 9. 上半部與下半部亮度對比
sky_contribution = 0
if features["sky_brightness"] > features["avg_brightness"] * 1.3:
if features["blue_ratio"] > 0.15:
sky_contribution = -weights["sky_brightness"] * 0.9
else:
sky_contribution = -weights["sky_brightness"] * 0.6
indoor_score += sky_contribution
feature_contributions["sky_brightness"] = sky_contribution
# 加入額外的餐廳特徵檢測邏輯
dining_feature_contribution = 0
# 檢測中央懸掛式燈具,有懸掛燈代表有天花板,就代表是室內
if circular_light_count >= 1 and features.get("light_distribution_uniformity", 0) > 0.4:
dining_feature_contribution = 1.5
indoor_score += dining_feature_contribution
feature_contributions["dining_features"] = dining_feature_contribution
# 10. 增強的藍天的檢測,即便是小面積的藍天也是很強的室外指標
sky_contribution = 0
if "sky_blue_ratio" in features:
# 只有當藍色區域集中在上部且亮度高時,才認為是藍天
if features["sky_blue_ratio"] > 0.01 and features["sky_brightness"] > features.get("avg_brightness", 0) * 1.2:
sky_outdoor_score = -2.5 * features["sky_blue_ratio"] * weights.get("blue_ratio", 1.2)
indoor_score += sky_outdoor_score
feature_contributions["sky_blue_detection"] = sky_outdoor_score
asian_street_indicators = 0
# 1: 高垂直結構強度
vertical_ratio = features.get("vertical_strength", 0) / max(features.get("horizontal_strength", 1e-5), 1e-5)
if vertical_ratio > 1.8:
asian_street_indicators += 1
# 2: 高邊緣密度 + 路面標記特徵
if features.get("edges_density", 0) > 0.25 and features.get("street_line_score", 0) > 0.2:
asian_street_indicators += 2
# 3: 多個亮點 + 亮度不均勻
if features.get("bright_spot_count", 0) > 5 and features.get("brightness_uniformity", 0) < 0.6:
asian_street_indicators += 1
# 4: 藍色區域小(天空被高樓遮擋)但亮度高
if features.get("blue_ratio", 0) < 0.1 and features.get("sky_brightness", 0) > features.get("avg_brightness", 0) * 1.1:
asian_street_indicators += 1
# 如果滿足至少 3 個指標,調整權重變成偏向室外的判斷
if asian_street_indicators >= 3:
# 記錄檢測到的模式
feature_contributions["asian_street_pattern"] = -2.5
indoor_score += -2.5 # 明顯向室外傾斜
# 降低室內指標的權重
if "boundary_features" in feature_contributions:
adjusted_contribution = feature_contributions["boundary_features"] * 0.4
indoor_score -= (feature_contributions["boundary_features"] - adjusted_contribution)
feature_contributions["boundary_features"] = adjusted_contribution
if "ceiling_features" in feature_contributions:
adjusted_contribution = feature_contributions["ceiling_features"] * 0.3
indoor_score -= (feature_contributions["ceiling_features"] - adjusted_contribution)
feature_contributions["ceiling_features"] = adjusted_contribution
# 添加信息到診斷數據
diagnostics["asian_street_detected"] = True
diagnostics["asian_street_indicators"] = asian_street_indicators
bedroom_indicators = 0
# 1: 窗戶和牆壁形成的直角
if features.get("brightness_uniformity", 0) > 0.6 and features.get("boundary_edge_score", 0) > 0.3:
bedroom_indicators += 1.5 # 增加權重
# 2: 天花板和光源
if features.get("ceiling_uniformity", 0) > 0.5 and features.get("bright_spot_count", 0) > 0:
bedroom_indicators += 2.5
# 3: 良好對比度的牆壁顏色,適合臥房還有客廳
if features.get("brightness_uniformity", 0) > 0.6 and features.get("avg_saturation", 0) < 100:
bedroom_indicators += 1.5
# 特殊的檢測 4: 檢測窗戶
if features.get("boundary_edge_score", 0) > 0.25 and features.get("brightness_std", 0) > 40:
bedroom_indicators += 1.5
# 如果滿足足夠的家居指標,提高多點室內判斷分數
if bedroom_indicators >= 3:
# 增加家居環境評分
home_env_score = 3
indoor_score += home_env_score
feature_contributions["home_environment_pattern"] = home_env_score
elif bedroom_indicators >= 2:
# 適度增加家居環境評分
home_env_score = 2
indoor_score += home_env_score
feature_contributions["home_environment_pattern"] = home_env_score
# 根據總分轉換為概率(使用sigmoid函數)
indoor_probability = 1 / (1 + np.exp(-indoor_score * 0.22))
# 判斷結果
is_indoor = indoor_probability > 0.5
return {
"is_indoor": is_indoor,
"indoor_probability": indoor_probability,
"indoor_score": indoor_score,
"feature_contributions": feature_contributions,
"diagnostics": diagnostics
}
def _determine_lighting_conditions(self, features, is_indoor):
"""
基於特徵和室內/室外判斷確定光照條件。
Args:
features: 特徵字典
is_indoor: 是否是室內環境
Returns:
Dict: 光照條件分析結果
"""
# 初始化
time_of_day = "unknown"
confidence = 0.5
diagnostics = {}
avg_brightness = features["avg_brightness"]
dark_pixel_ratio = features["dark_pixel_ratio"]
yellow_orange_ratio = features["yellow_orange_ratio"]
blue_ratio = features["blue_ratio"]
gray_ratio = features["gray_ratio"]
# 基於室內/室外分別判斷
if is_indoor:
# 計算室內住宅自然光指標
natural_window_light = 0
# 檢查窗戶特徵和光線特性
if (features.get("blue_ratio", 0) > 0.1 and
features.get("sky_brightness", 0) > avg_brightness * 1.1):
natural_window_light += 1
# 檢查均勻柔和的光線分布
if (features.get("brightness_uniformity", 0) > 0.65 and
features.get("brightness_std", 0) < 70):
natural_window_light += 1
# 檢查暖色調比例
if features.get("warm_ratio", 0) > 0.2:
natural_window_light += 1
# 家居環境指標
home_env_score = features.get("home_environment_pattern", 0)
if home_env_score > 1.5:
natural_window_light += 1
# 1. 室內明亮環境,可能有窗戶自然光
if avg_brightness > 130:
# 檢測自然光住宅空間 - 新增類型!
if natural_window_light >= 2 and home_env_score > 1.5:
time_of_day = "indoor_residential_natural" # 家裡的自然光類型
confidence = 0.8
diagnostics["reason"] = "Bright residential space with natural window lighting"
# 檢查窗戶特徵 - 如果有明亮的窗戶且色調為藍
elif features.get("blue_ratio", 0) > 0.1 and features.get("sky_brightness", 0) > 150:
time_of_day = "indoor_bright"
confidence = 0.8
diagnostics["reason"] = "Bright indoor scene with window light"
else:
time_of_day = "indoor_bright"
confidence = 0.75
diagnostics["reason"] = "High brightness in indoor environment"
# 2. 室內中等亮度環境
elif avg_brightness > 100:
time_of_day = "indoor_moderate"
confidence = 0.7
diagnostics["reason"] = "Moderate brightness in indoor environment"
# 3. 室內低光照環境
else:
time_of_day = "indoor_dim"
confidence = 0.65 + dark_pixel_ratio / 3
diagnostics["reason"] = "Low brightness in indoor environment"
# 1. 檢測設計師風格住宅,可以偵測到比較多種類的狀況
designer_residential_score = 0
# 檢測特色燈具
if (features.get("circular_light_count", 0) > 0 or features.get("bright_spot_count", 0) > 2):
designer_residential_score += 1
# 檢測高品質均勻照明
if features.get("brightness_uniformity", 0) > 0.7:
designer_residential_score += 1
# 檢測溫暖色調
if features.get("warm_ratio", 0) > 0.3:
designer_residential_score += 1
# 檢測家居環境特徵
if home_env_score > 1.5:
designer_residential_score += 1
if designer_residential_score >= 3 and home_env_score > 1.5:
time_of_day = "indoor_designer_residential"
confidence = 0.85
diagnostics["special_case"] = "Designer residential lighting with decorative elements"
# 2. 檢測餐廳/酒吧場景
elif avg_brightness < 150 and yellow_orange_ratio > 0.2:
if features["warm_ratio"] > 0.4:
time_of_day = "indoor_restaurant"
confidence = 0.65 + yellow_orange_ratio / 4
diagnostics["special_case"] = "Warm, yellow-orange lighting suggests restaurant/bar setting"
# 3. 檢測商業照明空間
elif avg_brightness > 120 and features["bright_spot_count"] > 4:
# 增加商業照明判別的精確度
commercial_score = 0
# 多個亮點
commercial_score += min(1.0, features["bright_spot_count"] * 0.05)
# 不太可能是住宅的指標
if features.get("home_environment_pattern", 0) < 1.5:
commercial_score += 0.5
# 整體照明結構化布局
if features.get("light_distribution_uniformity", 0) > 0.6:
commercial_score += 0.5
if commercial_score > 0.6 and designer_residential_score < 3:
time_of_day = "indoor_commercial"
confidence = 0.7 + commercial_score / 5
diagnostics["special_case"] = "Multiple structured light sources suggest commercial lighting"
else:
# 室外場景判斷保持不變
if avg_brightness < 90: # 降低夜間判斷的亮度閾值
# 檢測是否有車燈/街燈
has_lights = features["bright_spot_count"] > 3
if has_lights:
time_of_day = "night"
confidence = 0.8 + dark_pixel_ratio / 5
diagnostics["reason"] = "Low brightness with light sources detected"
# 檢查是否是霓虹燈場景
if yellow_orange_ratio > 0.15 and features["bright_spot_count"] > 5:
time_of_day = "neon_night"
confidence = 0.75 + yellow_orange_ratio / 3
diagnostics["special_case"] = "Multiple colorful light sources suggest neon lighting"
else:
time_of_day = "night"
confidence = 0.7 + dark_pixel_ratio / 3
diagnostics["reason"] = "Low brightness outdoor scene"
elif avg_brightness < 130 and yellow_orange_ratio > 0.2:
time_of_day = "sunset/sunrise"
confidence = 0.7 + yellow_orange_ratio / 3
diagnostics["reason"] = "Moderate brightness with yellow-orange tones"
elif avg_brightness > 150 and blue_ratio > 0.15:
time_of_day = "day_clear"
confidence = 0.7 + blue_ratio / 3
diagnostics["reason"] = "High brightness with blue tones (likely sky)"
elif avg_brightness > 130:
time_of_day = "day_cloudy"
confidence = 0.7 + gray_ratio / 3
diagnostics["reason"] = "Good brightness with higher gray tones"
else:
# 默認判斷
if yellow_orange_ratio > gray_ratio:
time_of_day = "sunset/sunrise"
confidence = 0.6 + yellow_orange_ratio / 3
diagnostics["reason"] = "Yellow-orange tones dominant"
else:
time_of_day = "day_cloudy"
confidence = 0.6 + gray_ratio / 3
diagnostics["reason"] = "Gray tones dominant"
# 檢查是否是特殊室外場景(如體育場)
if avg_brightness > 120 and features["brightness_uniformity"] > 0.8:
# 高亮度且非常均勻的光照可能是體育場燈光
time_of_day = "stadium_lighting"
confidence = 0.7
diagnostics["special_case"] = "Uniform bright lighting suggests stadium/sports lighting"
# 檢查是否是混合光照(如室內/室外過渡區)
if 100 < avg_brightness < 150 and 0.1 < blue_ratio < 0.2:
if features["gradient_ratio"] > 1.5:
time_of_day = "mixed_lighting"
confidence = 0.65
diagnostics["special_case"] = "Features suggest indoor-outdoor transition area"
# 確保信心值在 0-1 範圍內
confidence = min(0.95, max(0.5, confidence))
if time_of_day in ["indoor_residential_natural", "indoor_designer_residential"] and hasattr(self, "config"):
# 確保 LIGHTING_CONDITIONS 中有這些新類型的描述
if time_of_day == "indoor_residential_natural":
lightingType = {
"template_modifiers": {
"indoor_residential_natural": "naturally-lit residential"
},
"time_descriptions": {
"indoor_residential_natural": {
"general": "The scene is captured in a residential space with ample natural light from windows.",
"bright": "The residential space is brightly lit with natural daylight streaming through windows.",
"medium": "The home environment has good natural lighting providing a warm, inviting atmosphere.",
"dim": "The living space has soft natural light filtering through windows or openings."
}
}
}
elif time_of_day == "indoor_designer_residential":
lightingType = {
"template_modifiers": {
"indoor_designer_residential": "designer-lit residential"
},
"time_descriptions": {
"indoor_designer_residential": {
"general": "The scene is captured in a residential space with carefully designed lighting elements.",
"bright": "The home features professionally designed lighting with decorative fixtures creating a bright atmosphere.",
"medium": "The residential interior showcases curated lighting design balancing form and function.",
"dim": "The living space has thoughtfully placed designer lighting creating an intimate ambiance."
}
}
}
return {
"time_of_day": time_of_day,
"confidence": confidence,
"diagnostics": diagnostics
}
def _get_default_config(self):
"""
返回優化版本的默認配置參數。
"""
return {
"indoor_outdoor_weights": {
"blue_ratio": 0.6,
"brightness_uniformity": 1.2,
"gradient_ratio": 0.7,
"bright_spots": 0.8,
"color_tone": 0.5,
"sky_brightness": 0.9,
"brightness_variation": 0.7,
"ceiling_features": 1.5,
"light_features": 1.1,
"boundary_features": 2.8,
"street_features": 2,
"building_features": 1.6
},
"include_diagnostics": True
}
|