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
        }