์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŽจ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์™€ OpenCVยทPillow๋ฅผ ํ™œ์šฉํ•œ ์ปดํ“จํ„ฐ ๋น„์ „ ๊ฐœ๋ฐœ

๐ŸŽจ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์™€ OpenCVยทPillow๋ฅผ ํ™œ์šฉํ•œ ์ปดํ“จํ„ฐ ๋น„์ „ ๊ฐœ๋ฐœ

์นœ๊ตฌ์ฒ˜๋Ÿผ ์‰ฝ๊ฒŒ ๋ฐฐ์šฐ๋Š” Python ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์˜ ๋ชจ๋“  ๊ฒƒ

๐Ÿš€ ์ปดํ“จํ„ฐ ๋น„์ „, ์™œ ์ง€๊ธˆ ๋ฐฐ์›Œ์•ผ ํ• ๊นŒ?

์š”์ฆ˜ ์šฐ๋ฆฌ ์ฃผ๋ณ€์„ ๋‘˜๋Ÿฌ๋ณด๋ฉด ์ปดํ“จํ„ฐ ๋น„์ „ ๊ธฐ์ˆ ์ด ์ •๋ง ๋งŽ์ด ์‚ฌ์šฉ๋˜๊ณ  ์žˆ์–ด. ์Šค๋งˆํŠธํฐ์œผ๋กœ ์‚ฌ์ง„ ์ฐ์„ ๋•Œ ์ž๋™์œผ๋กœ ์–ผ๊ตด์„ ์ธ์‹ํ•˜๋Š” ๊ธฐ๋Šฅ, ์ธ์Šคํƒ€๊ทธ๋žจ ํ•„ํ„ฐ, ์ž์œจ์ฃผํ–‰ ์ž๋™์ฐจ, ์‹ฌ์ง€์–ด ๋ณ‘์›์—์„œ X-ray ์‚ฌ์ง„์„ ๋ถ„์„ํ•˜๋Š” ๊ฒƒ๊นŒ์ง€! ์ด ๋ชจ๋“  ๊ฒŒ ์ปดํ“จํ„ฐ ๋น„์ „ ๊ธฐ์ˆ ์ด์•ผ. ๐Ÿ˜Š

๊ทธ๋Ÿฐ๋ฐ ์ด๋Ÿฐ ๋ฉ‹์ง„ ๊ธฐ์ˆ ์„ ๋ฐฐ์šฐ๋ ค๋ฉด ์–ด๋””์„œ๋ถ€ํ„ฐ ์‹œ์ž‘ํ•ด์•ผ ํ• ๊นŒ? ๋ฐ”๋กœ Python์˜ OpenCV์™€ Pillow๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ๊ฐ•๋ ฅํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์•ผ. ์ด ๋‘ ์นœ๊ตฌ๋“ค์€ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์˜ ์–‘๋Œ€ ์‚ฐ๋งฅ์ด๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ง€. ์˜ค๋Š˜์€ ์ด ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•ด์„œ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์ปดํ“จํ„ฐ ๋น„์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š”์ง€ ์ž์„ธํžˆ ์•Œ์•„๋ณผ ๊ฑฐ์•ผ!

๐Ÿ’ก ์ด ๊ธ€์—์„œ ๋ฐฐ์šธ ๋‚ด์šฉ
  • โœ… OpenCV์™€ Pillow์˜ ์ฐจ์ด์ ๊ณผ ๊ฐ๊ฐ์˜ ์žฅ๋‹จ์ 
  • โœ… ์ด๋ฏธ์ง€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ, ์ €์žฅํ•˜๊ธฐ, ๊ธฐ๋ณธ ์กฐ์ž‘ ๋ฐฉ๋ฒ•
  • โœ… ํ•„ํ„ฐ์™€ ํšจ๊ณผ ์ ์šฉํ•˜๋Š” ์‹ค์ „ ๊ธฐ๋ฒ•
  • โœ… ์–ผ๊ตด ์ธ์‹๊ณผ ๊ฐ์ฒด ํƒ์ง€ ๊ตฌํ˜„ ๋ฐฉ๋ฒ•
  • โœ… ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š” ํ”„๋กœ์ ํŠธ ์˜ˆ์ œ
Pillow ์ด๋ฏธ์ง€ ํŽธ์ง‘ OpenCV ์ปดํ“จํ„ฐ ๋น„์ „ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋ฉด ์ตœ๊ฐ• ์กฐํ•ฉ! ํ•„ํ„ฐ ํšจ๊ณผ ๋ฆฌ์‚ฌ์ด์ง• ์–ผ๊ตด ์ธ์‹ ๊ฐ์ฒด ํƒ์ง€ ์ƒ‰์ƒ ๋ณ€ํ™˜ ์ด๋ฏธ์ง€ ํ•ฉ์„ฑ ๊ฒฝ๊ณ„์„  ๊ฒ€์ถœ ๋ชจ์…˜ ์ถ”์  ์ด๋ฏธ์ง€ ๋ถ„์„ ํŒจํ„ด ์ธ์‹

๐Ÿ“š OpenCV vs Pillow: ์–ด๋–ค ๊ฑธ ์„ ํƒํ•ด์•ผ ํ• ๊นŒ?

๋งŽ์€ ์‚ฌ๋žŒ๋“ค์ด ์ฒ˜์Œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋ฅผ ๋ฐฐ์šธ ๋•Œ ๊ฐ€์žฅ ๋จผ์ € ํ•˜๋Š” ์งˆ๋ฌธ์ด ๋ฐ”๋กœ ์ด๊ฑฐ์•ผ. "OpenCV๋ž‘ Pillow ์ค‘์— ๋ญ˜ ์จ์•ผ ํ•ด?" ์‚ฌ์‹ค ์ด ์งˆ๋ฌธ์€ "์น˜ํ‚จ์ด๋ž‘ ํ”ผ์ž ์ค‘์— ๋ญ๊ฐ€ ๋” ๋ง›์žˆ์–ด?"๋ผ๊ณ  ๋ฌป๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•ด. ๋‘˜ ๋‹ค ์ข‹์€๋ฐ ์šฉ๋„๊ฐ€ ๋‹ค๋ฅด๊ฑฐ๋“ ! ๐Ÿ•๐Ÿ—

๊ฐ„๋‹จํ•˜๊ฒŒ ์ •๋ฆฌํ•˜๋ฉด, Pillow๋Š” ์ด๋ฏธ์ง€ ํŽธ์ง‘๊ณผ ๊ฐ€๊ณต์— ํŠนํ™”๋˜์–ด ์žˆ๊ณ , OpenCV๋Š” ์‹ค์‹œ๊ฐ„ ๋น„๋””์˜ค ์ฒ˜๋ฆฌ์™€ ๊ณ ๊ธ‰ ์ปดํ“จํ„ฐ ๋น„์ „ ์ž‘์—…์— ์ตœ์ ํ™”๋˜์–ด ์žˆ์–ด. ๊ทธ๋Ÿผ ๊ฐ๊ฐ์˜ ํŠน์ง•์„ ์ž์„ธํžˆ ์‚ดํŽด๋ณผ๊นŒ?

๐ŸŽจ Pillow (PIL)

์žฅ์ :
โ€ข ์„ค์น˜์™€ ์‚ฌ์šฉ์ด ๋งค์šฐ ๊ฐ„๋‹จํ•จ
โ€ข ์ด๋ฏธ์ง€ ํฌ๋งท ๋ณ€ํ™˜์ด ์‰ฌ์›€
โ€ข ํ…์ŠคํŠธ ์ถ”๊ฐ€, ๋“œ๋กœ์ž‰ ๊ธฐ๋Šฅ ์šฐ์ˆ˜
โ€ข ๋ฉ”๋ชจ๋ฆฌ ํšจ์œจ์ 
โ€ข ์›น ๊ฐœ๋ฐœ๊ณผ ํ†ตํ•ฉ์ด ํŽธ๋ฆฌ

์ฃผ์š” ์šฉ๋„:
์ธ๋„ค์ผ ์ƒ์„ฑ, ์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€, ์ด๋ฏธ์ง€ ๋ฆฌ์‚ฌ์ด์ง•, ํฌ๋งท ๋ณ€ํ™˜, ๊ฐ„๋‹จํ•œ ํ•„ํ„ฐ ์ ์šฉ

๐Ÿ”ฌ OpenCV

์žฅ์ :
โ€ข ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ์ด ๋›ฐ์–ด๋‚จ
โ€ข ๊ณ ๊ธ‰ ์ปดํ“จํ„ฐ ๋น„์ „ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ œ๊ณต
โ€ข ๋น„๋””์˜ค ์ฒ˜๋ฆฌ ๊ธฐ๋Šฅ ๊ฐ•๋ ฅ
โ€ข ๋จธ์‹ ๋Ÿฌ๋‹ ๋ชจ๋ธ ํ†ตํ•ฉ ๊ฐ€๋Šฅ
โ€ข ๋Œ€์šฉ๋Ÿ‰ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์— ์ตœ์ ํ™”

์ฃผ์š” ์šฉ๋„:
์–ผ๊ตด ์ธ์‹, ๊ฐ์ฒด ์ถ”์ , ๋ชจ์…˜ ๊ฐ์ง€, ์˜์ƒ ๋ถ„์„, AR/VR ๊ฐœ๋ฐœ
๐ŸŽฏ ์‹ค์ „ ํŒ: ์‹ค๋ฌด์—์„œ๋Š” ๋‘ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ๊ฐ€ ๋งŽ์•„! Pillow๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ „์ฒ˜๋ฆฌํ•˜๊ณ , OpenCV๋กœ ๋ณต์žกํ•œ ๋ถ„์„์„ ์ˆ˜ํ–‰ํ•˜๋Š” ์‹์ด์ง€. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ๊ด€๋ จ ํ”„๋กœ์ ํŠธ๋ฅผ ์ง„ํ–‰ํ•  ๋•Œ๋„ ์ด๋Ÿฐ ์กฐํ•ฉ์„ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค๊ณ  ํ•ด.

๐Ÿ› ๏ธ ์„ค์น˜๋ถ€ํ„ฐ ์‹œ์ž‘ํ•˜๊ธฐ

์ž, ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ? ๋จผ์ € ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ์„ค์น˜ํ•ด์•ผ ํ•ด. 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')

ํ…์ŠคํŠธ ๊ธฐ๋Šฅ์€ ์›Œํ„ฐ๋งˆํฌ๋ฅผ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ์ด๋ฏธ์ง€์— ์„ค๋ช…์„ ๋„ฃ์„ ๋•Œ ์ •๋ง ์œ ์šฉํ•ด. ์˜ˆ๋ฅผ ๋“ค์–ด, ์žฌ๋Šฅ๋„ท์—์„œ ํฌํŠธํด๋ฆฌ์˜ค ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค ๋•Œ ์ž๋™์œผ๋กœ ๋กœ๊ณ ๋‚˜ ์ €์ž‘๊ถŒ ํ‘œ์‹œ๋ฅผ ๋„ฃ์„ ์ˆ˜ ์žˆ์ง€! ๐ŸŽฏ

Pillow ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ ์›Œํฌํ”Œ๋กœ์šฐ 1. ์ด๋ฏธ์ง€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ 2. ํฌ๊ธฐ/ํšŒ์ „ ์กฐ์ ˆ 3. ํ•„ํ„ฐ ์ ์šฉ 4. ํ…์ŠคํŠธ/ ๋„ํ˜• ์ถ”๊ฐ€ 5. ์ €์žฅ ์™„๋ฃŒ! ์ฃผ์š” ๊ธฐ๋Šฅ โ€ข ๋ฆฌ์‚ฌ์ด์ง• resize() โ€ข ์ž๋ฅด๊ธฐ crop() โ€ข ํšŒ์ „ rotate() โ€ข ํ•„ํ„ฐ filter() โ€ข ํ…์ŠคํŠธ text() ํ™œ์šฉ ๋ถ„์•ผ โ€ข ์ธ๋„ค์ผ ์ƒ์„ฑ โ€ข ์›Œํ„ฐ๋งˆํฌ ์ถ”๊ฐ€ โ€ข ์ด๋ฏธ์ง€ ํŽธ์ง‘ โ€ข ํฌ๋งท ๋ณ€ํ™˜ โ€ข ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ ์„ฑ๋Šฅ ํŒ โ€ข LANCZOS ๋ฆฌ์ƒ˜ํ”Œ๋ง โ€ข ์ ์ ˆํ•œ ํ’ˆ์งˆ ์„ค์ • โ€ข ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ โ€ข ๋ฐฐ์น˜ ์ตœ์ ํ™” โ€ข ํฌ๋งท ์„ ํƒ

๐Ÿ”ฌ 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์˜ ์ค‘์š”ํ•œ ํŠน์ง•

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)
๐ŸŽฏ ๋ณด๊ฐ„๋ฒ•(Interpolation) ์„ ํƒ ๊ฐ€์ด๋“œ:

โ€ข 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)

๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ๊ฐ€ ์ค‘์š”ํ•œ ์š”์ฆ˜, ์ด๋Ÿฐ ๋ชจ์ž์ดํฌ ๊ธฐ๋Šฅ์€ ์ •๋ง ์œ ์šฉํ•ด. ์œ ํŠœ๋ธŒ ์˜์ƒ์ด๋‚˜ ๋ธ”๋กœ๊ทธ ์‚ฌ์ง„์—์„œ ์–ผ๊ตด์„ ๊ฐ€๋ฆด ๋•Œ ์ž๋™์œผ๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์ง€. ์žฌ๋Šฅ๋„ท ๊ฐ™์€ ํ”Œ๋žซํผ์—์„œ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ์˜ฌ๋ฆด ๋•Œ๋„ ๊ณ ๊ฐ์˜ ์–ผ๊ตด์„ ๋ณดํ˜ธํ•˜๋Š” ๋ฐ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด!

OpenCV ์–ผ๊ตด ์ธ์‹ ํ”„๋กœ์„ธ์Šค ์ด๋ฏธ์ง€ ์ž…๋ ฅ ๊ทธ๋ ˆ์ด ์Šค์ผ€์ผ Haar Cascade ์–ผ๊ตด ๊ฒ€์ถœ! ์ฃผ์š” ํŒŒ๋ผ๋ฏธํ„ฐ scaleFactor ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ • ๋น„์œจ minNeighbors ์ตœ์†Œ ์ด์›ƒ ๊ฒ€์ถœ ์ˆ˜ minSize ์ตœ์†Œ ์–ผ๊ตด ํฌ๊ธฐ (30, 30) ๊ถŒ์žฅ ํ™œ์šฉ ์‚ฌ๋ก€ โœ“ ๋ณด์•ˆ ์‹œ์Šคํ…œ โœ“ ์ถœ์ž… ํ†ต์ œ โœ“ ํ™”์ƒํšŒ์˜ โœ“ ์‚ฌ์ง„ ์ž๋™ ํƒœ๊น… โœ“ ๊ฐ์ • ๋ถ„์„ โœ“ ๊ฐœ์ธ์ •๋ณด ๋ณดํ˜ธ ์„ฑ๋Šฅ ์ตœ์ ํ™” โ€ข ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ถ•์†Œ โ€ข ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ์‚ฌ์šฉ โ€ข ROI ์˜์—ญ ์ œํ•œ โ€ข ํ”„๋ ˆ์ž„ ์Šคํ‚ต โ€ข ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋”ฉ โ€ข GPU ๊ฐ€์† ์ถ”๊ฐ€ ๊ธฐ๋Šฅ โ€ข ๋ˆˆ ๊ฒ€์ถœ โ€ข ๋ฏธ์†Œ ๊ฒ€์ถœ โ€ข ์ „์‹  ๊ฒ€์ถœ โ€ข ํ”„๋กœํ•„ ๊ฒ€์ถœ โ€ข ๋ชจ์ž์ดํฌ ์ฒ˜๋ฆฌ โ€ข ์–ผ๊ตด ๋žœ๋“œ๋งˆํฌ

๐ŸŽฏ ๊ฐ์ฒด ํƒ์ง€์™€ ์ถ”์ 

์–ผ๊ตด ์ธ์‹์„ ๋ฐฐ์› ์œผ๋‹ˆ, ์ด์ œ ๋” ๋‚˜์•„๊ฐ€์„œ ๋‹ค์–‘ํ•œ ๊ฐ์ฒด๋ฅผ ํƒ์ง€ํ•˜๊ณ  ์ถ”์ ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์•Œ์•„๋ณด์ž! ์ด๊ฑด ์ž์œจ์ฃผํ–‰, ๋ณด์•ˆ ์‹œ์Šคํ…œ, ์Šคํฌ์ธ  ๋ถ„์„ ๋“ฑ ์ •๋ง ๋งŽ์€ ๋ถ„์•ผ์—์„œ ์‚ฌ์šฉ๋˜๋Š” ๊ธฐ์ˆ ์ด์•ผ. ๐ŸŽฏ

์ƒ‰์ƒ ๊ธฐ๋ฐ˜ ๊ฐ์ฒด ํƒ์ง€

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()
๐ŸŒˆ ๋‹ค์–‘ํ•œ ์ƒ‰์ƒ์˜ HSV ๋ฒ”์œ„:

๋นจ๊ฐ„์ƒ‰: [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()

์ด ํ•„ํ„ฐ ์นด๋ฉ”๋ผ๋Š” ์ธ์Šคํƒ€๊ทธ๋žจ ๊ฐ™์€ ์•ฑ์˜ ๊ธฐ๋ณธ ์›๋ฆฌ๋ฅผ ๋ณด์—ฌ์ค˜! ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋‹ค์–‘ํ•œ ํ•„ํ„ฐ๋ฅผ ์ ์šฉํ•˜๊ณ  ์‚ฌ์ง„์„ ์ €์žฅํ•  ์ˆ˜ ์žˆ์–ด. ์ด๊ฑธ ๊ธฐ๋ฐ˜์œผ๋กœ ๋” ๋ณต์žกํ•œ ํšจ๊ณผ๋“ค์„ ์ถ”๊ฐ€ํ•  ์ˆ˜๋„ ์žˆ์ง€. ์ •๋ง ์žฌ๋ฏธ์žˆ๋Š” ํ”„๋กœ์ ํŠธ์•ผ! ๐Ÿ“ธโœจ

์‹ค์ „ ํ”„๋กœ์ ํŠธ ์•„์ด๋””์–ด 1 ๋ฐฐ๊ฒฝ ์ œ๊ฑฐ ์„œ๋น„์Šค โ€ข ์ „์ž์ƒ๊ฑฐ๋ž˜ ์ œํ’ˆ ์‚ฌ์ง„ โ€ข ํ”„๋กœํ•„ ์‚ฌ์ง„ ํŽธ์ง‘ โ€ข ๋””์ž์ธ ์†Œ์Šค ์ œ์ž‘ โ€ข ์ผ๊ด„ ๋ฐฐ์น˜ ์ฒ˜๋ฆฌ 2 ๋ฌธ์„œ ์Šค์บ๋„ˆ ์•ฑ โ€ข ๋ช…ํ•จ ์ž๋™ ์ธ์‹ โ€ข ์˜์ˆ˜์ฆ ์ •๋ฆฌ โ€ข ์ฑ… ํŽ˜์ด์ง€ ์Šค์บ” โ€ข PDF ๋ณ€ํ™˜ ๊ธฐ๋Šฅ 3 ์‹ค์‹œ๊ฐ„ ํ•„ํ„ฐ ์นด๋ฉ”๋ผ โ€ข ์†Œ์…œ๋ฏธ๋””์–ด ํ•„ํ„ฐ โ€ข ํ™”์ƒํšŒ์˜ ๋ฐฐ๊ฒฝ โ€ข ๋ผ์ด๋ธŒ ์ŠคํŠธ๋ฆฌ๋ฐ โ€ข AR ํšจ๊ณผ ์ถ”๊ฐ€ 4 ์–ผ๊ตด ์ธ์‹ ์ถœ์ž… ์‹œ์Šคํ…œ โ€ข ๋ฌด์ธ ์ถœ์ž… ๊ด€๋ฆฌ โ€ข ๊ทผํƒœ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ โ€ข ๋ณด์•ˆ ๊ฐ•ํ™” โ€ข ๋ฐฉ๋ฌธ์ž ๊ธฐ๋ก 5 ํ’ˆ์งˆ ๊ฒ€์‚ฌ ์ž๋™ํ™” โ€ข ์ œ์กฐ์—… ๋ถˆ๋Ÿ‰ ๊ฒ€์ถœ โ€ข ๋†์‚ฐ๋ฌผ ๋“ฑ๊ธ‰ ๋ถ„๋ฅ˜ โ€ข ํฌ์žฅ ์ƒํƒœ ํ™•์ธ โ€ข ์‹ค์‹œ๊ฐ„ ๋ชจ๋‹ˆํ„ฐ๋ง 6 ์ฃผ์ฐจ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ โ€ข ์ฐจ๋Ÿ‰ ๋ฒˆํ˜ธํŒ ์ธ์‹ โ€ข ์ฃผ์ฐจ ๊ณต๊ฐ„ ๊ฐ์ง€ โ€ข ์ž๋™ ์š”๊ธˆ ๊ณ„์‚ฐ โ€ข ํ†ต๊ณ„ ๋ฐ ๋ถ„์„

โšก ์„ฑ๋Šฅ ์ตœ์ ํ™”์™€ ์‹ค๋ฌด ํŒ

์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ๋Š” ๊ณ„์‚ฐ๋Ÿ‰์ด ๋งŽ์•„์„œ ์„ฑ๋Šฅ ์ตœ์ ํ™”๊ฐ€ ์ •๋ง ์ค‘์š”ํ•ด. ํŠนํžˆ ์‹ค์‹œ๊ฐ„ ์ฒ˜๋ฆฌ๋‚˜ ๋Œ€์šฉ๋Ÿ‰ ์ด๋ฏธ์ง€๋ฅผ ๋‹ค๋ฃฐ ๋•Œ๋Š” ๋”์šฑ ๊ทธ๋ ‡์ง€. ์‹ค๋ฌด์—์„œ ๋ฐ”๋กœ ์“ธ ์ˆ˜ ์žˆ๋Š” ์ตœ์ ํ™” ๊ธฐ๋ฒ•๋“ค์„ ์•Œ๋ ค์ค„๊ฒŒ! ๐Ÿš€

์ด๋ฏธ์ง€ ํฌ๊ธฐ ์ตœ์ ํ™”

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๋Š” ๊ณ ๊ธ‰ ์ปดํ“จํ„ฐ ๋น„์ „ ์ž‘์—…์— ๊ฐ•๋ ฅ ๐Ÿ”ฌ
์‹ค์ „ ํ”„๋กœ์ ํŠธ๋กœ ๊ฒฝํ—˜์„ ์Œ“์ž ๐Ÿ’ผ
์„ฑ๋Šฅ ์ตœ์ ํ™”๋Š” ํ•„์ˆ˜! โšก
์žฌ๋Šฅ๋„ท์—์„œ ์‹ค๋ ฅ์„ ์ˆ˜์ต์œผ๋กœ! ๐Ÿ’ฐ

Happy Coding! ์ด๋ฏธ์ง€ ์ฒ˜๋ฆฌ์˜ ์„ธ๊ณ„์— ์˜ค์‹  ๊ฒƒ์„ ํ™˜์˜ํ•ฉ๋‹ˆ๋‹ค ๐ŸŽจ ๐Ÿ”ฌ ๐Ÿš€ Pillow OpenCV Your Project
๋Œ“๊ธ€ ์ž‘์„ฑ

์ด ๊ธ€์— ๋Œ€ํ•œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์ƒ๊ฐ์„ ๋“ค๋ ค์ฃผ์„ธ์š”

๋Œ“๊ธ€ 0