ytcollector 1.0.9__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.0.9
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
  ## 사용법
@@ -54,7 +57,7 @@ pip install opencv-python easyocr numpy
54
57
  ### 기본 실행
55
58
 
56
59
  ```bash
57
- python main.py
60
+ ytcollector
58
61
  ```
59
62
 
60
63
  기본값: 얼굴 카테고리 5개, 최대 3분 영상
@@ -66,7 +69,7 @@ python main.py
66
69
  | `-c`, `--categories` | 수집할 카테고리 | `face` |
67
70
  | `-n`, `--count` | 카테고리당 다운로드 수 | `5` |
68
71
  | `-d`, `--duration` | 최대 영상 길이(분) | `3` |
69
- | `-o`, `--output` | 저장 경로 | `~/Downloads/youtube_collection` |
72
+ | `-o`, `--output` | 저장 경로 | `.` (현재 폴더) |
70
73
  | `--fast` | 고속 모드 (병렬 다운로드) | 비활성화 |
71
74
  | `-w`, `--workers` | 병렬 다운로드 수 | `3` |
72
75
  | `--proxy` | 프록시 주소 | 없음 |
@@ -86,45 +89,45 @@ python main.py
86
89
 
87
90
  ```bash
88
91
  # 얼굴 영상 10개 수집
89
- python main.py -c face -n 10
92
+ ytcollector -c face -n 10
90
93
 
91
94
  # 번호판 영상 수집 (최대 5분)
92
- python main.py -c license_plate -d 5
95
+ ytcollector -c license_plate -d 5
93
96
 
94
97
  # 타투 영상 수집
95
- python main.py -c tattoo -n 5
98
+ ytcollector -c tattoo -n 5
96
99
  ```
97
100
 
98
101
  ### 여러 카테고리
99
102
 
100
103
  ```bash
101
104
  # 얼굴과 텍스트 각 10개씩
102
- python main.py -c face text -n 10
105
+ ytcollector -c face text -n 10
103
106
 
104
107
  # 모든 카테고리 수집
105
- python main.py -c face license_plate tattoo text -n 5
108
+ ytcollector -c face license_plate tattoo text -n 5
106
109
  ```
107
110
 
108
111
  ### 고속 모드
109
112
 
110
113
  ```bash
111
114
  # 병렬 다운로드 (기본 3개 동시)
112
- python main.py -c face -n 10 --fast
115
+ ytcollector -c face -n 10 --fast
113
116
 
114
117
  # 5개 동시 다운로드
115
- python main.py -c face -n 10 --fast -w 5
118
+ ytcollector -c face -n 10 --fast -w 5
116
119
  ```
117
120
 
118
121
  ### 저장 경로 지정
119
122
 
120
123
  ```bash
121
- python main.py -c face -o /path/to/save
124
+ ytcollector -c face -o /path/to/save
122
125
  ```
123
126
 
124
127
  ### 프록시 사용
125
128
 
126
129
  ```bash
127
- python main.py -c face --proxy http://proxy.server:8080
130
+ ytcollector -c face --proxy http://proxy.server:8080
128
131
  ```
129
132
 
130
133
  ## SBS Dataset 구축 (URL 리스트 기반)
@@ -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
  ## 사용법
@@ -21,7 +23,7 @@ pip install opencv-python easyocr numpy
21
23
  ### 기본 실행
22
24
 
23
25
  ```bash
24
- python main.py
26
+ ytcollector
25
27
  ```
26
28
 
27
29
  기본값: 얼굴 카테고리 5개, 최대 3분 영상
@@ -33,7 +35,7 @@ python main.py
33
35
  | `-c`, `--categories` | 수집할 카테고리 | `face` |
34
36
  | `-n`, `--count` | 카테고리당 다운로드 수 | `5` |
35
37
  | `-d`, `--duration` | 최대 영상 길이(분) | `3` |
36
- | `-o`, `--output` | 저장 경로 | `~/Downloads/youtube_collection` |
38
+ | `-o`, `--output` | 저장 경로 | `.` (현재 폴더) |
37
39
  | `--fast` | 고속 모드 (병렬 다운로드) | 비활성화 |
38
40
  | `-w`, `--workers` | 병렬 다운로드 수 | `3` |
39
41
  | `--proxy` | 프록시 주소 | 없음 |
@@ -53,45 +55,45 @@ python main.py
53
55
 
54
56
  ```bash
55
57
  # 얼굴 영상 10개 수집
56
- python main.py -c face -n 10
58
+ ytcollector -c face -n 10
57
59
 
58
60
  # 번호판 영상 수집 (최대 5분)
59
- python main.py -c license_plate -d 5
61
+ ytcollector -c license_plate -d 5
60
62
 
61
63
  # 타투 영상 수집
62
- python main.py -c tattoo -n 5
64
+ ytcollector -c tattoo -n 5
63
65
  ```
64
66
 
65
67
  ### 여러 카테고리
66
68
 
67
69
  ```bash
68
70
  # 얼굴과 텍스트 각 10개씩
69
- python main.py -c face text -n 10
71
+ ytcollector -c face text -n 10
70
72
 
71
73
  # 모든 카테고리 수집
72
- python main.py -c face license_plate tattoo text -n 5
74
+ ytcollector -c face license_plate tattoo text -n 5
73
75
  ```
74
76
 
75
77
  ### 고속 모드
76
78
 
77
79
  ```bash
78
80
  # 병렬 다운로드 (기본 3개 동시)
79
- python main.py -c face -n 10 --fast
81
+ ytcollector -c face -n 10 --fast
80
82
 
81
83
  # 5개 동시 다운로드
82
- python main.py -c face -n 10 --fast -w 5
84
+ ytcollector -c face -n 10 --fast -w 5
83
85
  ```
84
86
 
85
87
  ### 저장 경로 지정
86
88
 
87
89
  ```bash
88
- python main.py -c face -o /path/to/save
90
+ ytcollector -c face -o /path/to/save
89
91
  ```
90
92
 
91
93
  ### 프록시 사용
92
94
 
93
95
  ```bash
94
- python main.py -c face --proxy http://proxy.server:8080
96
+ ytcollector -c face --proxy http://proxy.server:8080
95
97
  ```
96
98
 
97
99
  ## SBS Dataset 구축 (URL 리스트 기반)
@@ -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.0.9"
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",
@@ -0,0 +1,311 @@
1
+ import re
2
+ import threading
3
+ from .config import LICENSE_PLATE_PATTERNS
4
+
5
+ # 선택적 import
6
+ try:
7
+ import cv2
8
+ CV2_AVAILABLE = True
9
+ except ImportError:
10
+ CV2_AVAILABLE = False
11
+
12
+ try:
13
+ import easyocr
14
+ EASYOCR_AVAILABLE = True
15
+ except ImportError:
16
+ EASYOCR_AVAILABLE = False
17
+
18
+ try:
19
+ import numpy as np
20
+ NUMPY_AVAILABLE = True
21
+ except ImportError:
22
+ NUMPY_AVAILABLE = False
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
+
33
+
34
+ class VideoAnalyzer:
35
+ """영상 분석 클래스 - 얼굴, 텍스트, 번호판, 타투 감지"""
36
+
37
+ _ocr_lock = threading.Lock()
38
+
39
+ def __init__(self):
40
+ self.ocr_reader = None
41
+ self.face_cascade = None
42
+ self.yolo_model = None
43
+
44
+ if CV2_AVAILABLE:
45
+ cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
46
+ self.face_cascade = cv2.CascadeClassifier(cascade_path)
47
+
48
+ def _init_ocr(self):
49
+ """OCR 리더 초기화 (필요할 때만, 스레드 안전)"""
50
+ if EASYOCR_AVAILABLE and self.ocr_reader is None:
51
+ with self._ocr_lock:
52
+ if self.ocr_reader is None:
53
+ print(" OCR 엔진 초기화 중...")
54
+ self.ocr_reader = easyocr.Reader(['ko', 'en'], gpu=False, verbose=False)
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
+
66
+ def extract_frames(self, video_path, num_frames=10):
67
+ """영상에서 균등 간격으로 프레임 추출"""
68
+ if not CV2_AVAILABLE:
69
+ return []
70
+
71
+ cap = cv2.VideoCapture(video_path)
72
+ if not cap.isOpened():
73
+ return []
74
+
75
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
76
+ if total_frames <= 0:
77
+ cap.release()
78
+ return []
79
+
80
+ frame_indices = [int(i * total_frames / (num_frames + 1)) for i in range(1, num_frames + 1)]
81
+ frames = []
82
+
83
+ for idx in frame_indices:
84
+ cap.set(cv2.CAP_PROP_POS_FRAMES, idx)
85
+ ret, frame = cap.read()
86
+ if ret:
87
+ frames.append(frame)
88
+
89
+ cap.release()
90
+ return frames
91
+
92
+ def detect_faces(self, frame):
93
+ """Haar Cascade로 얼굴 감지"""
94
+ if not CV2_AVAILABLE or self.face_cascade is None:
95
+ return []
96
+
97
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
98
+ return self.face_cascade.detectMultiScale(
99
+ gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)
100
+ )
101
+
102
+ def detect_text(self, frame):
103
+ """EasyOCR로 텍스트 감지 (스레드 안전)"""
104
+ if not EASYOCR_AVAILABLE:
105
+ return []
106
+
107
+ self._init_ocr()
108
+ try:
109
+ h, w = frame.shape[:2]
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)
124
+
125
+ with self._ocr_lock:
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}")
134
+ return []
135
+
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. 개별 텍스트 박스 체크
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):
170
+ return True
171
+
172
+ return False
173
+
174
+ def detect_tattoo(self, frame):
175
+ """피부 영역에서 타투(어두운 잉크 패턴) 감지"""
176
+ if not CV2_AVAILABLE or not NUMPY_AVAILABLE:
177
+ return False
178
+
179
+ try:
180
+ hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
181
+
182
+ # 피부색 범위
183
+ lower_skin = np.array([0, 30, 80], dtype=np.uint8)
184
+ upper_skin = np.array([17, 170, 255], dtype=np.uint8)
185
+ skin_mask = cv2.inRange(hsv, lower_skin, upper_skin)
186
+
187
+ # 노이즈 제거
188
+ kernel = np.ones((5, 5), np.uint8)
189
+ skin_mask = cv2.morphologyEx(skin_mask, cv2.MORPH_OPEN, kernel)
190
+ skin_mask = cv2.morphologyEx(skin_mask, cv2.MORPH_CLOSE, kernel)
191
+
192
+ skin_pixels = cv2.countNonZero(skin_mask)
193
+ total_pixels = frame.shape[0] * frame.shape[1]
194
+
195
+ # 피부 영역 최소 10% 필요
196
+ if skin_pixels < total_pixels * 0.10:
197
+ return False
198
+
199
+ # 피부 영역 내 어두운 픽셀(타투) 감지
200
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
201
+ skin_gray = cv2.bitwise_and(gray, gray, mask=skin_mask)
202
+ dark_mask = cv2.inRange(skin_gray, 1, 80)
203
+
204
+ dark_pixels = cv2.countNonZero(dark_mask)
205
+ dark_ratio = dark_pixels / max(skin_pixels, 1)
206
+
207
+ # 어두운 영역이 3~35%일 때 타투로 판정
208
+ if 0.03 < dark_ratio < 0.35:
209
+ contours, _ = cv2.findContours(dark_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
210
+ significant = [c for c in contours if cv2.contourArea(c) > 100]
211
+ return len(significant) >= 1
212
+
213
+ return False
214
+ except:
215
+ return False
216
+
217
+ def analyze(self, video_path):
218
+ """영상 전체 분석"""
219
+ results = {
220
+ 'face': False,
221
+ 'text': False,
222
+ 'license_plate': False,
223
+ 'tattoo': False,
224
+ 'face_count': 0,
225
+ 'detected_texts': [],
226
+ 'first_detection_sec': None,
227
+ 'first_detection_ts': None
228
+ }
229
+
230
+ if not CV2_AVAILABLE:
231
+ print(" ⚠ OpenCV 미설치")
232
+ return results
233
+
234
+ # 영상 정보 가져오기
235
+ cap = cv2.VideoCapture(video_path)
236
+ if not cap.isOpened():
237
+ return results
238
+
239
+ fps = cap.get(cv2.CAP_PROP_FPS) or 30
240
+ total_frames = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))
241
+ cap.release()
242
+
243
+ # 분석 프레임 수 증가 (10 -> 20)
244
+ num_analysis_frames = 20
245
+ # 영상이 아주 긴 경우(10분 이상) 더 많은 프레임 추출
246
+ if total_frames / fps > 600:
247
+ num_analysis_frames = 30
248
+
249
+ frame_indices = [int(i * total_frames / (num_analysis_frames + 1)) for i in range(1, num_analysis_frames + 1)]
250
+
251
+ all_texts = []
252
+ total_faces = 0
253
+
254
+ cap = cv2.VideoCapture(video_path)
255
+ for idx in frame_indices:
256
+ cap.set(cv2.CAP_PROP_POS_FRAMES, idx)
257
+ ret, frame = cap.read()
258
+ if not ret: continue
259
+
260
+ # 현재 프레임의 시간(초)
261
+ current_sec = idx / fps
262
+ detected_now = False
263
+
264
+ # 얼굴
265
+ faces = self.detect_faces(frame)
266
+ if len(faces) > 0:
267
+ results['face'] = True
268
+ total_faces += len(faces)
269
+ detected_now = True
270
+
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):
280
+ results['license_plate'] = True
281
+
282
+ # 타투
283
+ if self.detect_tattoo(frame):
284
+ results['tattoo'] = True
285
+ detected_now = True
286
+
287
+ # 첫 감지 시점 기록
288
+ if detected_now and results['first_detection_sec'] is None:
289
+ results['first_detection_sec'] = current_sec
290
+ m, s = int(current_sec // 60), int(current_sec % 60)
291
+ results['first_detection_ts'] = f"{m:02d}:{s:02d}"
292
+
293
+ cap.release()
294
+
295
+ if all_texts:
296
+ results['detected_texts'] = list(set(all_texts))[:10]
297
+
298
+ results['face_count'] = total_faces
299
+ return results
300
+
301
+
302
+ def check_dependencies():
303
+ """의존성 체크"""
304
+ missing = []
305
+ if not CV2_AVAILABLE:
306
+ missing.append("opencv-python")
307
+ if not EASYOCR_AVAILABLE:
308
+ missing.append("easyocr")
309
+ if not NUMPY_AVAILABLE:
310
+ missing.append("numpy")
311
+ return missing
@@ -51,8 +51,8 @@ def create_parser():
51
51
  parser.add_argument(
52
52
  '-o', '--output',
53
53
  type=str,
54
- default=os.path.expanduser("~/youtube"),
55
- help='저장 경로 (기본: ~/youtube)'
54
+ default=".",
55
+ help='저장 경로 (기본: 현재 폴더)'
56
56
  )
57
57
  parser.add_argument(
58
58
  '--fast',
@@ -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.0.9'
77
+ version='%(prog)s 1.1.2'
78
78
  )
79
79
  parser.add_argument(
80
80
  '--check-deps',
@@ -115,7 +115,7 @@ def run(
115
115
  categories = ['face']
116
116
 
117
117
  if output is None:
118
- output = os.path.expanduser("~/youtube")
118
+ output = "."
119
119
 
120
120
  # 의존성 체크
121
121
  missing = check_dependencies()
@@ -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
 
@@ -55,12 +51,38 @@ CATEGORY_NAMES = {
55
51
  'text': '텍스트'
56
52
  }
57
53
 
58
- # 번호판 정규식 패턴
54
+ # 카테고리별 제외 키워드 (제목에 포함 시 스킵)
55
+ BLACKLIST_KEYWORDS = {
56
+ 'tattoo': [
57
+ "두피 문신", "두피문신",
58
+ "눈썹 문신", "눈썹문신",
59
+ "입술 문신", "입술문신",
60
+ "립타투", "립 타투",
61
+ "헤어타투", "헤어 타투",
62
+ "구레나룻문신", "구레나룻 문신",
63
+ "틴트 입술",
64
+ "반영구", "SMP"
65
+ ],
66
+ 'face': [],
67
+ 'license_plate': [],
68
+ 'text': []
69
+ }
70
+
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
+ # 번호판 정규식 패턴 (한국 자동차 번호판 중심)
59
77
  LICENSE_PLATE_PATTERNS = [
60
- r'\d{2,3}[가-힣]\d{4}',
61
- r'[가-힣]{2}\d{2}[가-힣]\d{4}',
62
- r'[A-Z]{2,3}[-\s]?\d{2,4}[-\s]?[A-Z]{0,3}',
63
- 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+',
64
86
  ]
65
87
 
66
88
  # 스킵할 에러 메시지