Ver3.0 ๐จ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ OpenCVยทPillow๋ฅผ ํ์ฉํ ์ปดํจํฐ ๋น์ ๊ฐ๋ฐ

๐จ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ OpenCVยทPillow๋ฅผ ํ์ฉํ ์ปดํจํฐ ๋น์ ๊ฐ๋ฐ
๐ ์ปดํจํฐ ๋น์ , ์ ์ง๊ธ ๋ฐฐ์์ผ ํ ๊น?
์์ฆ ์ฐ๋ฆฌ ์ฃผ๋ณ์ ๋๋ฌ๋ณด๋ฉด ์ปดํจํฐ ๋น์ ๊ธฐ์ ์ด ์ ๋ง ๋ง์ด ์ฌ์ฉ๋๊ณ ์์ด. ์ค๋งํธํฐ์ผ๋ก ์ฌ์ง ์ฐ์ ๋ ์๋์ผ๋ก ์ผ๊ตด์ ์ธ์ํ๋ ๊ธฐ๋ฅ, ์ธ์คํ๊ทธ๋จ ํํฐ, ์์จ์ฃผํ ์๋์ฐจ, ์ฌ์ง์ด ๋ณ์์์ X-ray ์ฌ์ง์ ๋ถ์ํ๋ ๊ฒ๊น์ง! ์ด ๋ชจ๋ ๊ฒ ์ปดํจํฐ ๋น์ ๊ธฐ์ ์ด์ผ. ๐
๊ทธ๋ฐ๋ฐ ์ด๋ฐ ๋ฉ์ง ๊ธฐ์ ์ ๋ฐฐ์ฐ๋ ค๋ฉด ์ด๋์๋ถํฐ ์์ํด์ผ ํ ๊น? ๋ฐ๋ก Python์ OpenCV์ Pillow๋ผ๋ ๋ ๊ฐ์ง ๊ฐ๋ ฅํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ผ. ์ด ๋ ์น๊ตฌ๋ค์ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์๋ ์ฐ๋งฅ์ด๋ผ๊ณ ํ ์ ์์ง. ์ค๋์ ์ด ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ํ์ฉํด์ ์ค์ ๋ก ์ด๋ป๊ฒ ์ปดํจํฐ ๋น์ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค ์ ์๋์ง ์์ธํ ์์๋ณผ ๊ฑฐ์ผ!
- โ OpenCV์ Pillow์ ์ฐจ์ด์ ๊ณผ ๊ฐ๊ฐ์ ์ฅ๋จ์
- โ ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ, ์ ์ฅํ๊ธฐ, ๊ธฐ๋ณธ ์กฐ์ ๋ฐฉ๋ฒ
- โ ํํฐ์ ํจ๊ณผ ์ ์ฉํ๋ ์ค์ ๊ธฐ๋ฒ
- โ ์ผ๊ตด ์ธ์๊ณผ ๊ฐ์ฒด ํ์ง ๊ตฌํ ๋ฐฉ๋ฒ
- โ ์ค๋ฌด์์ ๋ฐ๋ก ์ธ ์ ์๋ ํ๋ก์ ํธ ์์
๐ OpenCV vs Pillow: ์ด๋ค ๊ฑธ ์ ํํด์ผ ํ ๊น?
๋ง์ ์ฌ๋๋ค์ด ์ฒ์ ์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ฅผ ๋ฐฐ์ธ ๋ ๊ฐ์ฅ ๋จผ์ ํ๋ ์ง๋ฌธ์ด ๋ฐ๋ก ์ด๊ฑฐ์ผ. "OpenCV๋ Pillow ์ค์ ๋ญ ์จ์ผ ํด?" ์ฌ์ค ์ด ์ง๋ฌธ์ "์นํจ์ด๋ ํผ์ ์ค์ ๋ญ๊ฐ ๋ ๋ง์์ด?"๋ผ๊ณ ๋ฌป๋ ๊ฒ๊ณผ ๋น์ทํด. ๋ ๋ค ์ข์๋ฐ ์ฉ๋๊ฐ ๋ค๋ฅด๊ฑฐ๋ ! ๐๐
๊ฐ๋จํ๊ฒ ์ ๋ฆฌํ๋ฉด, Pillow๋ ์ด๋ฏธ์ง ํธ์ง๊ณผ ๊ฐ๊ณต์ ํนํ๋์ด ์๊ณ , OpenCV๋ ์ค์๊ฐ ๋น๋์ค ์ฒ๋ฆฌ์ ๊ณ ๊ธ ์ปดํจํฐ ๋น์ ์์
์ ์ต์ ํ๋์ด ์์ด. ๊ทธ๋ผ ๊ฐ๊ฐ์ ํน์ง์ ์์ธํ ์ดํด๋ณผ๊น?
๐จ Pillow (PIL)
์ฅ์ :โข ์ค์น์ ์ฌ์ฉ์ด ๋งค์ฐ ๊ฐ๋จํจ
โข ์ด๋ฏธ์ง ํฌ๋งท ๋ณํ์ด ์ฌ์
โข ํ ์คํธ ์ถ๊ฐ, ๋๋ก์ ๊ธฐ๋ฅ ์ฐ์
โข ๋ฉ๋ชจ๋ฆฌ ํจ์จ์
โข ์น ๊ฐ๋ฐ๊ณผ ํตํฉ์ด ํธ๋ฆฌ
์ฃผ์ ์ฉ๋:
์ธ๋ค์ผ ์์ฑ, ์ํฐ๋งํฌ ์ถ๊ฐ, ์ด๋ฏธ์ง ๋ฆฌ์ฌ์ด์ง, ํฌ๋งท ๋ณํ, ๊ฐ๋จํ ํํฐ ์ ์ฉ
๐ฌ OpenCV
์ฅ์ :โข ์ค์๊ฐ ์ฒ๋ฆฌ ์ฑ๋ฅ์ด ๋ฐ์ด๋จ
โข ๊ณ ๊ธ ์ปดํจํฐ ๋น์ ์๊ณ ๋ฆฌ์ฆ ์ ๊ณต
โข ๋น๋์ค ์ฒ๋ฆฌ ๊ธฐ๋ฅ ๊ฐ๋ ฅ
โข ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ ํตํฉ ๊ฐ๋ฅ
โข ๋์ฉ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์ต์ ํ
์ฃผ์ ์ฉ๋:
์ผ๊ตด ์ธ์, ๊ฐ์ฒด ์ถ์ , ๋ชจ์ ๊ฐ์ง, ์์ ๋ถ์, AR/VR ๊ฐ๋ฐ
๐ ๏ธ ์ค์น๋ถํฐ ์์ํ๊ธฐ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก ์์ํด๋ณผ๊น? ๋จผ์ ํ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ค์ ์ค์นํด์ผ ํด. Python์ด ์ด๋ฏธ ์ค์น๋์ด ์๋ค๊ณ ๊ฐ์ ํ๊ณ ์งํํ ๊ฒ. ํฐ๋ฏธ๋์ด๋ ๋ช ๋ น ํ๋กฌํํธ๋ฅผ ์ด๊ณ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํด๋ณด์!
Step 1: Pillow ์ค์น
pip install Pillow
Pillow๋ Python Imaging Library(PIL)์ ํ๋์ ์ธ ๋ฒ์ ์ด์ผ. ์ค์น๊ฐ ์ ๋ง ๊ฐ๋จํ์ง?
Step 2: OpenCV ์ค์น
pip install opencv-python
pip install opencv-contrib-python
opencv-python์ ๊ธฐ๋ณธ ํจํค์ง๊ณ , opencv-contrib-python์ ์ถ๊ฐ ๊ธฐ๋ฅ๋ค์ด ํฌํจ๋ ํ์ฅ ํจํค์ง์ผ. ๋ ๋ค ์ค์นํ๋ ๊ฑธ ์ถ์ฒํด!
Step 3: ์ถ๊ฐ ์ ์ฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
pip install numpy
pip install matplotlib
numpy๋ ๋ฐฐ์ด ์ฐ์ฐ์ ํ์๊ณ , matplotlib์ ์ด๋ฏธ์ง๋ฅผ ํ๋ฉด์ ํ์ํ ๋ ์ ์ฉํด. ์ด๋ฏธ์ง ๋ฐ์ดํฐ๋ ๊ฒฐ๊ตญ ์ซ์ ๋ฐฐ์ด์ด๊ฑฐ๋ !
โข Python ๋ฒ์ ํธํ์ฑ ํ์ธ (3.7 ์ด์ ๊ถ์ฅ)
โข ๊ฐ์ํ๊ฒฝ ์ฌ์ฉ์ ์ถ์ฒ (venv ๋๋ conda)
โข Windows ์ฌ์ฉ์๋ Visual C++ ์ฌ๋ฐฐํฌ ํจํค์ง๊ฐ ํ์ํ ์ ์์
โข Mac M1/M2 ์นฉ ์ฌ์ฉ์๋ ์ถ๊ฐ ์ค์ ์ด ํ์ํ ์ ์์
๐ฌ Pillow๋ก ์์ํ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
Pillow๋ ์ ๋ง ์ง๊ด์ ์ด๊ณ ์ฌ์ฉํ๊ธฐ ์ฌ์. ๋ง์น ํฌํ ์ต์ ์ฝ๋๋ก ๋ค๋ฃจ๋ ๋๋์ด๋๊น? ๐ ๊ธฐ๋ณธ์ ์ธ ์์ ๋ถํฐ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐฐ์๋ณด์.
์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ์ ๊ธฐ๋ณธ ์ ๋ณด ํ์ธ
from PIL import Image
import os
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
img = Image.open('example.jpg')
# ์ด๋ฏธ์ง ๊ธฐ๋ณธ ์ ๋ณด ํ์ธ
print(f"์ด๋ฏธ์ง ํฌ๊ธฐ: {img.size}") # (width, height)
print(f"์ด๋ฏธ์ง ๋ชจ๋: {img.mode}") # RGB, RGBA, L ๋ฑ
print(f"์ด๋ฏธ์ง ํฌ๋งท: {img.format}") # JPEG, PNG ๋ฑ
# ์ด๋ฏธ์ง ํ์ (๊ธฐ๋ณธ ์ด๋ฏธ์ง ๋ทฐ์ด๋ก ์ด๋ฆผ)
img.show()
์ฌ๊ธฐ์ img.mode๋ ์ด๋ฏธ์ง์ ์์ ๋ชจ๋๋ฅผ ๋ํ๋ด. RGB๋ ์ผ๋ฐ์ ์ธ ์ปฌ๋ฌ ์ด๋ฏธ์ง, RGBA๋ ํฌ๋ช ๋๊ฐ ์๋ ์ด๋ฏธ์ง, L์ ํ๋ฐฑ ์ด๋ฏธ์ง๋ฅผ ์๋ฏธํด. ์ด ์ ๋ณด๋ ๋์ค์ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํ ๋ ์ ๋ง ์ค์ํ๋๊น ๊ผญ ๊ธฐ์ตํด๋ฌ!
์ด๋ฏธ์ง ํฌ๊ธฐ ์กฐ์ (๋ฆฌ์ฌ์ด์ง)
from PIL import Image
# ์๋ณธ ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
original = Image.open('photo.jpg')
# ๋ฐฉ๋ฒ 1: ํน์ ํฌ๊ธฐ๋ก ์กฐ์
resized = original.resize((800, 600))
# ๋ฐฉ๋ฒ 2: ๋น์จ ์ ์งํ๋ฉฐ ์กฐ์ (์ธ๋ค์ผ)
thumbnail = original.copy()
thumbnail.thumbnail((400, 400)) # ์ต๋ ํฌ๊ธฐ ์ง์
# ๋ฐฉ๋ฒ 3: ๋น์จ ๊ณ์ฐํด์ ์กฐ์
width, height = original.size
new_width = 1000
new_height = int(height * (new_width / width))
proportional = original.resize((new_width, new_height), Image.LANCZOS)
# ์ ์ฅํ๊ธฐ
resized.save('resized_photo.jpg', quality=95)
thumbnail.save('thumbnail.jpg', quality=90)
โข Image.LANCZOS: ์ต๊ณ ํ์ง, ์๋๋ ๋๋ฆผ (์ถ์ฒ!)
โข Image.BILINEAR: ์ค๊ฐ ํ์ง, ์ค๊ฐ ์๋
โข Image.NEAREST: ๋ฎ์ ํ์ง, ๋น ๋ฅธ ์๋
โข Image.BICUBIC: ์ข์ ํ์ง, ์ ๋นํ ์๋
์ผ๋ฐ์ ์ผ๋ก LANCZOS๋ฅผ ์ฌ์ฉํ๋ฉด ๊ฐ์ฅ ๊น๋ํ ๊ฒฐ๊ณผ๋ฅผ ์ป์ ์ ์์ด!
์ด๋ฏธ์ง ์๋ฅด๊ธฐ์ ํ์
from PIL import Image
img = Image.open('landscape.jpg')
# ์ด๋ฏธ์ง ์๋ฅด๊ธฐ (crop)
# ์ขํ: (left, top, right, bottom)
cropped = img.crop((100, 100, 500, 400))
# ์ด๋ฏธ์ง ํ์
rotated_90 = img.rotate(90, expand=True) # expand=True๋ก ์๋ฆผ ๋ฐฉ์ง
rotated_45 = img.rotate(45, fillcolor='white') # ๋น ๊ณต๊ฐ ์์ ์ง์
# ์ข์ฐ ๋ฐ์
flipped_lr = img.transpose(Image.FLIP_LEFT_RIGHT)
# ์ํ ๋ฐ์
flipped_tb = img.transpose(Image.FLIP_TOP_BOTTOM)
# ์ ์ฅ
cropped.save('cropped.jpg')
rotated_90.save('rotated.jpg')
flipped_lr.save('flipped.jpg')
์๋ฅด๊ธฐ ๊ธฐ๋ฅ์ ํนํ ์น์ฌ์ดํธ์์ ํ๋กํ ์ฌ์ง์ ์ ์ฌ๊ฐํ์ผ๋ก ๋ง๋ค ๋ ๋ง์ด ์ฌ์ฉํด. ์ฌ์ฉ์๊ฐ ์ ๋ก๋ํ ์ด๋ฏธ์ง์ ์ค์ ๋ถ๋ถ์ ์๋์ผ๋ก ์๋ผ๋ด๋ ๊ธฐ๋ฅ์ ๊ตฌํํ ์ ์์ง!
ํํฐ์ ํจ๊ณผ ์ ์ฉํ๊ธฐ
from PIL import Image, ImageFilter, ImageEnhance
img = Image.open('photo.jpg')
# ๋ธ๋ฌ ํจ๊ณผ
blurred = img.filter(ImageFilter.BLUR)
gaussian_blur = img.filter(ImageFilter.GaussianBlur(radius=5))
# ์คํ๋ (์ ๋ช
ํ๊ฒ)
sharpened = img.filter(ImageFilter.SHARPEN)
# ์ฃ์ง ๊ฒ์ถ
edges = img.filter(ImageFilter.FIND_EDGES)
# ์ ๋ณด์ฑ ํจ๊ณผ
embossed = img.filter(ImageFilter.EMBOSS)
# ๋ฐ๊ธฐ ์กฐ์
enhancer = ImageEnhance.Brightness(img)
brighter = enhancer.enhance(1.5) # 1.5๋ฐฐ ๋ฐ๊ฒ
darker = enhancer.enhance(0.7) # 0.7๋ฐฐ ์ด๋ก๊ฒ
# ๋๋น ์กฐ์
contrast_enhancer = ImageEnhance.Contrast(img)
high_contrast = contrast_enhancer.enhance(2.0)
# ์ฑ๋ ์กฐ์
color_enhancer = ImageEnhance.Color(img)
saturated = color_enhancer.enhance(1.5) # ์ฑ๋ ์ฆ๊ฐ
desaturated = color_enhancer.enhance(0.5) # ์ฑ๋ ๊ฐ์
# ํ๋ฐฑ ๋ณํ
grayscale = img.convert('L')
# ์ ์ฅ
blurred.save('blurred.jpg')
sharpened.save('sharpened.jpg')
grayscale.save('grayscale.jpg')
์ฌ๋ฌ ํจ๊ณผ๋ฅผ ์กฐํฉํ๋ฉด ์ธ์คํ๊ทธ๋จ ๊ฐ์ ํํฐ๋ฅผ ๋ง๋ค ์ ์์ด!
def vintage_filter(img):
# ์ฑ๋ ๋ฎ์ถ๊ธฐ
color = ImageEnhance.Color(img)
img = color.enhance(0.7)
# ๋๋น ๋์ด๊ธฐ
contrast = ImageEnhance.Contrast(img)
img = contrast.enhance(1.3)
# ์ฝ๊ฐ ์ด๋ก๊ฒ
brightness = ImageEnhance.Brightness(img)
img = brightness.enhance(0.9)
return img
# ์ฌ์ฉํ๊ธฐ
original = Image.open('photo.jpg')
vintage = vintage_filter(original)
vintage.save('vintage_photo.jpg')
ํ ์คํธ์ ๋ํ ๊ทธ๋ฆฌ๊ธฐ
from PIL import Image, ImageDraw, ImageFont
# ์ ์ด๋ฏธ์ง ์์ฑ ๋๋ ๊ธฐ์กด ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
img = Image.new('RGB', (800, 600), color='white')
# ๋๋: img = Image.open('background.jpg')
# Draw ๊ฐ์ฒด ์์ฑ
draw = ImageDraw.Draw(img)
# ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
draw.rectangle([(100, 100), (300, 200)], fill='lightblue', outline='blue', width=3)
# ์ ๊ทธ๋ฆฌ๊ธฐ
draw.ellipse([(400, 100), (600, 300)], fill='lightcoral', outline='red', width=3)
# ์ ๊ทธ๋ฆฌ๊ธฐ
draw.line([(100, 400), (700, 400)], fill='green', width=5)
# ํ
์คํธ ์ถ๊ฐ (๊ธฐ๋ณธ ํฐํธ)
draw.text((100, 450), "Hello, Pillow!", fill='black')
# ํ
์คํธ ์ถ๊ฐ (์ปค์คํ
ํฐํธ)
try:
font = ImageFont.truetype("arial.ttf", 40)
draw.text((100, 500), "Custom Font!", fill='navy', font=font)
except:
print("ํฐํธ ํ์ผ์ ์ฐพ์ ์ ์์ต๋๋ค. ๊ธฐ๋ณธ ํฐํธ๋ฅผ ์ฌ์ฉํฉ๋๋ค.")
draw.text((100, 500), "Custom Font!", fill='navy')
# ์ ์ฅ
img.save('drawing.jpg')
ํ ์คํธ ๊ธฐ๋ฅ์ ์ํฐ๋งํฌ๋ฅผ ์ถ๊ฐํ๊ฑฐ๋ ์ด๋ฏธ์ง์ ์ค๋ช ์ ๋ฃ์ ๋ ์ ๋ง ์ ์ฉํด. ์๋ฅผ ๋ค์ด, ์ฌ๋ฅ๋ท์์ ํฌํธํด๋ฆฌ์ค ์ด๋ฏธ์ง๋ฅผ ๋ง๋ค ๋ ์๋์ผ๋ก ๋ก๊ณ ๋ ์ ์๊ถ ํ์๋ฅผ ๋ฃ์ ์ ์์ง! ๐ฏ
๐ฌ OpenCV๋ก ๋ค์ด๊ฐ๋ ์ปดํจํฐ ๋น์ ์ ์ธ๊ณ
์, ์ด์ ๋ณธ๊ฒฉ์ ์ผ๋ก OpenCV์ ์ธ๊ณ๋ก ๋ค์ด๊ฐ๋ณด์! OpenCV๋ Pillow๋ณด๋ค ํจ์ฌ ๊ฐ๋ ฅํ๊ณ ๋ณต์กํ ๊ธฐ๋ฅ๋ค์ ์ ๊ณตํด. ์ฒ์์๋ ์ข ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ง๋ง, ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐ๋ผ์ค๋ฉด ๊ธ๋ฐฉ ์ต์ํด์ง ๊ฑฐ์ผ. ๐
OpenCV ๊ธฐ๋ณธ ์ฌ์ฉ๋ฒ
import cv2
import numpy as np
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
img = cv2.imread('image.jpg')
# ์ด๋ฏธ์ง๊ฐ ์ ๋๋ก ๋ก๋๋์๋์ง ํ์ธ
if img is None:
print("์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ฌ ์ ์์ต๋๋ค!")
else:
print(f"์ด๋ฏธ์ง ํฌ๊ธฐ: {img.shape}") # (height, width, channels)
# ์ด๋ฏธ์ง ํ์
cv2.imshow('Original Image', img)
cv2.waitKey(0) # ํค ์
๋ ฅ ๋๊ธฐ
cv2.destroyAllWindows() # ๋ชจ๋ ์๋์ฐ ๋ซ๊ธฐ
# ์ด๋ฏธ์ง ์ ์ฅ
cv2.imwrite('output.jpg', img)
OpenCV๋ ์ด๋ฏธ์ง๋ฅผ BGR ์์๋ก ์ฝ์ด์! ์ผ๋ฐ์ ์ธ RGB๊ฐ ์๋๋ผ Blue-Green-Red ์์์ผ. ์ด๊ฒ ์ฒ์์๋ ์ ๋ง ํท๊ฐ๋ฆฌ๋๋ฐ, ๋ค๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํจ๊ป ์ฌ์ฉํ ๋ ์์์ด ์ด์ํ๊ฒ ๋์ค๋ฉด ์ด๊ฒ ๋๋ฌธ์ผ ๊ฐ๋ฅ์ฑ์ด ๋์.
# BGR์ RGB๋ก ๋ณํ
rgb_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# RGB๋ฅผ BGR๋ก ๋ณํ
bgr_img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
์์ ๊ณต๊ฐ ๋ณํ
import cv2
img = cv2.imread('photo.jpg')
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# HSV ์์ ๊ณต๊ฐ์ผ๋ก ๋ณํ (์์ ๊ธฐ๋ฐ ์์
์ ์ ์ฉ)
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# LAB ์์ ๊ณต๊ฐ์ผ๋ก ๋ณํ (์กฐ๋ช
๋ณด์ ์ ์ ์ฉ)
lab = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
# ์ ์ฅ
cv2.imwrite('gray.jpg', gray)
cv2.imwrite('hsv.jpg', hsv)
์์ ๊ณต๊ฐ ๋ณํ์ ํน์ ์์ ์ ํ ๋ ์ ๋ง ์ค์ํด. ์๋ฅผ ๋ค์ด, ํน์ ์์์ ๋ฌผ์ฒด๋ฅผ ์ฐพ๊ณ ์ถ๋ค๋ฉด HSV ์์ ๊ณต๊ฐ์ด RGB๋ณด๋ค ํจ์ฌ ํจ๊ณผ์ ์ด์ผ. HSV๋ Hue(์์), Saturation(์ฑ๋), Value(๋ช ๋)๋ก ๊ตฌ์ฑ๋์ด ์์ด์ ์์์ ๋ ์ง๊ด์ ์ผ๋ก ๋ค๋ฃฐ ์ ์๊ฑฐ๋ !
์ด๋ฏธ์ง ํฌ๊ธฐ ์กฐ์ ๊ณผ ๋ณํ
import cv2
img = cv2.imread('image.jpg')
height, width = img.shape[:2]
# ๋ฐฉ๋ฒ 1: ํน์ ํฌ๊ธฐ๋ก ์กฐ์
resized = cv2.resize(img, (800, 600))
# ๋ฐฉ๋ฒ 2: ๋น์จ๋ก ์กฐ์
scale_percent = 50 # 50%๋ก ์ถ์
new_width = int(width * scale_percent / 100)
new_height = int(height * scale_percent / 100)
resized_percent = cv2.resize(img, (new_width, new_height))
# ๋ฐฉ๋ฒ 3: ๋ณด๊ฐ๋ฒ ์ง์
resized_inter = cv2.resize(img, (800, 600), interpolation=cv2.INTER_CUBIC)
# ํ์
center = (width // 2, height // 2)
rotation_matrix = cv2.getRotationMatrix2D(center, 45, 1.0) # 45๋ ํ์
rotated = cv2.warpAffine(img, rotation_matrix, (width, height))
# ์ข์ฐ ๋ฐ์
flipped_horizontal = cv2.flip(img, 1)
# ์ํ ๋ฐ์
flipped_vertical = cv2.flip(img, 0)
# ์ํ์ข์ฐ ๋ฐ์
flipped_both = cv2.flip(img, -1)
# ์ ์ฅ
cv2.imwrite('resized.jpg', resized)
cv2.imwrite('rotated.jpg', rotated)
โข cv2.INTER_NEAREST: ๊ฐ์ฅ ๋น ๋ฆ, ํ์ง ๋ฎ์
โข cv2.INTER_LINEAR: ๊ธฐ๋ณธ๊ฐ, ๋น ๋ฅด๊ณ ์ ๋นํ ํ์ง
โข cv2.INTER_CUBIC: ๋๋ฆฌ์ง๋ง ์ข์ ํ์ง (ํ๋ ์ ์ถ์ฒ)
โข cv2.INTER_LANCZOS4: ๊ฐ์ฅ ๋๋ฆฌ์ง๋ง ์ต๊ณ ํ์ง
โข cv2.INTER_AREA: ์ถ์ ์ ์ถ์ฒ, ๋ชจ์๋ ํจํด ๋ฐฉ์ง
์ด๋ฏธ์ง ํํฐ๋ง๊ณผ ๋ธ๋ฌ๋ง
import cv2
import numpy as np
img = cv2.imread('photo.jpg')
# ๊ฐ์ฐ์์ ๋ธ๋ฌ (์์ฐ์ค๋ฌ์ด ๋ธ๋ฌ)
gaussian = cv2.GaussianBlur(img, (15, 15), 0)
# ๋ฏธ๋์ธ ๋ธ๋ฌ (๋
ธ์ด์ฆ ์ ๊ฑฐ์ ํจ๊ณผ์ )
median = cv2.medianBlur(img, 5)
# ์๋ฐฉํฅ ํํฐ (๊ฒฝ๊ณ์ ๋ณด์กดํ๋ฉฐ ๋ธ๋ฌ)
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
# ์คํ๋ (์ ๋ช
ํ๊ฒ)
kernel_sharpen = np.array([[-1,-1,-1],
[-1, 9,-1],
[-1,-1,-1]])
sharpened = cv2.filter2D(img, -1, kernel_sharpen)
# ์ฃ์ง ๊ฒ์ถ - Canny
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
# ์ฃ์ง ๊ฒ์ถ - Sobel
sobelx = cv2.Sobel(gray, cv2.CV_64F, 1, 0, ksize=5)
sobely = cv2.Sobel(gray, cv2.CV_64F, 0, 1, ksize=5)
# ๋ชจํด๋ก์ง ์ฐ์ฐ (์นจ์๊ณผ ํฝ์ฐฝ)
kernel = np.ones((5,5), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)
dilation = cv2.dilate(img, kernel, iterations=1)
# ์ ์ฅ
cv2.imwrite('gaussian_blur.jpg', gaussian)
cv2.imwrite('edges.jpg', edges)
cv2.imwrite('sharpened.jpg', sharpened)
ํํฐ๋ง์ ์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ํต์ฌ์ด์ผ! ๊ฐ ํํฐ๋ ํน์ ๋ชฉ์ ์ ์ต์ ํ๋์ด ์์ด. ์๋ฅผ ๋ค์ด, ๊ฐ์ฐ์์ ๋ธ๋ฌ๋ ๋ฐฐ๊ฒฝ์ ํ๋ฆฌ๊ฒ ํด์ ํผ์ฌ์ฒด๋ฅผ ๊ฐ์กฐํ ๋ ์ข๊ณ , Canny ์ฃ์ง ๊ฒ์ถ์ ๋ฌผ์ฒด์ ์ค๊ณฝ์ ์ฐพ์ ๋ ์ฌ์ฉํด. ์ค์ ๋ก ์์จ์ฃผํ ์๋์ฐจ์์ ์ฐจ์ ์ ์ธ์ํ ๋๋ ์ฃ์ง ๊ฒ์ถ์ ์ฌ์ฉํ๋ค๊ณ ! ๐
๐ค ์ผ๊ตด ์ธ์: OpenCV์ ๊ฝ
์, ์ด์ ์ ๋ง ์ฌ๋ฏธ์๋ ๋ถ๋ถ์ด์ผ! ์ผ๊ตด ์ธ์์ OpenCV์ ๊ฐ์ฅ ์ธ๊ธฐ ์๋ ๊ธฐ๋ฅ ์ค ํ๋์ผ. Haar Cascade๋ผ๋ ์ฌ์ ํ์ต๋ ๋ชจ๋ธ์ ์ฌ์ฉํ๋ฉด ๋๋๋๋ก ์ฝ๊ฒ ์ผ๊ตด์ ์ธ์ํ ์ ์์ด. ๐ฎ
๊ธฐ๋ณธ ์ผ๊ตด ์ธ์
import cv2
# Haar Cascade ๋ถ๋ฅ๊ธฐ ๋ก๋
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
eye_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_eye.xml')
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
img = cv2.imread('people.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ์ผ๊ตด ๊ฒ์ถ
faces = face_cascade.detectMultiScale(
gray,
scaleFactor=1.1, # ์ด๋ฏธ์ง ํผ๋ผ๋ฏธ๋ ์ค์ผ์ผ
minNeighbors=5, # ์ต์ ์ด์ ์ (๋์์๋ก ์ ํํ์ง๋ง ๊ฒ์ถ๋ฅ ๋ฎ์)
minSize=(30, 30) # ์ต์ ์ผ๊ตด ํฌ๊ธฐ
)
print(f"๊ฒ์ถ๋ ์ผ๊ตด ์: {len(faces)}")
# ๊ฒ์ถ๋ ์ผ๊ตด์ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
for (x, y, w, h) in faces:
# ์ผ๊ตด ์์ญ์ ์ฌ๊ฐํ ๊ทธ๋ฆฌ๊ธฐ
cv2.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
# ์ผ๊ตด ์์ญ์์ ๋ ๊ฒ์ถ
roi_gray = gray[y:y+h, x:x+w]
roi_color = img[y:y+h, x:x+w]
eyes = eye_cascade.detectMultiScale(roi_gray)
for (ex, ey, ew, eh) in eyes:
cv2.rectangle(roi_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2)
# ๊ฒฐ๊ณผ ์ ์ฅ ๋ฐ ํ์
cv2.imwrite('detected_faces.jpg', img)
cv2.imshow('Face Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
scaleFactor: 1.1 ~ 1.4 ์ฌ์ด ๊ฐ ์ถ์ฒ. ์์์๋ก ์ ํํ์ง๋ง ๋๋ฆผ
minNeighbors: 3 ~ 6 ์ฌ์ด ๊ฐ ์ถ์ฒ. ๋์์๋ก ์ค๊ฒ์ถ ๊ฐ์
minSize: ๊ฒ์ถํ๋ ค๋ ์ผ๊ตด์ ์ต์ ํฌ๊ธฐ. ๋๋ฌด ์์ผ๋ฉด ์ค๊ฒ์ถ ์ฆ๊ฐ
์ค์ ํ๋ก์ ํธ์์๋ ์ฌ๋ฌ ๊ฐ์ ํ ์คํธํด๋ณด๊ณ ์ต์ ์ ์กฐํฉ์ ์ฐพ์์ผ ํด!
์ค์๊ฐ ์น์บ ์ผ๊ตด ์ธ์
import cv2
# ์น์บ ์ด๊ธฐ
cap = cv2.VideoCapture(0)
# Haar Cascade ๋ก๋
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
while True:
# ํ๋ ์ ์ฝ๊ธฐ
ret, frame = cap.read()
if not ret:
print("์น์บ ์ ์ฝ์ ์ ์์ต๋๋ค.")
break
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
# ์ผ๊ตด ๊ฒ์ถ
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# ๊ฒ์ถ๋ ์ผ๊ตด ํ์
for (x, y, w, h) in faces:
cv2.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
cv2.putText(frame, 'Face', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (255, 0, 0), 2)
# ํ๋ ์ ํ์
cv2.imshow('Face Detection', frame)
# 'q' ํค๋ฅผ ๋๋ฅด๋ฉด ์ข
๋ฃ
if cv2.waitKey(1) & 0xFF == ord('q'):
break
# ๋ฆฌ์์ค ํด์
cap.release()
cv2.destroyAllWindows()
์ค์๊ฐ ์ผ๊ตด ์ธ์์ ์ ๋ง ์ ๊ธฐํ์ง? ์ด ์ฝ๋๋ฅผ ์คํํ๋ฉด ์น์บ ์ ๋น์น ์ผ๊ตด์ ์ค์๊ฐ์ผ๋ก ์ธ์ํด์ ์ฌ๊ฐํ์ผ๋ก ํ์ํด์ค. ํ์ํ์ ์ฑ์ด๋ ๋ณด์ ์์คํ ์์ ์ด๋ฐ ๊ธฐ์ ์ ์ฌ์ฉํ๊ณ ์์ด! ๐ฅ
์ผ๊ตด์ ๋ชจ์์ดํฌ ์ฒ๋ฆฌํ๊ธฐ
import cv2
def pixelate_face(image, blocks=10):
"""์ผ๊ตด ์์ญ์ ๋ชจ์์ดํฌ ์ฒ๋ฆฌํ๋ ํจ์"""
# ์ด๋ฏธ์ง ํฌ๊ธฐ ๊ฐ์ ธ์ค๊ธฐ
(h, w) = image.shape[:2]
# ์์๋ก ์๊ฒ ์ถ์
temp = cv2.resize(image, (blocks, blocks), interpolation=cv2.INTER_LINEAR)
# ๋ค์ ์๋ ํฌ๊ธฐ๋ก ํ๋ (๋ชจ์์ดํฌ ํจ๊ณผ)
output = cv2.resize(temp, (w, h), interpolation=cv2.INTER_NEAREST)
return output
# ์ด๋ฏธ์ง ๋ถ๋ฌ์ค๊ธฐ
img = cv2.imread('person.jpg')
face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ์ผ๊ตด ๊ฒ์ถ
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# ๊ฐ ์ผ๊ตด์ ๋ชจ์์ดํฌ ์ ์ฉ
for (x, y, w, h) in faces:
# ์ผ๊ตด ์์ญ ์ถ์ถ
face_region = img[y:y+h, x:x+w]
# ๋ชจ์์ดํฌ ์ฒ๋ฆฌ
pixelated = pixelate_face(face_region, blocks=10)
# ์๋ณธ ์ด๋ฏธ์ง์ ์ ์ฉ
img[y:y+h, x:x+w] = pixelated
# ๊ฒฐ๊ณผ ์ ์ฅ
cv2.imwrite('pixelated_faces.jpg', img)
๊ฐ์ธ์ ๋ณด ๋ณดํธ๊ฐ ์ค์ํ ์์ฆ, ์ด๋ฐ ๋ชจ์์ดํฌ ๊ธฐ๋ฅ์ ์ ๋ง ์ ์ฉํด. ์ ํ๋ธ ์์์ด๋ ๋ธ๋ก๊ทธ ์ฌ์ง์์ ์ผ๊ตด์ ๊ฐ๋ฆด ๋ ์๋์ผ๋ก ์ฒ๋ฆฌํ ์ ์์ง. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ํฌํธํด๋ฆฌ์ค๋ฅผ ์ฌ๋ฆด ๋๋ ๊ณ ๊ฐ์ ์ผ๊ตด์ ๋ณดํธํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ด!
๐ฏ ๊ฐ์ฒด ํ์ง์ ์ถ์
์ผ๊ตด ์ธ์์ ๋ฐฐ์ ์ผ๋, ์ด์ ๋ ๋์๊ฐ์ ๋ค์ํ ๊ฐ์ฒด๋ฅผ ํ์งํ๊ณ ์ถ์ ํ๋ ๋ฐฉ๋ฒ์ ์์๋ณด์! ์ด๊ฑด ์์จ์ฃผํ, ๋ณด์ ์์คํ , ์คํฌ์ธ ๋ถ์ ๋ฑ ์ ๋ง ๋ง์ ๋ถ์ผ์์ ์ฌ์ฉ๋๋ ๊ธฐ์ ์ด์ผ. ๐ฏ
์์ ๊ธฐ๋ฐ ๊ฐ์ฒด ํ์ง
import cv2
import numpy as np
# ์ด๋ฏธ์ง ๋๋ ๋น๋์ค ๋ถ๋ฌ์ค๊ธฐ
cap = cv2.VideoCapture(0) # ์น์บ ์ฌ์ฉ
# ๋๋: img = cv2.imread('image.jpg')
while True:
ret, frame = cap.read()
if not ret:
break
# HSV ์์ ๊ณต๊ฐ์ผ๋ก ๋ณํ
hsv = cv2.cvtColor(frame, cv2.COLOR_BGR2HSV)
# ๋นจ๊ฐ์ ๋ฒ์ ์ ์ (HSV)
# ๋นจ๊ฐ์์ 0๋์ 180๋ ๊ทผ์ฒ์ ์์ด์ ๋ ๋ฒ์๋ก ๋๋
lower_red1 = np.array([0, 100, 100])
upper_red1 = np.array([10, 255, 255])
lower_red2 = np.array([160, 100, 100])
upper_red2 = np.array([180, 255, 255])
# ๋ง์คํฌ ์์ฑ
mask1 = cv2.inRange(hsv, lower_red1, upper_red1)
mask2 = cv2.inRange(hsv, lower_red2, upper_red2)
mask = mask1 + mask2
# ๋
ธ์ด์ฆ ์ ๊ฑฐ
kernel = np.ones((5, 5), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel)
mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
# ์ค๊ณฝ์ ์ฐพ๊ธฐ
contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# ๊ฐ์ฅ ํฐ ์ค๊ณฝ์ ์ฐพ๊ธฐ
if contours:
largest_contour = max(contours, key=cv2.contourArea)
# ๋ฉด์ ์ด ์ถฉ๋ถํ ํฐ ๊ฒฝ์ฐ๋ง ์ฒ๋ฆฌ
if cv2.contourArea(largest_contour) > 500:
# ๋ฐ์ด๋ฉ ๋ฐ์ค ๊ทธ๋ฆฌ๊ธฐ
x, y, w, h = cv2.boundingRect(largest_contour)
cv2.rectangle(frame, (x, y), (x+w, y+h), (0, 255, 0), 2)
# ์ค์ฌ์ ํ์
cx = x + w // 2
cy = y + h // 2
cv2.circle(frame, (cx, cy), 5, (255, 0, 0), -1)
# ํ
์คํธ ํ์
cv2.putText(frame, 'Red Object', (x, y-10),
cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
# ๊ฒฐ๊ณผ ํ์
cv2.imshow('Original', frame)
cv2.imshow('Mask', mask)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
๋นจ๊ฐ์: [0-10, 100-255, 100-255] ๋๋ [160-180, 100-255, 100-255]
์ฃผํฉ์: [10-25, 100-255, 100-255]
๋ ธ๋์: [25-35, 100-255, 100-255]
์ด๋ก์: [35-85, 100-255, 100-255]
ํ๋์: [85-125, 100-255, 100-255]
๋ณด๋ผ์: [125-160, 100-255, 100-255]
์ค์ ๋ก๋ ์กฐ๋ช ์กฐ๊ฑด์ ๋ฐ๋ผ ์กฐ์ ์ด ํ์ํด!
์ค๊ณฝ์ ๊ฒ์ถ๊ณผ ๋ํ ์ธ์
import cv2
import numpy as np
img = cv2.imread('shapes.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# ์ด์งํ
_, thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY)
# ์ค๊ณฝ์ ์ฐพ๊ธฐ
contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
def detect_shape(contour):
"""์ค๊ณฝ์ ์ผ๋ก๋ถํฐ ๋ํ ์ข
๋ฅ ํ๋ณ"""
# ์ค๊ณฝ์ ๊ทผ์ฌํ
epsilon = 0.04 * cv2.arcLength(contour, True)
approx = cv2.approxPolyDP(contour, epsilon, True)
# ๊ผญ์ง์ ๊ฐ์๋ก ๋ํ ํ๋ณ
vertices = len(approx)
if vertices == 3:
return "Triangle"
elif vertices == 4:
# ์ฌ๊ฐํ์ธ์ง ์ ์ฌ๊ฐํ์ธ์ง ๊ตฌ๋ถ
x, y, w, h = cv2.boundingRect(approx)
aspect_ratio = float(w) / h
if 0.95 <= aspect_ratio <= 1.05:
return "Square"
else:
return "Rectangle"
elif vertices == 5:
return "Pentagon"
elif vertices > 5:
# ์ํ๋ ๊ฒ์ฌ
area = cv2.contourArea(contour)
perimeter = cv2.arcLength(contour, True)
circularity = 4 * np.pi * area / (perimeter * perimeter)
if circularity > 0.8:
return "Circle"
else:
return "Polygon"
return "Unknown"
# ๊ฐ ์ค๊ณฝ์ ๋ถ์
for contour in contours:
# ๋๋ฌด ์์ ์ค๊ณฝ์ ์ ๋ฌด์
if cv2.contourArea(contour) < 100:
continue
# ๋ํ ์ข
๋ฅ ํ๋ณ
shape = detect_shape(contour)
# ์ค๊ณฝ์ ๊ทธ๋ฆฌ๊ธฐ
cv2.drawContours(img, [contour], -1, (0, 255, 0), 2)
# ์ค์ฌ์ ๊ณ์ฐ
M = cv2.moments(contour)
if M["m00"] != 0:
cx = int(M["m10"] / M["m00"])
cy = int(M["m01"] / M["m00"])
# ๋ํ ์ด๋ฆ ํ์
cv2.putText(img, shape, (cx-50, cy),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
cv2.imwrite('detected_shapes.jpg', img)
cv2.imshow('Shape Detection', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
๋ํ ์ธ์์ ์๊ฐ๋ณด๋ค ๋ง์ ๊ณณ์ ์ฌ์ฉ๋ผ. ์๋ฅผ ๋ค์ด, ๊ณต์ฅ์์ ์ ํ์ ๋ถ๋์ ๊ฒ์ฌํ๊ฑฐ๋, ๋ฌธ์์์ ํน์ ๊ธฐํธ๋ฅผ ์ฐพ๊ฑฐ๋, ๊ฒ์์์ ๋ฌผ์ฒด๋ฅผ ์ธ์ํ๋ ๋ฐ ํ์ฉํ ์ ์์ด. ์ ๋ง ์ค์ฉ์ ์ด์ง? ๐
๋ชจ์ ๊ฐ์ง (์์ง์ ํ์ง)
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
# ์ฒซ ๋ฒ์งธ ํ๋ ์ ์ฝ๊ธฐ
ret, frame1 = cap.read()
ret, frame2 = cap.read()
while True:
# ๋ ํ๋ ์์ ์ฐจ์ด ๊ณ์ฐ
diff = cv2.absdiff(frame1, frame2)
gray = cv2.cvtColor(diff, cv2.COLOR_BGR2GRAY)
# ๋ธ๋ฌ ์ ์ฉ (๋
ธ์ด์ฆ ๊ฐ์)
blur = cv2.GaussianBlur(gray, (5, 5), 0)
# ์ด์งํ
_, thresh = cv2.threshold(blur, 20, 255, cv2.THRESH_BINARY)
# ํฝ์ฐฝ (์์ ๊ตฌ๋ฉ ๋ฉ์ฐ๊ธฐ)
dilated = cv2.dilate(thresh, None, iterations=3)
# ์ค๊ณฝ์ ์ฐพ๊ธฐ
contours, _ = cv2.findContours(dilated, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
# ์์ง์์ด ๊ฐ์ง๋ ์์ญ ํ์
for contour in contours:
if cv2.contourArea(contour) < 1000: # ์์ ์์ง์ ๋ฌด์
continue
x, y, w, h = cv2.boundingRect(contour)
cv2.rectangle(frame1, (x, y), (x+w, y+h), (0, 255, 0), 2)
cv2.putText(frame1, "Motion Detected", (10, 30),
cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
# ๊ฒฐ๊ณผ ํ์
cv2.imshow('Motion Detection', frame1)
cv2.imshow('Threshold', thresh)
# ํ๋ ์ ์
๋ฐ์ดํธ
frame1 = frame2
ret, frame2 = cap.read()
if cv2.waitKey(30) & 0xFF == ord('q'):
break
cap.release()
cv2.destroyAllWindows()
๋ชจ์ ๊ฐ์ง๋ ๋ณด์ ์นด๋ฉ๋ผ์ ํต์ฌ ๊ธฐ๋ฅ์ด์ผ! ์์ง์์ด ์์ ๋๋ง ๋ นํ๋ฅผ ์์ํ๊ฑฐ๋ ์๋ฆผ์ ๋ณด๋ผ ์ ์์ด. ์ง์ CCTV๋ฅผ ์ค์นํ๋ค๋ฉด ์ด๋ฐ ๊ธฐ๋ฅ์ ์ง์ ๊ตฌํํด๋ณผ ์ ์๊ฒ ์ง? ๐ ๐
๐จ ์ค์ ํ๋ก์ ํธ: ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์ ํ๋ฆฌ์ผ์ด์ ๋ง๋ค๊ธฐ
์ด์ ๊น์ง ๋ฐฐ์ด ๋ด์ฉ์ ์ข ํฉํด์ ์ค์ ๋ก ์ฌ์ฉํ ์ ์๋ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค์ด๋ณด์! ์ฌ๊ธฐ์๋ ๋ช ๊ฐ์ง ์ค์ฉ์ ์ธ ์์ ๋ฅผ ์๊ฐํ ๊ฒ.
ํ๋ก์ ํธ 1: ์๋ ๋ฐฐ๊ฒฝ ์ ๊ฑฐ ๋๊ตฌ
import cv2
import numpy as np
from PIL import Image
def remove_background(image_path, output_path):
"""๋ฐฐ๊ฒฝ์ ์๋์ผ๋ก ์ ๊ฑฐํ๋ ํจ์"""
# ์ด๋ฏธ์ง ์ฝ๊ธฐ
img = cv2.imread(image_path)
# GrabCut ์๊ณ ๋ฆฌ์ฆ์ ์ํ ๋ง์คํฌ ์์ฑ
mask = np.zeros(img.shape[:2], np.uint8)
# ๋ฐฐ๊ฒฝ๊ณผ ์ ๊ฒฝ ๋ชจ๋ธ
bgd_model = np.zeros((1, 65), np.float64)
fgd_model = np.zeros((1, 65), np.float64)
# ๊ด์ฌ ์์ญ ์ค์ (์ด๋ฏธ์ง ์ค์ 80%)
height, width = img.shape[:2]
rect = (int(width*0.1), int(height*0.1),
int(width*0.8), int(height*0.8))
# GrabCut ์คํ
cv2.grabCut(img, mask, rect, bgd_model, fgd_model, 5, cv2.GC_INIT_WITH_RECT)
# ๋ง์คํฌ ์์ (ํ์คํ ์ ๊ฒฝ๊ณผ ๊ฐ๋ฅํ ์ ๊ฒฝ๋ง ๋จ๊น)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
# ๋ง์คํฌ ์ ์ฉ
result = img * mask2[:, :, np.newaxis]
# ๋ฐฐ๊ฒฝ์ ํฐ์์ผ๋ก
white_background = np.ones_like(img) * 255
white_background = white_background * (1 - mask2[:, :, np.newaxis])
result = result + white_background
# ์ ์ฅ
cv2.imwrite(output_path, result)
# Pillow๋ก PNG ํฌ๋ช
๋ฐฐ๊ฒฝ ๋ฒ์ ๋ ์์ฑ
result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(result_rgb)
# ํฐ์์ ํฌ๋ช
ํ๊ฒ
pil_img = pil_img.convert("RGBA")
datas = pil_img.getdata()
new_data = []
for item in datas:
# ํฐ์ ํฝ์
์ ํฌ๋ช
ํ๊ฒ
if item[0] > 240 and item[1] > 240 and item[2] > 240:
new_data.append((255, 255, 255, 0))
else:
new_data.append(item)
pil_img.putdata(new_data)
pil_img.save(output_path.replace('.jpg', '_transparent.png'))
print(f"๋ฐฐ๊ฒฝ ์ ๊ฑฐ ์๋ฃ! {output_path}์ ์ ์ฅ๋์์ต๋๋ค.")
# ์ฌ์ฉ ์์
remove_background('person.jpg', 'person_no_bg.jpg')
๋ฐฐ๊ฒฝ ์ ๊ฑฐ ๊ธฐ๋ฅ์ ์ ์์๊ฑฐ๋, ํ๋กํ ์ฌ์ง ํธ์ง, ๋์์ธ ์์ ๋ฑ์์ ์ ๋ง ์ ์ฉํด. ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ด๋ฐ ๊ธฐ๋ฅ์ ์๋น์ค๋ก ์ ๊ณตํ ์๋ ์์ด! ํนํ ์จ๋ผ์ธ ์ผํ๋ชฐ์์ ์ ํ ์ฌ์ง์ ๋ฐฐ๊ฒฝ์ ์ผ๊ด ์ ๊ฑฐํ ๋ ์๊ฐ์ ์์ฒญ ์ ์ฝํ ์ ์์ง.
ํ๋ก์ ํธ 2: ๋ฌธ์ ์ค์บ๋
import cv2
import numpy as np
def order_points(pts):
"""๋ค ์ ์ ์ข์, ์ฐ์, ์ฐํ, ์ขํ ์์๋ก ์ ๋ ฌ"""
rect = np.zeros((4, 2), dtype="float32")
# ์ข์๊ณผ ์ฐํ ์ฐพ๊ธฐ
s = pts.sum(axis=1)
rect[0] = pts[np.argmin(s)]
rect[2] = pts[np.argmax(s)]
# ์ฐ์๊ณผ ์ขํ ์ฐพ๊ธฐ
diff = np.diff(pts, axis=1)
rect[1] = pts[np.argmin(diff)]
rect[3] = pts[np.argmax(diff)]
return rect
def four_point_transform(image, pts):
"""ํฌ์ ๋ณํ์ผ๋ก ๋ฌธ์๋ฅผ ์ ๋ฉด์์ ๋ณธ ๊ฒ์ฒ๋ผ ๋ณํ"""
rect = order_points(pts)
(tl, tr, br, bl) = rect
# ์ ์ด๋ฏธ์ง์ ๋๋น ๊ณ์ฐ
width_a = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
width_b = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
max_width = max(int(width_a), int(width_b))
# ์ ์ด๋ฏธ์ง์ ๋์ด ๊ณ์ฐ
height_a = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
height_b = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
max_height = max(int(height_a), int(height_b))
# ๋ชฉํ ์ ๋ค
dst = np.array([
[0, 0],
[max_width - 1, 0],
[max_width - 1, max_height - 1],
[0, max_height - 1]], dtype="float32")
# ํฌ์ ๋ณํ ํ๋ ฌ ๊ณ์ฐ ๋ฐ ์ ์ฉ
M = cv2.getPerspectiveTransform(rect, dst)
warped = cv2.warpPerspective(image, M, (max_width, max_height))
return warped
def scan_document(image_path, output_path):
"""๋ฌธ์๋ฅผ ์ค์บํ๋ ํจ์"""
# ์ด๋ฏธ์ง ์ฝ๊ธฐ
image = cv2.imread(image_path)
orig = image.copy()
# ํฌ๊ธฐ ์กฐ์ (์ฒ๋ฆฌ ์๋ ํฅ์)
ratio = image.shape[0] / 500.0
image = cv2.resize(image, (int(image.shape[1] / ratio), 500))
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# ๊ฐ์ฐ์์ ๋ธ๋ฌ
gray = cv2.GaussianBlur(gray, (5, 5), 0)
# ์ฃ์ง ๊ฒ์ถ
edged = cv2.Canny(gray, 75, 200)
# ์ค๊ณฝ์ ์ฐพ๊ธฐ
contours, _ = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
contours = sorted(contours, key=cv2.contourArea, reverse=True)[:5]
# ์ฌ๊ฐํ ์ค๊ณฝ์ ์ฐพ๊ธฐ
screen_cnt = None
for c in contours:
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.02 * peri, True)
if len(approx) == 4:
screen_cnt = approx
break
if screen_cnt is None:
print("๋ฌธ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค!")
return
# ์๋ณธ ์ด๋ฏธ์ง ํฌ๊ธฐ๋ก ์ขํ ๋ณํ
screen_cnt = screen_cnt.reshape(4, 2) * ratio
# ํฌ์ ๋ณํ ์ ์ฉ
warped = four_point_transform(orig, screen_cnt)
# ๊ทธ๋ ์ด์ค์ผ์ผ ๋ณํ ๋ฐ ์ด์งํ (์ค์บ ํจ๊ณผ)
warped = cv2.cvtColor(warped, cv2.COLOR_BGR2GRAY)
warped = cv2.adaptiveThreshold(warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 11, 2)
# ์ ์ฅ
cv2.imwrite(output_path, warped)
print(f"๋ฌธ์ ์ค์บ ์๋ฃ! {output_path}์ ์ ์ฅ๋์์ต๋๋ค.")
return warped
# ์ฌ์ฉ ์์
scanned = scan_document('document_photo.jpg', 'scanned_document.jpg')
๋ฌธ์ ์ค์บ๋๋ ์ ๋ง ์ค์ฉ์ ์ธ ํ๋ก์ ํธ์ผ! ์ค๋งํธํฐ์ผ๋ก ์ฐ์ ๋ฌธ์ ์ฌ์ง์ ๊น๋ํ ์ค์บ ์ด๋ฏธ์ง๋ก ๋ณํํ ์ ์์ด. ๊ฐ๋๊ฐ ํ์ด์ง ์ฌ์ง๋ ์๋์ผ๋ก ๋ณด์ ํด์ฃผ๋๊น ์ ๋ง ํธ๋ฆฌํ์ง? ๐โจ
ํ๋ก์ ํธ 3: ์ค์๊ฐ ํํฐ ์นด๋ฉ๋ผ
import cv2
import numpy as np
class FilterCamera:
def __init__(self):
self.cap = cv2.VideoCapture(0)
self.current_filter = 'none'
def apply_filter(self, frame, filter_name):
"""๋ค์ํ ํํฐ ์ ์ฉ"""
if filter_name == 'none':
return frame
elif filter_name == 'grayscale':
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
return cv2.cvtColor(gray, cv2.COLOR_GRAY2BGR)
elif filter_name == 'sepia':
kernel = np.array([[0.272, 0.534, 0.131],
[0.349, 0.686, 0.168],
[0.393, 0.769, 0.189]])
return cv2.transform(frame, kernel)
elif filter_name == 'negative':
return cv2.bitwise_not(frame)
elif filter_name == 'blur':
return cv2.GaussianBlur(frame, (15, 15), 0)
elif filter_name == 'sharpen':
kernel = np.array([[-1,-1,-1],
[-1, 9,-1],
[-1,-1,-1]])
return cv2.filter2D(frame, -1, kernel)
elif filter_name == 'edge':
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray, 100, 200)
return cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
elif filter_name == 'cartoon':
# ์นดํฐ ํจ๊ณผ
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.medianBlur(gray, 5)
edges = cv2.adaptiveThreshold(gray, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 9, 9)
color = cv2.bilateralFilter(frame, 9, 300, 300)
cartoon = cv2.bitwise_and(color, color, mask=edges)
return cartoon
elif filter_name == 'vintage':
# ๋นํฐ์ง ํจ๊ณผ
kernel = np.array([[0.393, 0.769, 0.189],
[0.349, 0.686, 0.168],
[0.272, 0.534, 0.131]])
vintage = cv2.transform(frame, kernel)
vintage = cv2.addWeighted(vintage, 0.8, frame, 0.2, 0)
return vintage
return frame
def run(self):
"""์นด๋ฉ๋ผ ์คํ"""
filters = ['none', 'grayscale', 'sepia', 'negative', 'blur',
'sharpen', 'edge', 'cartoon', 'vintage']
filter_index = 0
print("ํํฐ ์นด๋ฉ๋ผ ์์!")
print("์คํ์ด์ค๋ฐ: ํํฐ ๋ณ๊ฒฝ")
print("s: ์ฌ์ง ์ ์ฅ")
print("q: ์ข
๋ฃ")
while True:
ret, frame = self.cap.read()
if not ret:
break
# ํํฐ ์ ์ฉ
filtered = self.apply_filter(frame, filters[filter_index])
# ํ์ฌ ํํฐ ์ด๋ฆ ํ์
cv2.putText(filtered, f"Filter: {filters[filter_index]}",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX,
1, (0, 255, 0), 2)
# ํ๋ฉด ํ์
cv2.imshow('Filter Camera', filtered)
# ํค ์
๋ ฅ ์ฒ๋ฆฌ
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord(' '): # ์คํ์ด์ค๋ฐ
filter_index = (filter_index + 1) % len(filters)
print(f"ํํฐ ๋ณ๊ฒฝ: {filters[filter_index]}")
elif key == ord('s'):
filename = f'photo_{filters[filter_index]}.jpg'
cv2.imwrite(filename, filtered)
print(f"์ฌ์ง ์ ์ฅ: {filename}")
self.cap.release()
cv2.destroyAllWindows()
# ์ฌ์ฉ ์์
camera = FilterCamera()
camera.run()
์ด ํํฐ ์นด๋ฉ๋ผ๋ ์ธ์คํ๊ทธ๋จ ๊ฐ์ ์ฑ์ ๊ธฐ๋ณธ ์๋ฆฌ๋ฅผ ๋ณด์ฌ์ค! ์ค์๊ฐ์ผ๋ก ๋ค์ํ ํํฐ๋ฅผ ์ ์ฉํ๊ณ ์ฌ์ง์ ์ ์ฅํ ์ ์์ด. ์ด๊ฑธ ๊ธฐ๋ฐ์ผ๋ก ๋ ๋ณต์กํ ํจ๊ณผ๋ค์ ์ถ๊ฐํ ์๋ ์์ง. ์ ๋ง ์ฌ๋ฏธ์๋ ํ๋ก์ ํธ์ผ! ๐ธโจ
โก ์ฑ๋ฅ ์ต์ ํ์ ์ค๋ฌด ํ
์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ ๊ณ์ฐ๋์ด ๋ง์์ ์ฑ๋ฅ ์ต์ ํ๊ฐ ์ ๋ง ์ค์ํด. ํนํ ์ค์๊ฐ ์ฒ๋ฆฌ๋ ๋์ฉ๋ ์ด๋ฏธ์ง๋ฅผ ๋ค๋ฃฐ ๋๋ ๋์ฑ ๊ทธ๋ ์ง. ์ค๋ฌด์์ ๋ฐ๋ก ์ธ ์ ์๋ ์ต์ ํ ๊ธฐ๋ฒ๋ค์ ์๋ ค์ค๊ฒ! ๐
์ด๋ฏธ์ง ํฌ๊ธฐ ์ต์ ํ
import cv2
import time
def process_with_resize(image_path, target_width=800):
"""์ด๋ฏธ์ง ํฌ๊ธฐ๋ฅผ ์ค์ฌ์ ์ฒ๋ฆฌ ์๋ ํฅ์"""
# ์๋ณธ ์ด๋ฏธ์ง ์ฝ๊ธฐ
img = cv2.imread(image_path)
original_height, original_width = img.shape[:2]
# ๋น์จ ๊ณ์ฐ
ratio = original_width / target_width
target_height = int(original_height / ratio)
# ์์ ์๊ฐ
start_time = time.time()
# ํฌ๊ธฐ ์กฐ์
resized = cv2.resize(img, (target_width, target_height))
# ์ฒ๋ฆฌ (์: ์ผ๊ตด ์ธ์)
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
gray = cv2.cvtColor(resized, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# ์๋ณธ ํฌ๊ธฐ๋ก ์ขํ ๋ณํ
for (x, y, w, h) in faces:
x_orig = int(x * ratio)
y_orig = int(y * ratio)
w_orig = int(w * ratio)
h_orig = int(h * ratio)
cv2.rectangle(img, (x_orig, y_orig),
(x_orig+w_orig, y_orig+h_orig), (255, 0, 0), 2)
# ์ฒ๋ฆฌ ์๊ฐ ๊ณ์ฐ
elapsed_time = time.time() - start_time
print(f"์ฒ๋ฆฌ ์๊ฐ: {elapsed_time:.2f}์ด")
return img
# ์ฌ์ฉ ์์
result = process_with_resize('large_image.jpg', target_width=800)
cv2.imwrite('result.jpg', result)
โข ์ผ๊ตด ์ธ์: 800-1000px ๋๋น๋ฉด ์ถฉ๋ถ
โข ๊ฐ์ฒด ํ์ง: 640-800px ๋๋น ๊ถ์ฅ
โข ํ ์คํธ ์ธ์: ์๋ณธ ํฌ๊ธฐ ์ ์ง ๋๋ ํ๋
โข ์ค์๊ฐ ์ฒ๋ฆฌ: 320-640px๋ก ์ถ์
์ฒ๋ฆฌ ํ ์๋ณธ ํฌ๊ธฐ๋ก ๊ฒฐ๊ณผ๋ฅผ ๋ณํํ๋ ๊ฒ์ ์์ง ๋ง!
๋ฉํฐ์ค๋ ๋ฉ์ผ๋ก ๋ฐฐ์น ์ฒ๋ฆฌ
import cv2
import os
from concurrent.futures import ThreadPoolExecutor, as_completed
from PIL import Image
def process_single_image(input_path, output_folder, operation='resize'):
"""๋จ์ผ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ํจ์"""
try:
filename = os.path.basename(input_path)
output_path = os.path.join(output_folder, filename)
if operation == 'resize':
# Pillow๋ก ๋ฆฌ์ฌ์ด์ง
img = Image.open(input_path)
img.thumbnail((800, 800))
img.save(output_path, quality=85)
elif operation == 'grayscale':
# OpenCV๋ก ํ๋ฐฑ ๋ณํ
img = cv2.imread(input_path)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
cv2.imwrite(output_path, gray)
elif operation == 'watermark':
# ์ํฐ๋งํฌ ์ถ๊ฐ
img = Image.open(input_path)
from PIL import ImageDraw, ImageFont
draw = ImageDraw.Draw(img)
# ์ํฐ๋งํฌ ํ
์คํธ
text = "ยฉ Your Name"
try:
font = ImageFont.truetype("arial.ttf", 30)
except:
font = ImageFont.load_default()
# ์ฐ์ธก ํ๋จ์ ๋ฐฐ์น
width, height = img.size
draw.text((width-200, height-50), text,
fill=(255, 255, 255, 128), font=font)
img.save(output_path, quality=90)
return f"์๋ฃ: {filename}"
except Exception as e:
return f"์ค๋ฅ: {filename} - {str(e)}"
def batch_process_images(input_folder, output_folder, operation='resize', max_workers=4):
"""์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ๋ณ๋ ฌ๋ก ์ฒ๋ฆฌ"""
# ์ถ๋ ฅ ํด๋ ์์ฑ
os.makedirs(output_folder, exist_ok=True)
# ์ด๋ฏธ์ง ํ์ผ ๋ชฉ๋ก
image_extensions = ['.jpg', '.jpeg', '.png', '.bmp']
image_files = [
os.path.join(input_folder, f)
for f in os.listdir(input_folder)
if os.path.splitext(f)[1].lower() in image_extensions
]
print(f"์ด {len(image_files)}๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค...")
# ๋ฉํฐ์ค๋ ๋ฉ์ผ๋ก ์ฒ๋ฆฌ
with ThreadPoolExecutor(max_workers=max_workers) as executor:
# ์์
์ ์ถ
futures = {
executor.submit(process_single_image, img_path, output_folder, operation): img_path
for img_path in image_files
}
# ์๋ฃ๋ ์์
์ฒ๋ฆฌ
completed = 0
for future in as_completed(futures):
result = future.result()
completed += 1
print(f"[{completed}/{len(image_files)}] {result}")
print("๋ชจ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์๋ฃ!")
# ์ฌ์ฉ ์์
batch_process_images(
input_folder='input_images',
output_folder='output_images',
operation='resize',
max_workers=4 # CPU ์ฝ์ด ์์ ๋ง๊ฒ ์กฐ์
)
๋ฉํฐ์ค๋ ๋ฉ์ ์ฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ๋์์ ์ฒ๋ฆฌํ ๋ ์ ๋ง ํจ๊ณผ์ ์ด์ผ! 4๊ฐ์ ์์ปค๋ฅผ ์ฌ์ฉํ๋ฉด ์ฒ๋ฆฌ ์๋๊ฐ ๊ฑฐ์ 4๋ฐฐ ๊ฐ๊น์ด ๋นจ๋ผ์ง ์ ์์ด. ๋๋์ ์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํด์ผ ํ๋ ์ ์์๊ฑฐ๋ ์ฌ์ดํธ๋ ์ฌ์ง ํธ์ง ์๋น์ค์์ ํ์์ ์ธ ๊ธฐ์ ์ด์ง. ๐ช
๋ฉ๋ชจ๋ฆฌ ํจ์จ์ ์ธ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
import cv2
import numpy as np
from PIL import Image
import gc
def memory_efficient_processing(image_paths, output_folder):
"""๋ฉ๋ชจ๋ฆฌ๋ฅผ ํจ์จ์ ์ผ๋ก ์ฌ์ฉํ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ"""
for i, img_path in enumerate(image_paths):
try:
# ์ด๋ฏธ์ง ์ฝ๊ธฐ
img = cv2.imread(img_path)
if img is None:
print(f"์ด๋ฏธ์ง๋ฅผ ์ฝ์ ์ ์์ต๋๋ค: {img_path}")
continue
# ์ฒ๋ฆฌ ์์
# 1. ํฌ๊ธฐ ์กฐ์
height, width = img.shape[:2]
if width > 1920:
scale = 1920 / width
new_width = 1920
new_height = int(height * scale)
img = cv2.resize(img, (new_width, new_height))
# 2. ํํฐ ์ ์ฉ
img = cv2.GaussianBlur(img, (5, 5), 0)
# 3. ์ ์ฅ
output_path = f"{output_folder}/processed_{i}.jpg"
cv2.imwrite(output_path, img, [cv2.IMWRITE_JPEG_QUALITY, 85])
# ๋ฉ๋ชจ๋ฆฌ ํด์
del img
# ์ฃผ๊ธฐ์ ์ผ๋ก ๊ฐ๋น์ง ์ปฌ๋ ์
์คํ
if i % 10 == 0:
gc.collect()
print(f"์ฒ๋ฆฌ ์๋ฃ: {i+1}/{len(image_paths)}")
except Exception as e:
print(f"์ค๋ฅ ๋ฐ์: {img_path} - {str(e)}")
continue
def process_large_image_in_chunks(image_path, output_path, chunk_size=1000):
"""๋์ฉ๋ ์ด๋ฏธ์ง๋ฅผ ์ฒญํฌ ๋จ์๋ก ์ฒ๋ฆฌ"""
# ์ด๋ฏธ์ง ์ ๋ณด ์ฝ๊ธฐ
img = cv2.imread(image_path)
height, width = img.shape[:2]
# ๊ฒฐ๊ณผ ์ด๋ฏธ์ง ์์ฑ
result = np.zeros_like(img)
# ์ฒญํฌ ๋จ์๋ก ์ฒ๋ฆฌ
for y in range(0, height, chunk_size):
for x in range(0, width, chunk_size):
# ์ฒญํฌ ์์ญ ๊ณ์ฐ
y_end = min(y + chunk_size, height)
x_end = min(x + chunk_size, width)
# ์ฒญํฌ ์ถ์ถ
chunk = img[y:y_end, x:x_end]
# ์ฒ๋ฆฌ (์: ์คํ๋)
kernel = np.array([[-1,-1,-1], [-1,9,-1], [-1,-1,-1]])
processed_chunk = cv2.filter2D(chunk, -1, kernel)
# ๊ฒฐ๊ณผ์ ์ ์ฅ
result[y:y_end, x:x_end] = processed_chunk
print(f"์ฒ๋ฆฌ ์ค: {y}/{height}, {x}/{width}")
# ์ ์ฅ
cv2.imwrite(output_path, result)
print("๋์ฉ๋ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์๋ฃ!")
# ์ฌ์ฉ ์์
image_list = ['img1.jpg', 'img2.jpg', 'img3.jpg']
memory_efficient_processing(image_list, 'output')
1. ์ด๋ฏธ์ง๋ฅผ ํ๋์ฉ ์ฒ๋ฆฌ: ๋ชจ๋ ์ด๋ฏธ์ง๋ฅผ ํ ๋ฒ์ ๋ฉ๋ชจ๋ฆฌ์ ๋ก๋ํ์ง ๋ง ๊ฒ
2. ์ฌ์ฉ ํ ์ฆ์ ํด์ : del ๋ช ๋ น์ด๋ก ๋ณ์ ์ญ์
3. ๊ฐ๋น์ง ์ปฌ๋ ์ : gc.collect()๋ก ์ฃผ๊ธฐ์ ์ผ๋ก ๋ฉ๋ชจ๋ฆฌ ์ ๋ฆฌ
4. ์ ์ ํ ์ด๋ฏธ์ง ํฌ๊ธฐ: ํ์ ์ด์์ผ๋ก ํฐ ์ด๋ฏธ์ง๋ ์ถ์
5. ์ฒญํฌ ์ฒ๋ฆฌ: ๋์ฉ๋ ์ด๋ฏธ์ง๋ ๋ถ๋ถ์ ์ผ๋ก ์ฒ๋ฆฌ
6. ์์ถ ์ ์ฅ: JPEG ํ์ง์ 85-90์ผ๋ก ์ค์ ํ์ฌ ์ฉ๋ ์ ์ฝ
์บ์ฑ์ผ๋ก ๋ฐ๋ณต ์์ ์ต์ ํ
import cv2
import pickle
import hashlib
import os
class ImageCache:
"""์ด๋ฏธ์ง ์ฒ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ์บ์ฑํ๋ ํด๋์ค"""
def __init__(self, cache_dir='cache'):
self.cache_dir = cache_dir
os.makedirs(cache_dir, exist_ok=True)
def get_cache_key(self, image_path, operation, params):
"""์บ์ ํค ์์ฑ"""
# ํ์ผ ๋ด์ฉ ๊ธฐ๋ฐ ํด์
with open(image_path, 'rb') as f:
file_hash = hashlib.md5(f.read()).hexdigest()
# ์์
๊ณผ ํ๋ผ๋ฏธํฐ๋ฅผ ํฌํจํ ํค
key_string = f"{file_hash}_{operation}_{str(params)}"
return hashlib.md5(key_string.encode()).hexdigest()
def get(self, image_path, operation, params):
"""์บ์์์ ๊ฒฐ๊ณผ ๊ฐ์ ธ์ค๊ธฐ"""
cache_key = self.get_cache_key(image_path, operation, params)
cache_file = os.path.join(self.cache_dir, f"{cache_key}.pkl")
if os.path.exists(cache_file):
with open(cache_file, 'rb') as f:
return pickle.load(f)
return None
def set(self, image_path, operation, params, result):
"""๊ฒฐ๊ณผ๋ฅผ ์บ์์ ์ ์ฅ"""
cache_key = self.get_cache_key(image_path, operation, params)
cache_file = os.path.join(self.cache_dir, f"{cache_key}.pkl")
with open(cache_file, 'wb') as f:
pickle.dump(result, f)
def clear(self):
"""์บ์ ์ ์ฒด ์ญ์ """
for file in os.listdir(self.cache_dir):
os.remove(os.path.join(self.cache_dir, file))
print("์บ์๊ฐ ์ญ์ ๋์์ต๋๋ค.")
def detect_faces_with_cache(image_path, cache=None):
"""์บ์ฑ์ ์ฌ์ฉํ ์ผ๊ตด ์ธ์"""
operation = 'face_detection'
params = {'scale': 1.3, 'neighbors': 5}
# ์บ์ ํ์ธ
if cache:
cached_result = cache.get(image_path, operation, params)
if cached_result is not None:
print("์บ์์์ ๊ฒฐ๊ณผ๋ฅผ ๊ฐ์ ธ์์ต๋๋ค!")
return cached_result
# ์ค์ ์ฒ๋ฆฌ
print("์ด๋ฏธ์ง๋ฅผ ์ฒ๋ฆฌํ๊ณ ์์ต๋๋ค...")
img = cv2.imread(image_path)
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# ๊ฒฐ๊ณผ๋ฅผ ์บ์์ ์ ์ฅ
if cache:
cache.set(image_path, operation, params, faces)
return faces
# ์ฌ์ฉ ์์
cache = ImageCache()
# ์ฒซ ๋ฒ์งธ ํธ์ถ (์ค์ ์ฒ๋ฆฌ)
faces1 = detect_faces_with_cache('photo.jpg', cache)
print(f"๊ฒ์ถ๋ ์ผ๊ตด: {len(faces1)}๊ฐ")
# ๋ ๋ฒ์งธ ํธ์ถ (์บ์์์ ๊ฐ์ ธ์ด)
faces2 = detect_faces_with_cache('photo.jpg', cache)
print(f"๊ฒ์ถ๋ ์ผ๊ตด: {len(faces2)}๊ฐ")
์บ์ฑ์ ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ๋ฐ๋ณตํด์ ์ฒ๋ฆฌํ ๋ ์์ฒญ๋ ์๊ฐ์ ์ ์ฝํด์ค! ํนํ ์น ์๋น์ค์์ ์ฌ์ฉ์๊ฐ ๊ฐ์ ์ด๋ฏธ์ง๋ฅผ ์ฌ๋ฌ ๋ฒ ์์ฒญํ ๋ ์ ์ฉํ์ง. ์ฒซ ๋ฒ์งธ๋ ์ฒ๋ฆฌํ๋ ๋ฐ ์๊ฐ์ด ๊ฑธ๋ฆฌ์ง๋ง, ๋ ๋ฒ์งธ๋ถํฐ๋ ๊ฑฐ์ ์ฆ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ ์ ์์ด. โก
๐ง ๋ฌธ์ ํด๊ฒฐ๊ณผ ๋๋ฒ๊น
์ด๋ฏธ์ง ์ฒ๋ฆฌ๋ฅผ ํ๋ค ๋ณด๋ฉด ์ฌ๋ฌ ๊ฐ์ง ๋ฌธ์ ์ ๋ถ๋ชํ๊ฒ ๋ผ. ์ฌ๊ธฐ์๋ ์์ฃผ ๋ฐ์ํ๋ ๋ฌธ์ ๋ค๊ณผ ํด๊ฒฐ ๋ฐฉ๋ฒ์ ์์๋ณผ๊ฒ!
์ผ๋ฐ์ ์ธ ์ค๋ฅ์ ํด๊ฒฐ์ฑ
1. ์ด๋ฏธ์ง๋ฅผ ๋ถ๋ฌ์ฌ ์ ์๋ ๊ฒฝ์ฐ
import cv2
import os
def safe_imread(image_path):
"""์์ ํ๊ฒ ์ด๋ฏธ์ง ์ฝ๊ธฐ"""
# ํ์ผ ์กด์ฌ ํ์ธ
if not os.path.exists(image_path):
print(f"ํ์ผ์ด ์กด์ฌํ์ง ์์ต๋๋ค: {image_path}")
return None
# ์ด๋ฏธ์ง ์ฝ๊ธฐ
img = cv2.imread(image_path)
if img is None:
# ๋ค๋ฅธ ๋ฐฉ๋ฒ์ผ๋ก ์๋
try:
from PIL import Image
import numpy as np
pil_img = Image.open(image_path)
img = cv2.cvtColor(np.array(pil_img), cv2.COLOR_RGB2BGR)
print("Pillow๋ก ์ด๋ฏธ์ง๋ฅผ ์ฝ์์ต๋๋ค.")
except Exception as e:
print(f"์ด๋ฏธ์ง๋ฅผ ์ฝ์ ์ ์์ต๋๋ค: {e}")
return None
return img
# ์ฌ์ฉ ์์
img = safe_imread('photo.jpg')
if img is not None:
print(f"์ด๋ฏธ์ง ํฌ๊ธฐ: {img.shape}")
2. ํ๊ธ ๊ฒฝ๋ก ๋ฌธ์
import cv2
import numpy as np
def imread_korean(filename):
"""ํ๊ธ ๊ฒฝ๋ก๋ฅผ ์ฒ๋ฆฌํ ์ ์๋ imread"""
try:
# numpy๋ก ํ์ผ ์ฝ๊ธฐ
stream = open(filename, "rb")
bytes_data = bytearray(stream.read())
numpy_array = np.asarray(bytes_data, dtype=np.uint8)
# OpenCV๋ก ๋์ฝ๋ฉ
img = cv2.imdecode(numpy_array, cv2.IMREAD_COLOR)
return img
except Exception as e:
print(f"์ค๋ฅ: {e}")
return None
def imwrite_korean(filename, img):
"""ํ๊ธ ๊ฒฝ๋ก๋ก ์ด๋ฏธ์ง ์ ์ฅ"""
try:
# ์ด๋ฏธ์ง ์ธ์ฝ๋ฉ
extension = filename.split('.')[-1]
result, encoded_img = cv2.imencode(f'.{extension}', img)
if result:
# ํ์ผ๋ก ์ ์ฅ
with open(filename, mode='w+b') as f:
encoded_img.tofile(f)
return True
return False
except Exception as e:
print(f"์ค๋ฅ: {e}")
return False
# ์ฌ์ฉ ์์
img = imread_korean('ํ๊ธ๊ฒฝ๋ก/์ฌ์ง.jpg')
if img is not None:
imwrite_korean('ํ๊ธ๊ฒฝ๋ก/๊ฒฐ๊ณผ.jpg', img)
3. ์์ ๊ณต๊ฐ ๋ณํ ์ค๋ฅ
import cv2
def safe_color_convert(img, conversion):
"""์์ ํ ์์ ๊ณต๊ฐ ๋ณํ"""
try:
# ์ด๋ฏธ์ง๊ฐ None์ธ์ง ํ์ธ
if img is None:
print("์ด๋ฏธ์ง๊ฐ None์
๋๋ค.")
return None
# ์ด๋ฏธ์ง ์ฐจ์ ํ์ธ
if len(img.shape) < 2:
print("์๋ชป๋ ์ด๋ฏธ์ง ํ์์
๋๋ค.")
return None
# ์ด๋ฏธ ๊ทธ๋ ์ด์ค์ผ์ผ์ธ ๊ฒฝ์ฐ
if len(img.shape) == 2 and conversion == cv2.COLOR_BGR2GRAY:
print("์ด๋ฏธ ๊ทธ๋ ์ด์ค์ผ์ผ ์ด๋ฏธ์ง์
๋๋ค.")
return img
# ๋ณํ ์ํ
converted = cv2.cvtColor(img, conversion)
return converted
except cv2.error as e:
print(f"์์ ๋ณํ ์ค๋ฅ: {e}")
return None
# ์ฌ์ฉ ์์
img = cv2.imread('photo.jpg')
gray = safe_color_convert(img, cv2.COLOR_BGR2GRAY)
- โ ํ์ผ ๊ฒฝ๋ก๊ฐ ์ฌ๋ฐ๋ฅธ์ง ํ์ธ
- โ ์ด๋ฏธ์ง ํ์ผ์ด ์์๋์ง ์์๋์ง ํ์ธ
- โ ์ด๋ฏธ์ง ํฌ๊ธฐ์ ํ์ ํ์ธ (img.shape)
- โ ์์ ์ฑ๋ ์์ ํ์ธ (BGR vs RGB)
- โ ๋ฐ์ดํฐ ํ์ ํ์ธ (uint8, float32 ๋ฑ)
- โ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ ์ฌ๋ถ ํ์ธ
- โ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ฒ์ ํธํ์ฑ ํ์ธ
์ฑ๋ฅ ํ๋กํ์ผ๋ง
import cv2
import time
import cProfile
import pstats
def profile_function(func):
"""ํจ์ ์คํ ์๊ฐ ์ธก์ ๋ฐ์ฝ๋ ์ดํฐ"""
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} ์คํ ์๊ฐ: {end_time - start_time:.4f}์ด")
return result
return wrapper
@profile_function
def process_image_method1(img):
"""๋ฐฉ๋ฒ 1: ๊ธฐ๋ณธ ์ฒ๋ฆฌ"""
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
return edges
@profile_function
def process_image_method2(img):
"""๋ฐฉ๋ฒ 2: ์ต์ ํ๋ ์ฒ๋ฆฌ"""
# ํฌ๊ธฐ ์ถ์
small = cv2.resize(img, None, fx=0.5, fy=0.5)
gray = cv2.cvtColor(small, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
edges = cv2.Canny(blurred, 50, 150)
# ์๋ ํฌ๊ธฐ๋ก ๋ณต์
edges = cv2.resize(edges, (img.shape[1], img.shape[0]))
return edges
def detailed_profiling():
"""์์ธํ ํ๋กํ์ผ๋ง"""
img = cv2.imread('large_image.jpg')
# cProfile ์ฌ์ฉ
profiler = cProfile.Profile()
profiler.enable()
# ์ฒ๋ฆฌ ์คํ
result = process_image_method1(img)
profiler.disable()
# ๊ฒฐ๊ณผ ์ถ๋ ฅ
stats = pstats.Stats(profiler)
stats.sort_stats('cumulative')
stats.print_stats(10) # ์์ 10๊ฐ ํจ์
# ์ฌ์ฉ ์์
img = cv2.imread('test.jpg')
result1 = process_image_method1(img)
result2 = process_image_method2(img)
# ์์ธ ํ๋กํ์ผ๋ง
detailed_profiling()
๐ ์ค๋ฌด ํ์ฉ ์ฌ๋ก์ ์์ตํ
์ด์ ๊น์ง ๋ฐฐ์ด ๊ธฐ์ ๋ค์ ์ค์ ๋ก ์ด๋ป๊ฒ ํ์ฉํ๊ณ ์์ตํํ ์ ์๋์ง ์์๋ณด์! ์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์์ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ ๊ฒ๋ ์ข์ ๋ฐฉ๋ฒ์ด์ผ. ๐ฐ
์์ตํ ์์ด๋์ด
๐ธ ์ฌ์ง ํธ์ง ์๋น์ค
์ ๊ณต ์๋น์ค:โข ๋ฐฐ๊ฒฝ ์ ๊ฑฐ (๊ฑด๋น 5,000์~)
โข ์๋ณด์ ๋ฐ ๋ฆฌํฐ์นญ
โข ์ผ๊ด ๋ฆฌ์ฌ์ด์ง
โข ์ํฐ๋งํฌ ์ถ๊ฐ
โข ํํฐ ์ ์ฉ
ํ๊ฒ ๊ณ ๊ฐ:
์จ๋ผ์ธ ์ผํ๋ชฐ, ๋ธ๋ก๊ฑฐ, ์ธํ๋ฃจ์ธ์
๐ข ๊ธฐ์ ์ฉ ์๋ฃจ์
์ ๊ณต ์๋น์ค:โข ํ์ง ๊ฒ์ฌ ์์คํ
โข ์ผ๊ตด ์ธ์ ์ถ์ ๊ด๋ฆฌ
โข ๋ฌธ์ ์๋ํ ์ฒ๋ฆฌ
โข ์ฌ๊ณ ๊ด๋ฆฌ ์์คํ
โข ๋ณด์ ๋ชจ๋ํฐ๋ง
ํ๊ฒ ๊ณ ๊ฐ:
์ ์กฐ์ ์ฒด, ๋ฌผ๋ฅ์ผํฐ, ์คํผ์ค
ํฌํธํด๋ฆฌ์ค ํ๋ก์ ํธ ์์
"""
์์ฑ๋ ๋์ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ์น ์๋น์ค ์์
Flask๋ฅผ ์ฌ์ฉํ ๊ฐ๋จํ API ์๋ฒ
"""
from flask import Flask, request, send_file, jsonify
import cv2
import numpy as np
from PIL import Image
import io
import base64
app = Flask(__name__)
@app.route('/api/remove-background', methods=['POST'])
def remove_background_api():
"""๋ฐฐ๊ฒฝ ์ ๊ฑฐ API"""
try:
# ์ด๋ฏธ์ง ๋ฐ๊ธฐ
file = request.files['image']
img = Image.open(file.stream)
img_array = np.array(img)
# OpenCV๋ก ๋ณํ
img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
# ๋ฐฐ๊ฒฝ ์ ๊ฑฐ ์ฒ๋ฆฌ
mask = np.zeros(img_bgr.shape[:2], np.uint8)
bgd_model = np.zeros((1, 65), np.float64)
fgd_model = np.zeros((1, 65), np.float64)
height, width = img_bgr.shape[:2]
rect = (int(width*0.1), int(height*0.1),
int(width*0.8), int(height*0.8))
cv2.grabCut(img_bgr, mask, rect, bgd_model, fgd_model,
5, cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask == 2) | (mask == 0), 0, 1).astype('uint8')
result = img_bgr * mask2[:, :, np.newaxis]
# ๊ฒฐ๊ณผ๋ฅผ ์ด๋ฏธ์ง๋ก ๋ณํ
result_rgb = cv2.cvtColor(result, cv2.COLOR_BGR2RGB)
result_img = Image.fromarray(result_rgb)
# ๋ฐ์ดํธ๋ก ๋ณํ
img_io = io.BytesIO()
result_img.save(img_io, 'PNG')
img_io.seek(0)
return send_file(img_io, mimetype='image/png')
except Exception as e:
return jsonify({'error': str(e)}), 400
@app.route('/api/detect-faces', methods=['POST'])
def detect_faces_api():
"""์ผ๊ตด ์ธ์ API"""
try:
file = request.files['image']
img = Image.open(file.stream)
img_array = np.array(img)
img_bgr = cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR)
# ์ผ๊ตด ๊ฒ์ถ
face_cascade = cv2.CascadeClassifier(
cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
)
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5)
# ๊ฒฐ๊ณผ ๋ฐํ
faces_list = [
{'x': int(x), 'y': int(y), 'width': int(w), 'height': int(h)}
for (x, y, w, h) in faces
]
return jsonify({
'face_count': len(faces),
'faces': faces_list
})
except Exception as e:
return jsonify({'error': str(e)}), 400
@app.route('/api/apply-filter', methods=['POST'])
def apply_filter_api():
"""ํํฐ ์ ์ฉ API"""
try:
file = request.files['image']
filter_type = request.form.get('filter', 'none')
img = Image.open(file.stream)
if filter_type == 'grayscale':
img = img.convert('L').convert('RGB')
elif filter_type == 'sepia':
img_array = np.array(img)
kernel = np.array([[0.393, 0.769, 0.189],
[0.349, 0.686, 0.168],
[0.272, 0.534, 0.131]])
img_array = cv2.transform(img_array, kernel)
img = Image.fromarray(np.uint8(img_array))
# ๊ฒฐ๊ณผ ๋ฐํ
img_io = io.BytesIO()
img.save(img_io, 'JPEG', quality=90)
img_io.seek(0)
return send_file(img_io, mimetype='image/jpeg')
except Exception as e:
return jsonify({'error': str(e)}), 400
if __name__ == '__main__':
app.run(debug=True, host='0.0.0.0', port=5000)
์ด๋ฐ API ์๋ฒ๋ฅผ ๋ง๋ค๋ฉด ์น์ฌ์ดํธ๋ ๋ชจ๋ฐ์ผ ์ฑ์์ ์ฝ๊ฒ ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์์ด! ์ฌ๋ฅ๋ท์์ ์ด๋ฐ ์๋น์ค๋ฅผ ์ ๊ณตํ๋ฉด ๋ง์ ์์๊ฐ ์์ ๊ฑฐ์ผ. ํนํ ์๊ท๋ชจ ์ผํ๋ชฐ์ด๋ ์คํํธ์ ์์ ํ์๋ก ํ๋ ๊ธฐ๋ฅ๋ค์ด๊ฑฐ๋ . ๐
๊ธฐ๋ณธ ์๋น์ค:
โข ๋ฐฐ๊ฒฝ ์ ๊ฑฐ: ์ด๋ฏธ์ง๋น 3,000~10,000์
โข ๋ฆฌ์ฌ์ด์ง/ํฌ๋งท ๋ณํ: ์ด๋ฏธ์ง๋น 1,000~3,000์
โข ํํฐ ์ ์ฉ: ์ด๋ฏธ์ง๋น 2,000~5,000์
โข ์ผ๊ด ์ฒ๋ฆฌ: 100์ฅ ๊ธฐ์ค 50,000~200,000์
๊ณ ๊ธ ์๋น์ค:
โข ์ปค์คํ ํํฐ ๊ฐ๋ฐ: ํ๋ก์ ํธ๋น 300,000~1,000,000์
โข ์ผ๊ตด ์ธ์ ์์คํ : ํ๋ก์ ํธ๋น 500,000~3,000,000์
โข ํ์ง ๊ฒ์ฌ ์์คํ : ํ๋ก์ ํธ๋น 1,000,000~5,000,000์
โข ์ ์ง๋ณด์: ์ 100,000~500,000์
๐ ํ์ต ๋ก๋๋งต๊ณผ ์ถ๊ฐ ์๋ฃ
์ปดํจํฐ ๋น์ ์ ์ ๋ง ๋๊ณ ๊น์ ๋ถ์ผ์ผ. ์ฌ๊ธฐ์ ๋ฐฐ์ด ๋ด์ฉ์ ์์์ ๋ถ๊ณผํด! ๋ ๊น์ด ๊ณต๋ถํ๊ณ ์ถ๋ค๋ฉด ๋ค์ ๋ก๋๋งต์ ๋ฐ๋ผ๊ฐ๋ณด์. ๐
์ด๊ธ ๋จ๊ณ (1-2๊ฐ์)
โ
Python ๊ธฐ์ด ๋ฌธ๋ฒ ์๋ฒฝํ ์ตํ๊ธฐ
โ
Pillow๋ก ๊ธฐ๋ณธ ์ด๋ฏธ์ง ์กฐ์ ์ฐ์ต
โ
OpenCV ์ค์น ๋ฐ ๊ธฐ๋ณธ ํจ์ ์ตํ๊ธฐ
โ
๊ฐ๋จํ ํํฐ ํจ๊ณผ ๊ตฌํ
โ
์ด๋ฏธ์ง ํ์ผ ์
์ถ๋ ฅ ๋ง์คํฐ
์ถ์ฒ ํ๋ก์ ํธ: ์ธ๋ค์ผ ์์ฑ๊ธฐ, ์ํฐ๋งํฌ ์ถ๊ฐ ๋๊ตฌ
์ค๊ธ ๋จ๊ณ (2-4๊ฐ์)
โ
์์ ๊ณต๊ฐ ๋ณํ ์ดํดํ๊ธฐ
โ
ํํฐ๋ง๊ณผ ์ปจ๋ณผ๋ฃจ์
์ฐ์ฐ
โ
์ค๊ณฝ์ ๊ฒ์ถ๊ณผ ๋ํ ์ธ์
โ
Haar Cascade๋ก ์ผ๊ตด ์ธ์
โ
์ค์๊ฐ ๋น๋์ค ์ฒ๋ฆฌ
โ
์ฑ๋ฅ ์ต์ ํ ๊ธฐ๋ฒ
์ถ์ฒ ํ๋ก์ ํธ: ๋ฌธ์ ์ค์บ๋, ๋ชจ์
๊ฐ์ง CCTV, ํํฐ ์นด๋ฉ๋ผ
๊ณ ๊ธ ๋จ๊ณ (4-6๊ฐ์)
โ
๋ฅ๋ฌ๋ ๊ธฐ๋ฐ ๊ฐ์ฒด ํ์ง (YOLO, SSD)
โ
์ผ๊ตด ๋๋๋งํฌ ๊ฒ์ถ
โ
์ด๋ฏธ์ง ์ธ๊ทธ๋ฉํ
์ด์
โ
OCR (๊ดํ ๋ฌธ์ ์ธ์)
โ
3D ์ฌ๊ตฌ์ฑ
โ
์ค์๊ฐ ์ถ์ ์๊ณ ๋ฆฌ์ฆ
์ถ์ฒ ํ๋ก์ ํธ: ์์จ์ฃผํ ์๋ฎฌ๋ ์ดํฐ, AR ํํฐ, ์ค๋งํธ ๋ณด์ ์์คํ
์ ๋ฌธ๊ฐ ๋จ๊ณ (6๊ฐ์ ์ด์)
โ
TensorFlow/PyTorch๋ก ์ปค์คํ
๋ชจ๋ธ ๊ฐ๋ฐ
โ
GAN์ผ๋ก ์ด๋ฏธ์ง ์์ฑ
โ
์๋ฃ ์์ ๋ถ์
โ
์์ฑ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
โ
์ค์๊ฐ ๋์ฉ๋ ๋น๋์ค ์ฒ๋ฆฌ
โ
์ฃ์ง ๋๋ฐ์ด์ค ์ต์ ํ
์ถ์ฒ ํ๋ก์ ํธ: AI ๊ธฐ๋ฐ ์ฌ์ง ํธ์ง ์ฑ, ์๋ฃ ์ง๋จ ๋ณด์กฐ ์์คํ
์ ์ฉํ ํ์ต ์๋ฃ
โข OpenCV ๊ณต์ ๋ฌธ์: docs.opencv.org
โข Pillow ๊ณต์ ๋ฌธ์: pillow.readthedocs.io
โข NumPy ๊ณต์ ๋ฌธ์: numpy.org/doc
๐ฅ ์จ๋ผ์ธ ๊ฐ์:
โข Coursera - Computer Vision Specialization
โข Udemy - OpenCV Python ์์ ์ ๋ณต
โข YouTube - PyImageSearch ์ฑ๋
๐ ์ถ์ฒ ๋์:
โข "Learning OpenCV 4" - Adrian Kaehler
โข "Programming Computer Vision with Python" - Jan Erik Solem
โข "Practical Python and OpenCV" - Adrian Rosebrock
๐ป ์ค์ต ํ๋ซํผ:
โข Kaggle - ์ด๋ฏธ์ง ์ฒ๋ฆฌ ๋ํ ์ฐธ์ฌ
โข GitHub - ์คํ์์ค ํ๋ก์ ํธ ๊ธฐ์ฌ
โข ์ฌ๋ฅ๋ท - ์ค์ ํ๋ก์ ํธ ์์ฃผ ๋ฐ ๊ฒฝํ ์๊ธฐ
๐ฏ ๋ง๋ฌด๋ฆฌํ๋ฉฐ
์! ์ ๋ง ๊ธด ์ฌ์ ์ด์์ง? ๐ ์ฐ๋ฆฌ๋ Pillow์ OpenCV์ ๊ธฐ์ด๋ถํฐ ์ค์ ํ๋ก์ ํธ, ์ฑ๋ฅ ์ต์ ํ, ๊ทธ๋ฆฌ๊ณ ์์ตํ ๋ฐฉ๋ฒ๊น์ง ์ ๋ง ๋ง์ ๋ด์ฉ์ ๋ค๋ค์ด.
์ด๋ฏธ์ง ์ฒ๋ฆฌ์ ์ปดํจํฐ ๋น์ ์ ์ ๋ง ๋งค๋ ฅ์ ์ธ ๋ถ์ผ์ผ. ์ฐ๋ฆฌ๊ฐ ๋งค์ผ ์ฌ์ฉํ๋ ์ค๋งํธํฐ ์นด๋ฉ๋ผ, ์์
๋ฏธ๋์ด ํํฐ, ์์จ์ฃผํ ์๋์ฐจ, ์๋ฃ ์ง๋จ ์์คํ
๋ฑ ๋ชจ๋ ๊ณณ์ ์ด ๊ธฐ์ ์ด ์ฌ์ฉ๋๊ณ ์์ด. ๊ทธ๋ฆฌ๊ณ ์์ผ๋ก๋ ๊ณ์ ๋ฐ์ ํ ๊ฑฐ์ผ!
์ฒ์์๋ ์ด๋ ต๊ฒ ๋๊ปด์ง ์ ์์ง๋ง, ํ๋์ฉ ์ฐจ๊ทผ์ฐจ๊ทผ ๋ฐ๋ผํ๋ค ๋ณด๋ฉด ์ด๋์ ๋ฉ์ง ํ๋ก์ ํธ๋ฅผ ๋ง๋ค๊ณ ์๋ ์์ ์ ๋ฐ๊ฒฌํ๊ฒ ๋ ๊ฑฐ์ผ. ์คํจ๋ฅผ ๋๋ ค์ํ์ง ๋ง๊ณ , ๋ง์ด ์คํํ๊ณ ์ฐ์ตํด๋ด. ๊ทธ๊ฒ ๊ฐ์ฅ ๋น ๋ฅธ ํ์ต ๋ฐฉ๋ฒ์ด๊ฑฐ๋ ! ๐ช
์ฌ๋ฅ๋ท ๊ฐ์ ํ๋ซํผ์ ํ์ฉํ๋ฉด ๋ฐฐ์ด ๊ธฐ์ ์ ์ค์ ํ๋ก์ ํธ์ ์ ์ฉํ๋ฉด์ ์์ต๋ ์ฐฝ์ถํ ์ ์์ด. ์์ ํ๋ก์ ํธ๋ถํฐ ์์ํด์ ์ ์ฐจ ๊ท๋ชจ๋ฅผ ํค์๋๊ฐ๋ฉด ๋ผ. ๋๊ตฌ๋ ์ฒ์์๋ ์ด๋ณด์์๋ค๋ ๊ฑธ ๊ธฐ์ตํด!
์ด ๊ธ์ด ์ฌ๋ฌ๋ถ์ ์ปดํจํฐ ๋น์ ์ฌ์ ์ ์ข์ ์ถ๋ฐ์ ์ด ๋์์ผ๋ฉด ์ข๊ฒ ์ด. ๊ถ๊ธํ ์ ์ด ์๊ฑฐ๋ ๋งํ๋ ๋ถ๋ถ์ด ์๋ค๋ฉด ์ธ์ ๋ ์ปค๋ฎค๋ํฐ์ ์ง๋ฌธํ๊ณ , ๋ค๋ฅธ ๊ฐ๋ฐ์๋ค๊ณผ ์ํตํ๋ฉด์ ํจ๊ป ์ฑ์ฅํด๋๊ฐ์! ๐
์, ์ด์ ์ฌ๋ฌ๋ถ์ ์ฐจ๋ก์ผ! ์ค๋ ๋ฐฐ์ด ๋ด์ฉ์ ๋ฐํ์ผ๋ก ์ฒซ ๋ฒ์งธ ํ๋ก์ ํธ๋ฅผ ์์ํด๋ณด๋ ๊ฑด ์ด๋? ์์ ๊ฒ๋ถํฐ ์์ํด์ ์ ์ ๋ฐ์ ์์ผ ๋๊ฐ๋ค ๋ณด๋ฉด, ์ด๋์ ์ ๋ฌธ๊ฐ๊ฐ ๋์ด ์์ ๊ฑฐ์ผ!
ํ์ด์ ๋น์ด! ํ์ดํ
! ๐โจ
๐ ํต์ฌ ์์ฝ
Pillow๋ ๊ฐ๋จํ ์ด๋ฏธ์ง ํธ์ง์ ์ต์ ๐ธ
OpenCV๋ ๊ณ ๊ธ ์ปดํจํฐ ๋น์ ์์
์ ๊ฐ๋ ฅ ๐ฌ
์ค์ ํ๋ก์ ํธ๋ก ๊ฒฝํ์ ์์ ๐ผ
์ฑ๋ฅ ์ต์ ํ๋ ํ์! โก
์ฌ๋ฅ๋ท์์ ์ค๋ ฅ์ ์์ต์ผ๋ก! ๐ฐ
๊ด๋ จ ํค์๋
๋๊ธ 0
์ง์์ธ์ ์ฒ - ์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
์ง์ ์ฌ์ฐ๊ถ ๋ณดํธ ๊ณ ์ง
- ์ ์๊ถ ๋ฐ ์์ ๊ถ: ๋ณธ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ๋ ์ AI ๊ธฐ์ ๋ก ์์ฑ๋์์ผ๋ฉฐ, ๋ํ๋ฏผ๊ตญ ์ ์๊ถ๋ฒ ๋ฐ ๊ตญ์ ์ ์๊ถ ํ์ฝ์ ์ํด ๋ณดํธ๋ฉ๋๋ค.
- AI ์์ฑ ์ปจํ ์ธ ์ ๋ฒ์ ์ง์: ๋ณธ AI ์์ฑ ์ปจํ ์ธ ๋ ์ฌ๋ฅ๋ท์ ์ง์ ์ฐฝ์๋ฌผ๋ก ์ธ์ ๋๋ฉฐ, ๊ด๋ จ ๋ฒ๊ท์ ๋ฐ๋ผ ์ ์๊ถ ๋ณดํธ๋ฅผ ๋ฐ์ต๋๋ค.
- ์ฌ์ฉ ์ ํ: ์ฌ๋ฅ๋ท์ ๋ช ์์ ์๋ฉด ๋์ ์์ด ๋ณธ ์ปจํ ์ธ ๋ฅผ ๋ณต์ , ์์ , ๋ฐฐํฌ, ๋๋ ์์ ์ ์ผ๋ก ํ์ฉํ๋ ํ์๋ ์๊ฒฉํ ๊ธ์ง๋ฉ๋๋ค.
- ๋ฐ์ดํฐ ์์ง ๊ธ์ง: ๋ณธ ์ปจํ ์ธ ์ ๋ํ ๋ฌด๋จ ์คํฌ๋ํ, ํฌ๋กค๋ง, ๋ฐ ์๋ํ๋ ๋ฐ์ดํฐ ์์ง์ ๋ฒ์ ์ ์ฌ์ ๋์์ด ๋ฉ๋๋ค.
- AI ํ์ต ์ ํ: ์ฌ๋ฅ๋ท์ AI ์์ฑ ์ปจํ ์ธ ๋ฅผ ํ AI ๋ชจ๋ธ ํ์ต์ ๋ฌด๋จ ์ฌ์ฉํ๋ ํ์๋ ๊ธ์ง๋๋ฉฐ, ์ด๋ ์ง์ ์ฌ์ฐ๊ถ ์นจํด๋ก ๊ฐ์ฃผ๋ฉ๋๋ค.

๋๊ธ ์์ฑ
์ด ๊ธ์ ๋ํ ์ฌ๋ฌ๋ถ์ ์๊ฐ์ ๋ค๋ ค์ฃผ์ธ์
๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค
๋๊ธ์ ์์ฑํ๋ ค๋ฉด ๋จผ์ ๋ก๊ทธ์ธํด์ฃผ์ธ์.