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.
- {ytcollector-1.1.1 → ytcollector-1.1.2}/PKG-INFO +8 -5
- {ytcollector-1.1.1 → ytcollector-1.1.2}/README.md +6 -4
- {ytcollector-1.1.1 → ytcollector-1.1.2}/pyproject.toml +2 -1
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/analyzer.py +85 -13
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/cli.py +1 -1
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/config.py +21 -13
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector.egg-info/PKG-INFO +8 -5
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector.egg-info/requires.txt +1 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/setup.cfg +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/__init__.py +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/dataset_builder.py +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/downloader.py +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector/utils.py +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector.egg-info/SOURCES.txt +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector.egg-info/dependency_links.txt +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector.egg-info/entry_points.txt +0 -0
- {ytcollector-1.1.1 → ytcollector-1.1.2}/ytcollector.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ytcollector
|
|
3
|
-
Version: 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
|
-
| 번호판 |
|
|
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
|
-
| 번호판 |
|
|
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.
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
@@ -37,14 +37,10 @@ CATEGORY_QUERIES = {
|
|
|
37
37
|
],
|
|
38
38
|
'text': [
|
|
39
39
|
"SBS 런닝맨 레전드",
|
|
40
|
-
"SBS
|
|
41
|
-
"
|
|
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
|
-
|
|
75
|
-
r'
|
|
76
|
-
|
|
77
|
-
r'\d{2
|
|
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.
|
|
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
|
-
| 번호판 |
|
|
189
|
-
| 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 |
|
|
190
|
+
| 텍스트 | EasyOCR | 한국어/영어 문자 인식 (분석 품질 및 프레임 수 개선) |
|
|
191
|
+
| 번호판 | YOLO-World + OCR | YOLO-World 기반 시각적 감지 + OCR 매칭 |
|
|
192
|
+
| 타투 | OpenCV HSV 분석 | 피부 영역 내 잉크 패턴 감지 |
|
|
190
193
|
|
|
191
194
|
## 주의사항
|
|
192
195
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|