ytcollector 1.1.1__tar.gz → 1.1.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytcollector
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: YouTube 콘텐츠 수집기 - 얼굴, 번호판, 타투, 텍스트 감지
5
5
  Author: YTCollector Team
6
6
  License: MIT
@@ -24,6 +24,7 @@ Provides-Extra: analysis
24
24
  Requires-Dist: opencv-python>=4.5.0; extra == "analysis"
25
25
  Requires-Dist: easyocr>=1.6.0; extra == "analysis"
26
26
  Requires-Dist: numpy>=1.20.0; extra == "analysis"
27
+ Requires-Dist: ultralytics>=8.0.0; extra == "analysis"
27
28
  Provides-Extra: dev
28
29
  Requires-Dist: pytest>=7.0.0; extra == "dev"
29
30
  Requires-Dist: black>=23.0.0; extra == "dev"
@@ -46,7 +47,9 @@ pip install yt-dlp
46
47
  ### 분석 기능용 패키지 (권장)
47
48
 
48
49
  ```bash
49
- pip install opencv-python easyocr numpy
50
+ pip install opencv-python easyocr numpy ultralytics
51
+ # YOLO-World 기능을 사용하려면 아래 CLIP 라이브러리 수동 설치가 필요합니다.
52
+ pip install "git+https://github.com/ultralytics/CLIP.git"
50
53
  ```
51
54
 
52
55
  ## 사용법
@@ -184,9 +187,9 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
184
187
  | 감지 항목 | 사용 기술 | 설명 |
185
188
  |-----------|-----------|------|
186
189
  | 얼굴 | OpenCV Haar Cascade | 정면 얼굴 감지 |
187
- | 텍스트 | EasyOCR | 한국어/영어 문자 인식 |
188
- | 번호판 | EasyOCR + 정규식 | 번호판 패턴 매칭 |
189
- | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 |
190
+ | 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
191
+ | 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
192
+ | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
190
193
 
191
194
  ## 주의사항
192
195
 
@@ -13,7 +13,9 @@ pip install yt-dlp
13
13
  ### 분석 기능용 패키지 (권장)
14
14
 
15
15
  ```bash
16
- pip install opencv-python easyocr numpy
16
+ pip install opencv-python easyocr numpy ultralytics
17
+ # YOLO-World 기능을 사용하려면 아래 CLIP 라이브러리 수동 설치가 필요합니다.
18
+ pip install "git+https://github.com/ultralytics/CLIP.git"
17
19
  ```
18
20
 
19
21
  ## 사용법
@@ -151,9 +153,9 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
151
153
  | 감지 항목 | 사용 기술 | 설명 |
152
154
  |-----------|-----------|------|
153
155
  | 얼굴 | OpenCV Haar Cascade | 정면 얼굴 감지 |
154
- | 텍스트 | EasyOCR | 한국어/영어 문자 인식 |
155
- | 번호판 | EasyOCR + 정규식 | 번호판 패턴 매칭 |
156
- | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 |
156
+ | 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
157
+ | 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
158
+ | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
157
159
 
158
160
  ## 주의사항
159
161
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ytcollector"
7
- version = "1.1.1"
7
+ version = "1.1.2"
8
8
  description = "YouTube 콘텐츠 수집기 - 얼굴, 번호판, 타투, 텍스트 감지"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -34,6 +34,7 @@ analysis = [
34
34
  "opencv-python>=4.5.0",
35
35
  "easyocr>=1.6.0",
36
36
  "numpy>=1.20.0",
37
+ "ultralytics>=8.0.0",
37
38
  ]
38
39
  dev = [
39
40
  "pytest>=7.0.0",
@@ -21,6 +21,15 @@ try:
21
21
  except ImportError:
22
22
  NUMPY_AVAILABLE = False
23
23
 
24
+ try:
25
+ from ultralytics import YOLOWorld
26
+ YOLO_AVAILABLE = True
27
+ except ImportError:
28
+ YOLOWorld = None
29
+ YOLO_AVAILABLE = False
30
+
31
+ from .config import LICENSE_PLATE_PATTERNS, YOLO_MODEL_NAME, YOLO_CONFIDENCE, YOLO_PROMPTS
32
+
24
33
 
25
34
  class VideoAnalyzer:
26
35
  """영상 분석 클래스 - 얼굴, 텍스트, 번호판, 타투 감지"""
@@ -30,6 +39,7 @@ class VideoAnalyzer:
30
39
  def __init__(self):
31
40
  self.ocr_reader = None
32
41
  self.face_cascade = None
42
+ self.yolo_model = None
33
43
 
34
44
  if CV2_AVAILABLE:
35
45
  cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
@@ -43,6 +53,16 @@ class VideoAnalyzer:
43
53
  print(" OCR 엔진 초기화 중...")
44
54
  self.ocr_reader = easyocr.Reader(['ko', 'en'], gpu=False, verbose=False)
45
55
 
56
+ def _init_yolo(self):
57
+ """YOLO-World 모델 초기화 (필요할 때만, 스레드 안전)"""
58
+ if YOLO_AVAILABLE and self.yolo_model is None:
59
+ with self._ocr_lock:
60
+ if self.yolo_model is None:
61
+ print(f" YOLO-World 모델({YOLO_MODEL_NAME}) 로딩 중...")
62
+ self.yolo_model = YOLOWorld(YOLO_MODEL_NAME)
63
+ # 감지할 클래스(프롬프트) 설정
64
+ self.yolo_model.set_classes(YOLO_PROMPTS)
65
+
46
66
  def extract_frames(self, video_path, num_frames=10):
47
67
  """영상에서 균등 간격으로 프레임 추출"""
48
68
  if not CV2_AVAILABLE:
@@ -87,23 +107,68 @@ class VideoAnalyzer:
87
107
  self._init_ocr()
88
108
  try:
89
109
  h, w = frame.shape[:2]
90
- if w > 640:
91
- scale = 640 / w
92
- frame = cv2.resize(frame, (640, int(h * scale)))
110
+
111
+ # 가독성 개선을 위해 1080p 수준으로 리사이즈 (너무 작으면 인식률 저하)
112
+ if w > 1280:
113
+ scale = 1280 / w
114
+ frame = cv2.resize(frame, (1280, int(h * scale)), interpolation=cv2.INTER_LANCZOS4)
115
+ elif w < 640:
116
+ # 너무 작은 경우 확대
117
+ scale = 960 / w
118
+ frame = cv2.resize(frame, (960, int(h * scale)), interpolation=cv2.INTER_CUBIC)
119
+
120
+ # 전처리: 그레이스케일 및 대비 강화 (옵션)
121
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
122
+ clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
123
+ processed = clahe.apply(gray)
93
124
 
94
125
  with self._ocr_lock:
95
- results = self.ocr_reader.readtext(frame)
96
- return [r[1] for r in results if r[2] > 0.3]
97
- except:
126
+ # 원본(컬러)과 전처리(그레이) 중 선택 가능하나 보통 EasyOCR은 컬러에서 잘 작동함
127
+ # 대비 강화된 그레이스케일을 사용해봄
128
+ results = self.ocr_reader.readtext(processed)
129
+
130
+ # 신뢰도 임계값 0.25로 약간 하향 조정 (기존 0.3)
131
+ return [r[1] for r in results if r[2] > 0.25]
132
+ except Exception as e:
133
+ print(f" ⚠ OCR 에러: {e}")
98
134
  return []
99
135
 
100
- def detect_license_plate(self, texts):
101
- """텍스트에서 번호판 패턴 감지"""
136
+ def detect_license_plate(self, texts, frame=None):
137
+ """텍스트에서 번호판 패턴 감지 및 YOLO-World 보조 감지"""
138
+ # 1. YOLO-World로 번호판 영역 확인
139
+ if YOLO_AVAILABLE and frame is not None:
140
+ try:
141
+ self._init_yolo()
142
+ if self.yolo_model:
143
+ results = self.yolo_model(frame, verbose=False, conf=YOLO_CONFIDENCE)
144
+ for r in results:
145
+ # YOLO-World 클래스 인덱스는 YOLO_PROMPTS 순서와 같음
146
+ # 0: license plate, 1: tattoo, 2: face (config 기준)
147
+ if any(box.cls == 0 for box in r.boxes):
148
+ # 번호판이 감지됨 -> 텍스트가 조금이라도 있으면 통과
149
+ if texts: return True
150
+ # 텍스트가 없어도 신뢰도가 높으면 감지된 것으로 간주 (옵션)
151
+ if any(box.conf > 0.5 for box in r.boxes if box.cls == 0):
152
+ return True
153
+ except:
154
+ pass
155
+
156
+ if not texts:
157
+ return False
158
+
159
+ # 2. 개별 텍스트 박스 체크
102
160
  for text in texts:
103
- text_clean = text.replace(' ', '').upper()
161
+ text_clean = re.sub(r'[^0-9가-힣]', '', text)
104
162
  for pattern in LICENSE_PLATE_PATTERNS:
105
163
  if re.search(pattern, text_clean):
106
164
  return True
165
+
166
+ # 3. 프레임 내 모든 텍스트 결합 후 체크 (번호판이 쪼개진 경우 대응)
167
+ combined_text = "".join([re.sub(r'[^0-9가-힣]', '', t) for t in texts])
168
+ for pattern in LICENSE_PLATE_PATTERNS:
169
+ if re.search(pattern, combined_text):
170
+ return True
171
+
107
172
  return False
108
173
 
109
174
  def detect_tattoo(self, frame):
@@ -175,7 +240,12 @@ class VideoAnalyzer:
175
240
  total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
176
241
  cap.release()
177
242
 
178
- num_analysis_frames = 10
243
+ # 분석 프레임 수 증가 (10 -> 20)
244
+ num_analysis_frames = 20
245
+ # 영상이 아주 긴 경우(10분 이상) 더 많은 프레임 추출
246
+ if total_frames / fps > 600:
247
+ num_analysis_frames = 30
248
+
179
249
  frame_indices = [int(i * total_frames / (num_analysis_frames + 1)) for i in range(1, num_analysis_frames + 1)]
180
250
 
181
251
  all_texts = []
@@ -198,12 +268,16 @@ class VideoAnalyzer:
198
268
  total_faces += len(faces)
199
269
  detected_now = True
200
270
 
201
- # 텍스트
271
+ # 텍스트 및 번호판
202
272
  texts = self.detect_text(frame)
203
273
  if texts:
204
274
  results['text'] = True
205
275
  all_texts.extend(texts)
206
276
  detected_now = True
277
+
278
+ # 번호판 감지 (프레임별로 결합 텍스트 및 YOLO 확인)
279
+ if self.detect_license_plate(texts, frame=frame):
280
+ results['license_plate'] = True
207
281
 
208
282
  # 타투
209
283
  if self.detect_tattoo(frame):
@@ -218,9 +292,7 @@ class VideoAnalyzer:
218
292
 
219
293
  cap.release()
220
294
 
221
- # 번호판 (텍스트에서)
222
295
  if all_texts:
223
- results['license_plate'] = self.detect_license_plate(all_texts)
224
296
  results['detected_texts'] = list(set(all_texts))[:10]
225
297
 
226
298
  results['face_count'] = total_faces
@@ -74,7 +74,7 @@ def create_parser():
74
74
  parser.add_argument(
75
75
  '-v', '--version',
76
76
  action='version',
77
- version='%(prog)s 1.1.1'
77
+ version='%(prog)s 1.1.2'
78
78
  )
79
79
  parser.add_argument(
80
80
  '--check-deps',
@@ -37,14 +37,10 @@ CATEGORY_QUERIES = {
37
37
  ],
38
38
  'text': [
39
39
  "SBS 런닝맨 레전드",
40
- "SBS 미운우리새끼 명장면",
41
- "SBS 동상이몽 클립",
42
- "SBS 집사부일체 모음",
43
- "SBS 골목식당 레전드",
44
- "SBS 맛남의광장 클립",
45
- "SBS 불타는청춘 명장면",
46
- "SBS 정글의법칙 레전드",
47
- "SBS 예능",
40
+ "SBS 예능 쇼츠",
41
+ "재미있는 자막 영상 쇼츠",
42
+ "SBS 파워FM 보이는 라디오",
43
+ "SBS 연예대상 소감",
48
44
  ],
49
45
  }
50
46
 
@@ -61,6 +57,9 @@ BLACKLIST_KEYWORDS = {
61
57
  "두피 문신", "두피문신",
62
58
  "눈썹 문신", "눈썹문신",
63
59
  "입술 문신", "입술문신",
60
+ "립타투", "립 타투",
61
+ "헤어타투", "헤어 타투",
62
+ "구레나룻문신", "구레나룻 문신",
64
63
  "틴트 입술",
65
64
  "반영구", "SMP"
66
65
  ],
@@ -69,12 +68,21 @@ BLACKLIST_KEYWORDS = {
69
68
  'text': []
70
69
  }
71
70
 
72
- # 번호판 정규식 패턴
71
+ # YOLO 설정
72
+ YOLO_MODEL_NAME = 'yolov8s-world.pt' # YOLO-World 모델 (Open Vocabulary)
73
+ YOLO_CONFIDENCE = 0.3 # YOLO-World는 임계값을 약간 낮게 설정 가능
74
+ YOLO_PROMPTS = ["license plate"]
75
+
76
+ # 번호판 정규식 패턴 (한국 자동차 번호판 중심)
73
77
  LICENSE_PLATE_PATTERNS = [
74
- r'\d{2,3}[가-힣]\d{4}',
75
- r'[가-힣]{2}\d{2}[가-힣]\d{4}',
76
- r'[A-Z]{2,3}[-\s]?\d{2,4}[-\s]?[A-Z]{0,3}',
77
- r'\d{2,4}[-\s]?[A-Z]{2,3}[-\s]?\d{2,4}',
78
+ # 1. 신형/구형 번호판 (12가 3456, 123가 4567)
79
+ r'\d{2,3}[가-힣]{1}\d{4}',
80
+ # 2. 지역 포함 번호판 (서울 12 가 3456)
81
+ r'[가-힣]{2}\d{2}[가-힣]{1}\d{4}',
82
+ # 3. 전기차/외교/임시 등 특수 패턴 대응
83
+ r'[가-힣]{2,3}\d{4}', # (예: 외교 1234, 임시 1234)
84
+ # 4. 결합된 텍스트에서 숫자-글자-숫자 구성 포착
85
+ r'\d+[가-힣]+\d+',
78
86
  ]
79
87
 
80
88
  # 스킵할 에러 메시지
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytcollector
3
- Version: 1.1.1
3
+ Version: 1.1.2
4
4
  Summary: YouTube 콘텐츠 수집기 - 얼굴, 번호판, 타투, 텍스트 감지
5
5
  Author: YTCollector Team
6
6
  License: MIT
@@ -24,6 +24,7 @@ Provides-Extra: analysis
24
24
  Requires-Dist: opencv-python>=4.5.0; extra == "analysis"
25
25
  Requires-Dist: easyocr>=1.6.0; extra == "analysis"
26
26
  Requires-Dist: numpy>=1.20.0; extra == "analysis"
27
+ Requires-Dist: ultralytics>=8.0.0; extra == "analysis"
27
28
  Provides-Extra: dev
28
29
  Requires-Dist: pytest>=7.0.0; extra == "dev"
29
30
  Requires-Dist: black>=23.0.0; extra == "dev"
@@ -46,7 +47,9 @@ pip install yt-dlp
46
47
  ### 분석 기능용 패키지 (권장)
47
48
 
48
49
  ```bash
49
- pip install opencv-python easyocr numpy
50
+ pip install opencv-python easyocr numpy ultralytics
51
+ # YOLO-World 기능을 사용하려면 아래 CLIP 라이브러리 수동 설치가 필요합니다.
52
+ pip install "git+https://github.com/ultralytics/CLIP.git"
50
53
  ```
51
54
 
52
55
  ## 사용법
@@ -184,9 +187,9 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
184
187
  | 감지 항목 | 사용 기술 | 설명 |
185
188
  |-----------|-----------|------|
186
189
  | 얼굴 | OpenCV Haar Cascade | 정면 얼굴 감지 |
187
- | 텍스트 | EasyOCR | 한국어/영어 문자 인식 |
188
- | 번호판 | EasyOCR + 정규식 | 번호판 패턴 매칭 |
189
- | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 |
190
+ | 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
191
+ | 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
192
+ | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
190
193
 
191
194
  ## 주의사항
192
195
 
@@ -7,6 +7,7 @@ ytcollector[analysis,dev]
7
7
  opencv-python>=4.5.0
8
8
  easyocr>=1.6.0
9
9
  numpy>=1.20.0
10
+ ultralytics>=8.0.0
10
11
 
11
12
  [dev]
12
13
  pytest>=7.0.0
File without changes