ytcollector 1.1.2__tar.gz → 1.1.7__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.2
3
+ Version: 1.1.7
4
4
  Summary: YouTube 콘텐츠 수집기 - 얼굴, 번호판, 타투, 텍스트 감지
5
5
  Author: YTCollector Team
6
6
  License: MIT
@@ -44,7 +44,9 @@ Requires-Dist: ytcollector[analysis,dev]; extra == "all"
44
44
  pip install yt-dlp
45
45
  ```
46
46
 
47
- ### 분석 기능용 패키지 (권장)
47
+ ### 분석 기능용 패키지 (권장 - v1.1.6+)
48
+
49
+ 분석 기능을 원활하게 사용하려면 아래 패키지들이 필요합니다. GPU(CUDA)가 설치된 경우 자동으로 가속이 활성화됩니다.
48
50
 
49
51
  ```bash
50
52
  pip install opencv-python easyocr numpy ultralytics
@@ -132,7 +134,7 @@ ytcollector -c face --proxy http://proxy.server:8080
132
134
 
133
135
  ## SBS Dataset 구축 (URL 리스트 기반)
134
136
 
135
- URL 리스트를 기반으로 영상을 수집하고 특정 시점을 기준으로 자동으로 클리핑(3분 미만)하는 기능입니다.
137
+ URL 리스트를 기반으로 영상을 수집하고 특정 시점을 기준으로 자동으로 클리핑(3분 미만)하는 기능입니다. (v1.1.6에서 ROI 엔진 최적화 적용)
136
138
 
137
139
  ### 실행 방법
138
140
 
@@ -168,7 +170,8 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
168
170
  ├── 번호판_미감지/ # 번호판 미감지 (수동 확인용)
169
171
  ├── 타투/ # 타투 감지된 영상
170
172
  ├── 텍스트/ # 텍스트 감지된 영상
171
- └── .archive.txt # 다운로드 기록 (중복 방지)
173
+ ├── .archive.txt # 기본 다운로드 기록
174
+ └── youtube_url_*.txt # 카테고리별 성공 로그 (v1.1.5+ 중복 체크에 활용)
172
175
  ```
173
176
 
174
177
  ## 파일 구조
@@ -188,9 +191,14 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
188
191
  |-----------|-----------|------|
189
192
  | 얼굴 | OpenCV Haar Cascade | 정면 얼굴 감지 |
190
193
  | 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
191
- | 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
194
+ | 번호판 | YOLO-World + ROI OCR | v1.1.6: YOLO 감지 해당 영역만 OCR (속도 2x, 정확도 향상) |
192
195
  | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
193
196
 
197
+ ### 주요 최적화 (v1.1.5~1.1.6)
198
+ - **ROI 기반 감지**: 전체 화면이 아닌 YOLO가 지정한 영역만 OCR하여 속도와 정확도 대폭 향상
199
+ - **GPU 가속 지원**: CUDA 사용 가능 시 YOLO 및 OCR 자동 가속
200
+ - **로그 기반 중복 방지**: 로컬 파일이 없어도 `youtube_url_*.txt` 기록을 참조하여 중복 분석 방지
201
+
194
202
  ## 주의사항
195
203
 
196
204
  - 영상은 다운로드 후 분석하여 해당 카테고리가 감지된 경우에만 저장됩니다
@@ -10,7 +10,9 @@
10
10
  pip install yt-dlp
11
11
  ```
12
12
 
13
- ### 분석 기능용 패키지 (권장)
13
+ ### 분석 기능용 패키지 (권장 - v1.1.6+)
14
+
15
+ 분석 기능을 원활하게 사용하려면 아래 패키지들이 필요합니다. GPU(CUDA)가 설치된 경우 자동으로 가속이 활성화됩니다.
14
16
 
15
17
  ```bash
16
18
  pip install opencv-python easyocr numpy ultralytics
@@ -98,7 +100,7 @@ ytcollector -c face --proxy http://proxy.server:8080
98
100
 
99
101
  ## SBS Dataset 구축 (URL 리스트 기반)
100
102
 
101
- URL 리스트를 기반으로 영상을 수집하고 특정 시점을 기준으로 자동으로 클리핑(3분 미만)하는 기능입니다.
103
+ URL 리스트를 기반으로 영상을 수집하고 특정 시점을 기준으로 자동으로 클리핑(3분 미만)하는 기능입니다. (v1.1.6에서 ROI 엔진 최적화 적용)
102
104
 
103
105
  ### 실행 방법
104
106
 
@@ -134,7 +136,8 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
134
136
  ├── 번호판_미감지/ # 번호판 미감지 (수동 확인용)
135
137
  ├── 타투/ # 타투 감지된 영상
136
138
  ├── 텍스트/ # 텍스트 감지된 영상
137
- └── .archive.txt # 다운로드 기록 (중복 방지)
139
+ ├── .archive.txt # 기본 다운로드 기록
140
+ └── youtube_url_*.txt # 카테고리별 성공 로그 (v1.1.5+ 중복 체크에 활용)
138
141
  ```
139
142
 
140
143
  ## 파일 구조
@@ -154,9 +157,14 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
154
157
  |-----------|-----------|------|
155
158
  | 얼굴 | OpenCV Haar Cascade | 정면 얼굴 감지 |
156
159
  | 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
157
- | 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
160
+ | 번호판 | YOLO-World + ROI OCR | v1.1.6: YOLO 감지 해당 영역만 OCR (속도 2x, 정확도 향상) |
158
161
  | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
159
162
 
163
+ ### 주요 최적화 (v1.1.5~1.1.6)
164
+ - **ROI 기반 감지**: 전체 화면이 아닌 YOLO가 지정한 영역만 OCR하여 속도와 정확도 대폭 향상
165
+ - **GPU 가속 지원**: CUDA 사용 가능 시 YOLO 및 OCR 자동 가속
166
+ - **로그 기반 중복 방지**: 로컬 파일이 없어도 `youtube_url_*.txt` 기록을 참조하여 중복 분석 방지
167
+
160
168
  ## 주의사항
161
169
 
162
170
  - 영상은 다운로드 후 분석하여 해당 카테고리가 감지된 경우에만 저장됩니다
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ytcollector"
7
- version = "1.1.2"
7
+ version = "1.1.7"
8
8
  description = "YouTube 콘텐츠 수집기 - 얼굴, 번호판, 타투, 텍스트 감지"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.8"
@@ -22,7 +22,7 @@ from .analyzer import VideoAnalyzer, check_dependencies
22
22
  from .downloader import YouTubeDownloader
23
23
  from .cli import run, main as cli_main
24
24
 
25
- __version__ = "1.0.0"
25
+ __version__ = "1.1.7"
26
26
  __all__ = [
27
27
  # 주요 클래스
28
28
  "VideoAnalyzer",
@@ -1,33 +1,37 @@
1
- import re
2
1
  import threading
3
- from .config import LICENSE_PLATE_PATTERNS
2
+ import re
3
+ import os
4
4
 
5
- # 선택적 import
6
5
  try:
7
6
  import cv2
8
7
  CV2_AVAILABLE = True
9
8
  except ImportError:
10
9
  CV2_AVAILABLE = False
11
10
 
12
- try:
13
- import easyocr
14
- EASYOCR_AVAILABLE = True
15
- except ImportError:
16
- EASYOCR_AVAILABLE = False
17
-
18
11
  try:
19
12
  import numpy as np
20
13
  NUMPY_AVAILABLE = True
21
14
  except ImportError:
22
15
  NUMPY_AVAILABLE = False
23
16
 
17
+ try:
18
+ import easyocr
19
+ EASYOCR_AVAILABLE = True
20
+ except ImportError:
21
+ EASYOCR_AVAILABLE = False
22
+
24
23
  try:
25
24
  from ultralytics import YOLOWorld
26
25
  YOLO_AVAILABLE = True
27
26
  except ImportError:
28
- YOLOWorld = None
29
27
  YOLO_AVAILABLE = False
30
28
 
29
+ try:
30
+ import torch
31
+ USE_GPU = torch.cuda.is_available()
32
+ except ImportError:
33
+ USE_GPU = False
34
+
31
35
  from .config import LICENSE_PLATE_PATTERNS, YOLO_MODEL_NAME, YOLO_CONFIDENCE, YOLO_PROMPTS
32
36
 
33
37
 
@@ -46,20 +50,23 @@ class VideoAnalyzer:
46
50
  self.face_cascade = cv2.CascadeClassifier(cascade_path)
47
51
 
48
52
  def _init_ocr(self):
49
- """OCR 리더 초기화 (필요할 때만, 스레드 안전)"""
53
+ """OCR 리더 초기화 (필요할 때만, 스레드 안전, GPU 가속 체크)"""
50
54
  if EASYOCR_AVAILABLE and self.ocr_reader is None:
51
55
  with self._ocr_lock:
52
56
  if self.ocr_reader is None:
53
- print(" OCR 엔진 초기화 중...")
54
- self.ocr_reader = easyocr.Reader(['ko', 'en'], gpu=False, verbose=False)
57
+ gpu_status = "사용" if USE_GPU else "미사용"
58
+ print(f" OCR 엔진 초기화 중... (GPU: {gpu_status})")
59
+ self.ocr_reader = easyocr.Reader(['ko', 'en'], gpu=USE_GPU, verbose=False)
55
60
 
56
61
  def _init_yolo(self):
57
- """YOLO-World 모델 초기화 (필요할 때만, 스레드 안전)"""
62
+ """YOLO-World 모델 초기화 (필요할 때만, 스레드 안전, GPU 가속 체크)"""
58
63
  if YOLO_AVAILABLE and self.yolo_model is None:
59
64
  with self._ocr_lock:
60
65
  if self.yolo_model is None:
61
- print(f" YOLO-World 모델({YOLO_MODEL_NAME}) 로딩 중...")
66
+ device = "cuda" if USE_GPU else "cpu"
67
+ print(f" YOLO-World 모델 로딩 중... (Device: {device})")
62
68
  self.yolo_model = YOLOWorld(YOLO_MODEL_NAME)
69
+ self.yolo_model.to(device)
63
70
  # 감지할 클래스(프롬프트) 설정
64
71
  self.yolo_model.set_classes(YOLO_PROMPTS)
65
72
 
@@ -133,42 +140,66 @@ class VideoAnalyzer:
133
140
  print(f" ⚠ OCR 에러: {e}")
134
141
  return []
135
142
 
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:
143
+ def detect_license_plate(self, frame, texts=None):
144
+ """
145
+ ROI 기반 번호판 감지 (최적화 버전)
146
+ 1. YOLO로 번호판 영역(ROI)을 먼저 찾음
147
+ 2. 찾은 영역만 잘라서 OCR 수행 (속도 및 정확도 향상)
148
+ """
149
+ if not YOLO_AVAILABLE or frame is None:
157
150
  return False
151
+
152
+ try:
153
+ self._init_yolo()
154
+ results = self.yolo_model(frame, verbose=False, conf=YOLO_CONFIDENCE)
158
155
 
159
- # 2. 개별 텍스트 박스 체크
160
- for text in texts:
161
- text_clean = re.sub(r'[^0-9가-힣]', '', text)
162
- for pattern in LICENSE_PLATE_PATTERNS:
163
- if re.search(pattern, text_clean):
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):
156
+ yolo_high_conf = False
157
+ roi_ocr_matched = False
158
+
159
+ for r in results:
160
+ # 0: license plate
161
+ for box in r.boxes:
162
+ if box.cls == 0:
163
+ conf = float(box.conf)
164
+ if conf > 0.8:
165
+ yolo_high_conf = True
166
+
167
+ # ROI 크로핑 및 타겟 OCR
168
+ x1, y1, x2, y2 = map(int, box.xyxy[0])
169
+ h, w = frame.shape[:2]
170
+ # 패딩 10% 추가
171
+ pad_w = int((x2 - x1) * 0.1)
172
+ pad_h = int((y2 - y1) * 0.1)
173
+
174
+ crop_x1 = max(0, x1 - pad_w)
175
+ crop_y1 = max(0, y1 - pad_h)
176
+ crop_x2 = min(w, x2 + pad_w)
177
+ crop_y2 = min(h, y2 + pad_h)
178
+
179
+ roi = frame[crop_y1:crop_y2, crop_x1:crop_x2]
180
+ if roi.size > 0:
181
+ # ROI에 대해서만 OCR 실행
182
+ roi_texts = self.detect_text(roi)
183
+ if roi_texts:
184
+ combined_roi = "".join([re.sub(r'[^0-9가-힣]', '', t) for t in roi_texts])
185
+ for pattern in LICENSE_PLATE_PATTERNS:
186
+ # 개별 텍스트 및 결합 텍스트 확인
187
+ if any(re.search(pattern, re.sub(r'[^0-9가-힣]', '', t)) for t in roi_texts) or \
188
+ re.search(pattern, combined_roi):
189
+ roi_ocr_matched = True
190
+ break
191
+
192
+ if roi_ocr_matched: break
193
+ if roi_ocr_matched: break
194
+
195
+ # 최종 판정
196
+ if roi_ocr_matched or yolo_high_conf:
170
197
  return True
171
198
 
199
+ except Exception as e:
200
+ # print(f" ⚠ 번호판 ROI 분석 에러: {e}")
201
+ pass
202
+
172
203
  return False
173
204
 
174
205
  def detect_tattoo(self, frame):
@@ -214,7 +245,7 @@ class VideoAnalyzer:
214
245
  except:
215
246
  return False
216
247
 
217
- def analyze(self, video_path):
248
+ def analyze(self, video_path, target_category=None):
218
249
  """영상 전체 분석"""
219
250
  results = {
220
251
  'face': False,
@@ -268,16 +299,26 @@ class VideoAnalyzer:
268
299
  total_faces += len(faces)
269
300
  detected_now = True
270
301
 
271
- # 텍스트 번호판
272
- texts = self.detect_text(frame)
273
- if texts:
274
- results['text'] = True
275
- all_texts.extend(texts)
276
- detected_now = True
277
-
278
- # 번호판 감지 (프레임별로 결합 텍스트 및 YOLO 확인)
279
- if self.detect_license_plate(texts, frame=frame):
302
+ # 번호판 감지 (타겟 카테고리가 번호판인 경우 우선 수행 - ROI 최적화)
303
+ if target_category == 'license_plate':
304
+ if self.detect_license_plate(frame):
280
305
  results['license_plate'] = True
306
+ detected_now = True
307
+
308
+ # 텍스트 감지 (일반 텍스트 카테고리이거나 번호판 수집 중에도 텍스트 로그 기록을 위해 실행)
309
+ # 번호판 감지가 필요 없는 경우 전체 OCR을 건너뛰어 속도 향상 가능
310
+ if target_category == 'text' or (detected_now and target_category != 'license_plate'):
311
+ texts = self.detect_text(frame)
312
+ if texts:
313
+ results['text'] = True
314
+ all_texts.extend(texts)
315
+ detected_now = True
316
+ elif target_category == 'license_plate' and not results['license_plate']:
317
+ # 번호판을 못 찾은 경우에만 전체 화면 OCR 한 번 더 시도 (보수적 접근)
318
+ texts = self.detect_text(frame)
319
+ if texts:
320
+ all_texts.extend(texts)
321
+ # 이미 detect_license_plate에서 결과가 안 나왔으므로 여기서는 텍스트만 수집
281
322
 
282
323
  # 타투
283
324
  if self.detect_tattoo(frame):
@@ -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.2'
77
+ version='%(prog)s 1.1.6'
78
78
  )
79
79
  parser.add_argument(
80
80
  '--check-deps',
@@ -31,6 +31,29 @@ class YouTubeDownloader:
31
31
  def _get_ua(self):
32
32
  return random.choice(USER_AGENTS)
33
33
 
34
+ def _extract_vid(self, url):
35
+ """URL에서 비디오 ID 추출"""
36
+ if "v=" in url:
37
+ return url.split("v=")[1].split("&")[0]
38
+ return url.split("/")[-1]
39
+
40
+ def _load_processed_ids(self):
41
+ """모든 youtube_url_*.txt 파일에서 이미 처리된 비디오 ID 목록 로드"""
42
+ processed_ids = set()
43
+ for filename in os.listdir("."):
44
+ if filename.startswith("youtube_url_") and filename.endswith(".txt"):
45
+ try:
46
+ with open(filename, 'r', encoding='utf-8') as f:
47
+ for line in f:
48
+ if line.strip() and not line.strip().startswith('#'):
49
+ url = line.split(',')[0].strip()
50
+ vid = self._extract_vid(url)
51
+ if vid:
52
+ processed_ids.add(vid)
53
+ except:
54
+ continue
55
+ return processed_ids
56
+
34
57
  def _get_query(self, category):
35
58
  """검색어 순환 반환"""
36
59
  if category not in self.query_index:
@@ -161,7 +184,7 @@ class YouTubeDownloader:
161
184
 
162
185
  if status == "ok" and filepath:
163
186
  print(f" 🔍 분석 중...")
164
- analysis = self.analyzer.analyze(filepath)
187
+ analysis = self.analyzer.analyze(filepath, target_category=category)
165
188
 
166
189
  detected = []
167
190
  if analysis['face']:
@@ -243,10 +266,19 @@ class YouTubeDownloader:
243
266
 
244
267
  # 필터링
245
268
  filtered = []
269
+ processed_ids = self._load_processed_ids()
270
+
246
271
  for entry in entries:
247
272
  if not entry: continue
248
273
 
249
274
  vid = entry.get('id')
275
+ if not vid: continue
276
+
277
+ # 이미 처리된 영상은 즉시 패스
278
+ if vid in processed_ids:
279
+ print(f" ⏭ [기록됨] {vid}")
280
+ continue
281
+
250
282
  title = entry.get('title', '')
251
283
  dur = entry.get('duration') or self._get_duration(vid)
252
284
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ytcollector
3
- Version: 1.1.2
3
+ Version: 1.1.7
4
4
  Summary: YouTube 콘텐츠 수집기 - 얼굴, 번호판, 타투, 텍스트 감지
5
5
  Author: YTCollector Team
6
6
  License: MIT
@@ -44,7 +44,9 @@ Requires-Dist: ytcollector[analysis,dev]; extra == "all"
44
44
  pip install yt-dlp
45
45
  ```
46
46
 
47
- ### 분석 기능용 패키지 (권장)
47
+ ### 분석 기능용 패키지 (권장 - v1.1.6+)
48
+
49
+ 분석 기능을 원활하게 사용하려면 아래 패키지들이 필요합니다. GPU(CUDA)가 설치된 경우 자동으로 가속이 활성화됩니다.
48
50
 
49
51
  ```bash
50
52
  pip install opencv-python easyocr numpy ultralytics
@@ -132,7 +134,7 @@ ytcollector -c face --proxy http://proxy.server:8080
132
134
 
133
135
  ## SBS Dataset 구축 (URL 리스트 기반)
134
136
 
135
- URL 리스트를 기반으로 영상을 수집하고 특정 시점을 기준으로 자동으로 클리핑(3분 미만)하는 기능입니다.
137
+ URL 리스트를 기반으로 영상을 수집하고 특정 시점을 기준으로 자동으로 클리핑(3분 미만)하는 기능입니다. (v1.1.6에서 ROI 엔진 최적화 적용)
136
138
 
137
139
  ### 실행 방법
138
140
 
@@ -168,7 +170,8 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
168
170
  ├── 번호판_미감지/ # 번호판 미감지 (수동 확인용)
169
171
  ├── 타투/ # 타투 감지된 영상
170
172
  ├── 텍스트/ # 텍스트 감지된 영상
171
- └── .archive.txt # 다운로드 기록 (중복 방지)
173
+ ├── .archive.txt # 기본 다운로드 기록
174
+ └── youtube_url_*.txt # 카테고리별 성공 로그 (v1.1.5+ 중복 체크에 활용)
172
175
  ```
173
176
 
174
177
  ## 파일 구조
@@ -188,9 +191,14 @@ https://www.youtube.com/watch?v=aqz-KE-bpKQ, 00:10, sample_task
188
191
  |-----------|-----------|------|
189
192
  | 얼굴 | OpenCV Haar Cascade | 정면 얼굴 감지 |
190
193
  | 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
191
- | 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
194
+ | 번호판 | YOLO-World + ROI OCR | v1.1.6: YOLO 감지 해당 영역만 OCR (속도 2x, 정확도 향상) |
192
195
  | 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
193
196
 
197
+ ### 주요 최적화 (v1.1.5~1.1.6)
198
+ - **ROI 기반 감지**: 전체 화면이 아닌 YOLO가 지정한 영역만 OCR하여 속도와 정확도 대폭 향상
199
+ - **GPU 가속 지원**: CUDA 사용 가능 시 YOLO 및 OCR 자동 가속
200
+ - **로그 기반 중복 방지**: 로컬 파일이 없어도 `youtube_url_*.txt` 기록을 참조하여 중복 분석 방지
201
+
194
202
  ## 주의사항
195
203
 
196
204
  - 영상은 다운로드 후 분석하여 해당 카테고리가 감지된 경우에만 저장됩니다
File without changes