Spaces:
Sleeping
Sleeping
| #!/usr/bin/env python3 | |
| # -*- coding: utf-8 -*- | |
| """ | |
| 🔍 간단한 이미지 분석기 | |
| 이미지에서 기본적인 시각적 특성을 추출하는 모듈 | |
| 기능: | |
| 1. 🎨 색상 분석 (주요 색상 추출) | |
| 2. 📏 크기 추정 | |
| 3. 🏷️ 객체 타입 추정 (확장자 기반) | |
| 4. 📄 기본 설명 생성 | |
| """ | |
| import os | |
| from PIL import Image | |
| import random | |
| from typing import Dict, List, Any | |
| class ImageAnalyzer: | |
| """간단한 이미지 분석기""" | |
| def __init__(self): | |
| """초기화""" | |
| self.color_names = { | |
| "red": ["빨강", "빨간색", "적색"], | |
| "blue": ["파랑", "파란색", "청색"], | |
| "green": ["초록", "녹색", "그린"], | |
| "yellow": ["노랑", "노란색", "황색"], | |
| "black": ["검정", "검은색", "흑색"], | |
| "white": ["하양", "흰색", "백색"], | |
| "gray": ["회색", "그레이"], | |
| "brown": ["갈색", "브라운"], | |
| "orange": ["주황", "오렌지색"], | |
| "purple": ["보라", "보라색", "자주색"], | |
| "pink": ["분홍", "핑크색"], | |
| "silver": ["은색", "실버"], | |
| "gold": ["금색", "골드"] | |
| } | |
| self.object_types = [ | |
| "스마트폰", "노트북", "태블릿", "이어폰", "헤드폰", "스피커", | |
| "마우스", "키보드", "시계", "안경", "가방", "지갑", "열쇠", | |
| "컵", "머그컵", "펜", "연필", "책", "노트", "다이어리", | |
| "인형", "쿠션", "베개", "담요", "램프", "화분", "액자", | |
| "카메라", "게임기", "리모컨", "충전기", "케이블", "보조배터리" | |
| ] | |
| self.materials = [ | |
| "플라스틱", "금속", "유리", "가죽", "원목", "섬유", "실리콘", | |
| "고무", "세라믹", "종이", "카드보드", "스테인리스", "알루미늄" | |
| ] | |
| def analyze_image(self, image_path: str) -> Dict[str, Any]: | |
| """ | |
| 이미지 분석 수행 | |
| Args: | |
| image_path: 이미지 파일 경로 | |
| Returns: | |
| 분석 결과 딕셔너리 | |
| """ | |
| try: | |
| if not os.path.exists(image_path): | |
| return self._get_default_analysis("파일이 존재하지 않습니다.") | |
| # 이미지 로드 | |
| with Image.open(image_path) as img: | |
| # 기본 정보 추출 | |
| width, height = img.size | |
| file_size = os.path.getsize(image_path) | |
| # 색상 분석 (간단한 방식) | |
| colors = self._analyze_colors(img) | |
| # 크기 추정 | |
| size_category = self._estimate_size(width, height, file_size) | |
| # 객체 타입 추정 (파일명 기반 + 랜덤) | |
| object_type = self._estimate_object_type(image_path) | |
| # 재질 추정 (랜덤 선택) | |
| materials = self._estimate_materials() | |
| # 상태 추정 | |
| condition = random.choice(["새것", "양호", "보통", "사용감 있음"]) | |
| # 설명 생성 | |
| description = self._generate_description(object_type, colors, size_category, condition) | |
| return { | |
| "object_type": object_type, | |
| "colors": colors, | |
| "materials": materials, | |
| "size": size_category, | |
| "condition": condition, | |
| "description": description, | |
| "image_info": { | |
| "width": width, | |
| "height": height, | |
| "file_size": file_size, | |
| "format": img.format | |
| } | |
| } | |
| except Exception as e: | |
| print(f"❌ 이미지 분석 중 오류: {e}") | |
| return self._get_default_analysis(f"분석 오류: {str(e)}") | |
| def _analyze_colors(self, img: Image.Image) -> List[str]: | |
| """간단한 색상 분석""" | |
| try: | |
| # 이미지를 축소해서 처리 속도 향상 | |
| img_small = img.resize((50, 50)) | |
| img_rgb = img_small.convert('RGB') | |
| # 픽셀 색상 샘플링 | |
| colors = [] | |
| width, height = img_small.size | |
| # 9개 지점에서 색상 샘플링 | |
| sample_points = [ | |
| (width//4, height//4), (width//2, height//4), (3*width//4, height//4), | |
| (width//4, height//2), (width//2, height//2), (3*width//4, height//2), | |
| (width//4, 3*height//4), (width//2, 3*height//4), (3*width//4, 3*height//4) | |
| ] | |
| for x, y in sample_points: | |
| r, g, b = img_rgb.getpixel((x, y)) | |
| color_name = self._rgb_to_color_name(r, g, b) | |
| if color_name not in colors: | |
| colors.append(color_name) | |
| # 최대 3개 색상만 반환 | |
| return colors[:3] if colors else ["회색"] | |
| except Exception as e: | |
| print(f"색상 분석 오류: {e}") | |
| return random.sample(list(self.color_names.keys()), 2) | |
| def _rgb_to_color_name(self, r: int, g: int, b: int) -> str: | |
| """RGB 값을 색상 이름으로 변환""" | |
| # 밝기 계산 | |
| brightness = (r + g + b) / 3 | |
| # 흑백 판단 | |
| if brightness < 50: | |
| return "black" | |
| elif brightness > 200: | |
| return "white" | |
| elif abs(r - g) < 30 and abs(g - b) < 30 and abs(r - b) < 30: | |
| return "gray" | |
| # 색상 판단 (간단한 방식) | |
| if r > g + 30 and r > b + 30: | |
| if g > 100: | |
| return "orange" | |
| else: | |
| return "red" | |
| elif g > r + 30 and g > b + 30: | |
| return "green" | |
| elif b > r + 30 and b > g + 30: | |
| if r > 100: | |
| return "purple" | |
| else: | |
| return "blue" | |
| elif r > 150 and g > 150 and b < 100: | |
| return "yellow" | |
| elif r > 150 and g < 100 and b > 150: | |
| return "purple" | |
| elif r > 100 and g > 100 and b > 150: | |
| return "pink" | |
| elif r > 130 and g > 100 and b < 80: | |
| return "brown" | |
| else: | |
| return random.choice(["gray", "silver", "gold"]) | |
| def _estimate_size(self, width: int, height: int, file_size: int) -> str: | |
| """크기 추정""" | |
| # 해상도 기반 크기 추정 | |
| total_pixels = width * height | |
| if total_pixels < 100000: # 약 300x300 미만 | |
| return "작은" | |
| elif total_pixels < 500000: # 약 700x700 미만 | |
| return "보통" | |
| elif total_pixels < 2000000: # 약 1400x1400 미만 | |
| return "큰" | |
| else: | |
| return "매우 큰" | |
| def _estimate_object_type(self, image_path: str) -> str: | |
| """객체 타입 추정""" | |
| filename = os.path.basename(image_path).lower() | |
| # 파일명에서 키워드 추출 | |
| for obj_type in self.object_types: | |
| if any(keyword in filename for keyword in [obj_type, obj_type.lower()]): | |
| return obj_type | |
| # 일반적인 키워드 매칭 | |
| keyword_mapping = { | |
| "phone": "스마트폰", "mobile": "스마트폰", "iphone": "스마트폰", "samsung": "스마트폰", | |
| "laptop": "노트북", "computer": "노트북", "macbook": "노트북", "notebook": "노트북", | |
| "tablet": "태블릿", "ipad": "태블릿", | |
| "headphone": "헤드폰", "earphone": "이어폰", "earbuds": "이어폰", | |
| "speaker": "스피커", "audio": "스피커", | |
| "mouse": "마우스", "keyboard": "키보드", | |
| "watch": "시계", "clock": "시계", | |
| "bag": "가방", "backpack": "가방", | |
| "cup": "컵", "mug": "머그컵", | |
| "book": "책", "note": "노트", | |
| "camera": "카메라", "photo": "카메라", | |
| "game": "게임기", "console": "게임기", | |
| "lamp": "램프", "light": "램프", | |
| "plant": "화분", "flower": "화분" | |
| } | |
| for keyword, obj_type in keyword_mapping.items(): | |
| if keyword in filename: | |
| return obj_type | |
| # 기본값: 랜덤 선택 | |
| return random.choice(self.object_types) | |
| def _estimate_materials(self) -> List[str]: | |
| """재질 추정 (랜덤)""" | |
| num_materials = random.randint(1, 3) | |
| return random.sample(self.materials, num_materials) | |
| def _generate_description(self, object_type: str, colors: List[str], size: str, condition: str) -> str: | |
| """설명 생성""" | |
| color_desc = ", ".join(colors) | |
| descriptions = [ | |
| f"{color_desc} 색상의 {size} {object_type}입니다. 상태는 {condition}해 보입니다.", | |
| f"{size} 크기의 {object_type}로, {color_desc} 톤이 특징적입니다. {condition} 상태입니다.", | |
| f"{condition} 상태의 {object_type}입니다. {color_desc} 색상이 인상적이며 {size} 사이즈입니다.", | |
| f"이 {object_type}은 {color_desc} 색조로 되어 있고, {size} 크기에 {condition} 상태를 보입니다." | |
| ] | |
| return random.choice(descriptions) | |
| def _get_default_analysis(self, error_msg: str = "") -> Dict[str, Any]: | |
| """기본 분석 결과 반환""" | |
| return { | |
| "object_type": random.choice(self.object_types), | |
| "colors": random.sample(list(self.color_names.keys()), 2), | |
| "materials": random.sample(self.materials, 2), | |
| "size": random.choice(["작은", "보통", "큰"]), | |
| "condition": random.choice(["양호", "보통"]), | |
| "description": f"이미지 분석에 실패했지만 기본적인 물건으로 추정됩니다. {error_msg}", | |
| "image_info": { | |
| "width": 0, | |
| "height": 0, | |
| "file_size": 0, | |
| "format": "Unknown" | |
| } | |
| } | |
| # 테스트 함수 | |
| def test_image_analyzer(): | |
| """이미지 분석기 테스트""" | |
| print("🔍 이미지 분석기 테스트") | |
| print("=" * 40) | |
| analyzer = ImageAnalyzer() | |
| # 더미 분석 테스트 | |
| result = analyzer._get_default_analysis("테스트") | |
| print(f"📊 테스트 결과:") | |
| print(f" 객체 타입: {result['object_type']}") | |
| print(f" 색상: {result['colors']}") | |
| print(f" 재질: {result['materials']}") | |
| print(f" 크기: {result['size']}") | |
| print(f" 상태: {result['condition']}") | |
| print(f" 설명: {result['description']}") | |
| if __name__ == "__main__": | |
| test_image_analyzer() |