File size: 11,558 Bytes
12d9ea9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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

import logging
import traceback
from typing import Dict, List, Any, Optional

logger = logging.getLogger(__name__)

class FunctionalZoneDetector:
    """
    負責基於物件關聯性的功能區域識別
    處理物件組合分析和描述性區域命名
    """

    def __init__(self):
        """初始化功能區域檢測器"""
        try:
            logger.info("FunctionalZoneDetector initialized successfully")
        except Exception as e:
            logger.error(f"Failed to initialize FunctionalZoneDetector: {str(e)}")
            logger.error(traceback.format_exc())
            raise

    def identify_primary_functional_area(self, detected_objects: List[Dict]) -> Dict:
        """
        識別主要功能區域,基於最強的物件關聯性組合
        採用通用邏輯處理各種室內場景

        Args:
            detected_objects: 檢測到的物件列表

        Returns:
            主要功能區域字典或None
        """
        try:
            # 用餐區域檢測(桌椅組合)
            dining_area = self.detect_functional_combination(
                detected_objects,
                primary_objects=[60],  # dining table
                supporting_objects=[56, 40, 41, 42, 43],  # chair, wine glass, cup, fork, knife
                min_supporting=2,
                description_template="Dining area with table and seating arrangement"
            )
            if dining_area:
                return dining_area

            # 休息區域檢測(沙發電視組合或床)
            seating_area = self.detect_functional_combination(
                detected_objects,
                primary_objects=[57, 59],  # sofa, bed
                supporting_objects=[62, 58, 56],  # tv, potted plant, chair
                min_supporting=1,
                description_template="Seating and relaxation area"
            )
            if seating_area:
                return seating_area

            # 工作區域檢測(電子設備與家具組合)
            work_area = self.detect_functional_combination(
                detected_objects,
                primary_objects=[63, 66],  # laptop, keyboard
                supporting_objects=[60, 56, 64],  # dining table, chair, mouse
                min_supporting=2,
                description_template="Workspace area with electronics and furniture"
            )
            if work_area:
                return work_area

            return None

        except Exception as e:
            logger.error(f"Error identifying primary functional area: {str(e)}")
            logger.error(traceback.format_exc())
            return None

    def identify_secondary_functional_area(self, detected_objects: List[Dict], existing_zones: Dict) -> Dict:
        """
        識別次要功能區域,避免與主要區域重疊

        Args:
            detected_objects: 檢測到的物件列表
            existing_zones: 已存在的功能區域

        Returns:
            次要功能區域字典或None
        """
        try:
            # 獲取已使用的區域
            used_regions = set(zone.get("region") for zone in existing_zones.values())

            # 裝飾區域檢測(植物集中區域)
            decorative_area = self.detect_functional_combination(
                detected_objects,
                primary_objects=[58],  # potted plant
                supporting_objects=[75],  # vase
                min_supporting=0,
                min_primary=3,  # 至少需要3個植物
                description_template="Decorative area with plants and ornamental items",
                exclude_regions=used_regions
            )
            if decorative_area:
                return decorative_area

            # 儲存區域檢測(廚房電器組合)
            storage_area = self.detect_functional_combination(
                detected_objects,
                primary_objects=[72, 68, 69],  # refrigerator, microwave, oven
                supporting_objects=[71],  # sink
                min_supporting=0,
                min_primary=2,
                description_template="Kitchen appliance and storage area",
                exclude_regions=used_regions
            )
            if storage_area:
                return storage_area

            return None

        except Exception as e:
            logger.error(f"Error identifying secondary functional area: {str(e)}")
            logger.error(traceback.format_exc())
            return None

    def detect_functional_combination(self, detected_objects: List[Dict], primary_objects: List[int],
                                    supporting_objects: List[int], min_supporting: int,
                                    description_template: str, min_primary: int = 1,
                                    exclude_regions: set = None) -> Dict:
        """
        通用的功能組合檢測方法
        基於主要物件和支持物件的組合判斷功能區域

        Args:
            detected_objects: 檢測到的物件列表
            primary_objects: 主要物件的class_id列表
            supporting_objects: 支持物件的class_id列表
            min_supporting: 最少需要的支持物件數量
            description_template: 描述模板
            min_primary: 最少需要的主要物件數量
            exclude_regions: 需要排除的區域集合

        Returns:
            功能區域資訊字典,如果不符合條件則返回None
        """
        try:
            if exclude_regions is None:
                exclude_regions = set()

            # 收集主要物件
            primary_objs = [obj for obj in detected_objects
                        if obj.get("class_id") in primary_objects and obj.get("confidence", 0) >= 0.4]

            # 收集支持物件
            supporting_objs = [obj for obj in detected_objects
                            if obj.get("class_id") in supporting_objects and obj.get("confidence", 0) >= 0.4]

            # 檢查是否滿足最少數量要求
            if len(primary_objs) < min_primary or len(supporting_objs) < min_supporting:
                return None

            # 按區域組織物件
            region_combinations = {}
            all_relevant_objs = primary_objs + supporting_objs

            for obj in all_relevant_objs:
                region = obj.get("region")

                # 排除指定區域
                if region in exclude_regions:
                    continue

                if region not in region_combinations:
                    region_combinations[region] = {"primary": [], "supporting": [], "all": []}

                region_combinations[region]["all"].append(obj)

                if obj.get("class_id") in primary_objects:
                    region_combinations[region]["primary"].append(obj)
                else:
                    region_combinations[region]["supporting"].append(obj)

            # 找到最佳區域組合
            best_region = None
            best_score = 0

            for region, objs in region_combinations.items():
                # 計算該區域的評分
                primary_count = len(objs["primary"])
                supporting_count = len(objs["supporting"])

                # 必須滿足最低要求
                if primary_count < min_primary or supporting_count < min_supporting:
                    continue

                # 計算組合評分(主要物件權重較高)
                score = primary_count * 2 + supporting_count

                if score > best_score:
                    best_score = score
                    best_region = region

            if best_region is None:
                return None

            best_combination = region_combinations[best_region]
            all_objects = [obj["class_name"] for obj in best_combination["all"]]

            return {
                "region": best_region,
                "objects": all_objects,
                "description": description_template
            }

        except Exception as e:
            logger.error(f"Error detecting functional combination: {str(e)}")
            logger.error(traceback.format_exc())
            return None

    def generate_descriptive_zone_key_from_data(self, zone_data: Dict, priority_level: str) -> str:
        """
        基於區域與物品名產生一個比較有描述性的區域

        Args:
            zone_data: 區域數據字典
            priority_level: 優先級別(primary/secondary)

        Returns:
            str: 描述性區域鍵名
        """
        try:
            objects = zone_data.get("objects", [])
            region = zone_data.get("region", "")
            description = zone_data.get("description", "")

            # 基於物件內容確定功能類型
            if any("dining" in obj.lower() or "table" in obj.lower() for obj in objects):
                base_name = "dining area"
            elif any("chair" in obj.lower() or "sofa" in obj.lower() for obj in objects):
                base_name = "seating area"
            elif any("bed" in obj.lower() for obj in objects):
                base_name = "sleeping area"
            elif any("laptop" in obj.lower() or "keyboard" in obj.lower() for obj in objects):
                base_name = "workspace area"
            elif any("plant" in obj.lower() or "vase" in obj.lower() for obj in objects):
                base_name = "decorative area"
            elif any("refrigerator" in obj.lower() or "microwave" in obj.lower() for obj in objects):
                base_name = "kitchen area"
            else:
                # 基於描述內容推斷
                if "dining" in description.lower():
                    base_name = "dining area"
                elif "seating" in description.lower() or "relaxation" in description.lower():
                    base_name = "seating area"
                elif "work" in description.lower():
                    base_name = "workspace area"
                elif "decorative" in description.lower():
                    base_name = "decorative area"
                else:
                    base_name = "functional area"

            # 為次要區域添加位置標識以區分
            if priority_level == "secondary" and region:
                spatial_context = self.get_spatial_context_description(region)
                if spatial_context:
                    return f"{spatial_context} {base_name}"

            return base_name

        except Exception as e:
            logger.warning(f"Error generating descriptive zone key: {str(e)}")
            return "activity area"

    def get_spatial_context_description(self, region: str) -> str:
        """
        獲取空間上下文描述

        Args:
            region: 區域位置標識

        Returns:
            str: 空間上下文描述
        """
        try:
            spatial_mapping = {
                "top_left": "upper left",
                "top_center": "upper",
                "top_right": "upper right",
                "middle_left": "left side",
                "middle_center": "central",
                "middle_right": "right side",
                "bottom_left": "lower left",
                "bottom_center": "lower",
                "bottom_right": "lower right"
            }

            return spatial_mapping.get(region, "")

        except Exception as e:
            logger.warning(f"Error getting spatial context for region '{region}': {str(e)}")
            return ""