๐Ÿ˜Qr

QR ์ฝ”๋“œ ์ธ์‹

OpenCV์™€ pyzbar ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ QR ์ฝ”๋“œ๋ฅผ ์ธ์‹ํ•˜๋Š” ๋‹จ๊ณ„์  ์ ‘๊ทผ๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.

ํ™˜๊ฒฝ ์„ค์ •

ํ•„์ˆ˜ ์†Œํ”„ํŠธ์›จ์–ด ์„ค์น˜

Visual Studio 2022 C Redistributable (64-bit) ์„ค์น˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. pyzbar ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ C ๋Ÿฐํƒ€์ž„์— ์˜์กดํ•˜๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ•„์ˆ˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

pip install opencv-python
pip install pyzbar
pip install matplotlib

๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ๋กœ๋”ฉ ๋ฐ ํ‘œ์‹œ

๊ธฐ๋ณธ ์ด๋ฏธ์ง€ ์ฝ๊ธฐ

์ฒซ ๋ฒˆ์งธ ๋‹จ๊ณ„๋กœ QR ์ฝ”๋“œ๊ฐ€ ํฌํ•จ๋œ ์ด๋ฏธ์ง€๋ฅผ ๋กœ๋”ฉํ•˜๊ณ  ํ™”๋ฉด์— ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

import cv2 
import matplotlib.pyplot as plt
import pyzbar.pyzbar as pyzbar

# ์ด๋ฏธ์ง€ ๋กœ๋”ฉ
img = cv2.imread('../img/frame.png')

# matplotlib์œผ๋กœ ์ด๋ฏธ์ง€ ํ‘œ์‹œ
plt.imshow(img)
plt.show()

cv2.waitKey(0)
cv2.destroyAllWindows()

์ฃผ์š” ํฌ์ธํŠธ:

  • cv2.imread()๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋ฉ”๋ชจ๋ฆฌ์— ๋กœ๋”ฉ

  • plt.imshow()๋กœ ์ด๋ฏธ์ง€๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œ

  • ์ด๋ฏธ์ง€ ํŒŒ์ผ ๊ฒฝ๋กœ๊ฐ€ ์ •ํ™•ํ•œ์ง€ ํ™•์ธ ํ•„์š”

QR ์ฝ”๋“œ ๋””์ฝ”๋”ฉ

๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜ ๋ฐ ๋””์ฝ”๋”ฉ

QR ์ฝ”๋“œ ์ธ์‹์„ ์œ„ํ•ด ์ด๋ฏธ์ง€๋ฅผ ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜ํ•˜๊ณ  pyzbar๋กœ ๋””์ฝ”๋”ฉํ•ฉ๋‹ˆ๋‹ค.

import pyzbar.pyzbar as pyzbar

# ์ด๋ฏธ์ง€ ๋กœ๋”ฉ
img = cv2.imread('../img/frame.png')

# ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜ (QR ์ฝ”๋“œ ์ธ์‹์„ ์œ„ํ•ด ํ•„์š”)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ์ด๋ฏธ์ง€ ํ‘œ์‹œ
plt.imshow(gray, cmap='gray')
plt.show()

# QR ์ฝ”๋“œ ๋””์ฝ”๋”ฉ
decoded = pyzbar.decode(gray)
print(decoded)

cv2.waitKey(0)
cv2.destroyAllWindows()

ํ•ต์‹ฌ ๊ฐœ๋…:

  • cv2.cvtColor()๋กœ ์ปฌ๋Ÿฌ ๊ณต๊ฐ„ ๋ณ€ํ™˜

  • pyzbar.decode()๊ฐ€ QR ์ฝ”๋“œ ์ •๋ณด๋ฅผ ์ถ”์ถœ

  • ๋””์ฝ”๋”ฉ ๊ฒฐ๊ณผ๋Š” ๋ฆฌ์ŠคํŠธ ํ˜•ํƒœ๋กœ ๋ฐ˜ํ™˜

QR ์ฝ”๋“œ ์˜์—ญ ์‹œ๊ฐํ™”

๊ฒฝ๊ณ„ ์‚ฌ๊ฐํ˜• ๊ทธ๋ฆฌ๊ธฐ

์ธ์‹๋œ QR ์ฝ”๋“œ ์ฃผ๋ณ€์— ์‚ฌ๊ฐํ˜•์„ ๊ทธ๋ ค์„œ ์œ„์น˜๋ฅผ ์‹œ๊ฐ์ ์œผ๋กœ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

# ๋””์ฝ”๋”ฉ๋œ QR ์ฝ”๋“œ ์ •๋ณด ์ฒ˜๋ฆฌ
for d in decoded:
    # QR ์ฝ”๋“œ ๋ฐ์ดํ„ฐ ์ถœ๋ ฅ
    print(d.data.decode('utf-8'))
    print(d.type)
    
    # QR ์ฝ”๋“œ ์ฃผ๋ณ€์— ์ดˆ๋ก์ƒ‰ ์‚ฌ๊ฐํ˜• ๊ทธ๋ฆฌ๊ธฐ
    cv2.rectangle(img, 
                 (d.rect[0], d.rect[1]), 
                 (d.rect[0] + d.rect[2], d.rect[1] + d.rect[3]), 
                 (0, 255, 0), 20)

plt.imshow(img)
plt.show()

๋งค๊ฐœ๋ณ€์ˆ˜ ์„ค๋ช…:

  • d.rect: QR ์ฝ”๋“œ์˜ ์œ„์น˜ ์ •๋ณด (x, y, width, height)

  • (0, 255, 0): BGR ์ƒ‰์ƒ (์ดˆ๋ก์ƒ‰)

  • 20: ์„ ์˜ ๋‘๊ป˜

ํ…์ŠคํŠธ ์ •๋ณด ์ถ”๊ฐ€

QR ์ฝ”๋“œ ์œ„์— ๋””์ฝ”๋”ฉ๋œ ํ…์ŠคํŠธ ์ •๋ณด๋ฅผ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค.

# QR ์ฝ”๋“œ ์œ„์— ํ…์ŠคํŠธ ์ถ”๊ฐ€
text = d.data.decode('utf-8')  # ๋””์ฝ”๋”ฉ๋œ ํ…์ŠคํŠธ
cv2.putText(img, text, 
           (d.rect[0], d.rect[1] - 50), 
           cv2.FONT_HERSHEY_SIMPLEX, 
           3, (0, 255, 0), 2, cv2.LINE_AA)

๋งค๊ฐœ๋ณ€์ˆ˜ ์ƒ์„ธ:

  • text: ํ‘œ์‹œํ•  ํ…์ŠคํŠธ

  • (d.rect[0], d.rect[1] - 50): ํ…์ŠคํŠธ ์œ„์น˜ (QR ์ฝ”๋“œ ์œ„์ชฝ)

  • cv2.FONT_HERSHEY_SIMPLEX: ํฐํŠธ ํƒ€์ž…

  • 3: ํฐํŠธ ํฌ๊ธฐ

  • 2: ํ…์ŠคํŠธ ๋‘๊ป˜

  • cv2.LINE_AA: ์•ˆํ‹ฐ์•จ๋ฆฌ์–ด์‹ฑ

์‹ค์‹œ๊ฐ„ ์›น์บ  QR ์ฝ”๋“œ ์ธ์‹

์™„์ „ํ•œ ์›น์บ  QR ์ฝ”๋“œ ์Šค์บ๋„ˆ

์›น์บ ์„ ์‚ฌ์šฉํ•˜์—ฌ ์‹ค์‹œ๊ฐ„์œผ๋กœ QR ์ฝ”๋“œ๋ฅผ ์ธ์‹ํ•˜๊ณ  ํ‘œ์‹œํ•˜๋Š” ์™„์ „ํ•œ ํ”„๋กœ๊ทธ๋žจ์ž…๋‹ˆ๋‹ค.

import pyzbar.pyzbar as pyzbar
import cv2

# ์›น์บ  ์ดˆ๊ธฐํ™” (๊ธฐ๋ณธ ์นด๋ฉ”๋ผ ์‚ฌ์šฉ)
cap = cv2.VideoCapture(0)

# ์ด๋ฏธ์ง€ ์ €์žฅ์„ ์œ„ํ•œ ์นด์šดํ„ฐ
i = 0

while(cap.isOpened()):
    # ํ”„๋ ˆ์ž„ ์ฝ๊ธฐ
    ret, img = cap.read()
    
    if not ret:
        continue
    
    # ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ ๋ณ€ํ™˜
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # QR ์ฝ”๋“œ ๋””์ฝ”๋”ฉ
    decoded = pyzbar.decode(gray)
    
    # ์ธ์‹๋œ QR ์ฝ”๋“œ ์ฒ˜๋ฆฌ
    for d in decoded: 
        x, y, w, h = d.rect
        
        # QR ์ฝ”๋“œ ๋ฐ์ดํ„ฐ ์ถ”์ถœ
        barcode_data = d.data.decode("utf-8")
        barcode_type = d.type
        
        # ๋นจ๊ฐ„์ƒ‰ ์‚ฌ๊ฐํ˜•์œผ๋กœ QR ์ฝ”๋“œ ์˜์—ญ ํ‘œ์‹œ
        cv2.rectangle(img, (x, y), (x + w, y + h), (0, 0, 255), 2)
        
        # ํ…์ŠคํŠธ ์ •๋ณด ์ƒ์„ฑ ๋ฐ ํ‘œ์‹œ
        text = '%s (%s)' % (barcode_data, barcode_type)
        cv2.putText(img, text, (x, y), 
                   cv2.FONT_HERSHEY_SIMPLEX, 1, 
                   (0, 255, 255), 2, cv2.LINE_AA)
    
    # ํ™”๋ฉด์— ๊ฒฐ๊ณผ ํ‘œ์‹œ
    cv2.imshow('img', img)
    
    # ํ‚ค๋ณด๋“œ ์ž…๋ ฅ ์ฒ˜๋ฆฌ
    key = cv2.waitKey(1)
    if key == ord('q'):        # 'q' ํ‚ค๋กœ ์ข…๋ฃŒ
        break
    elif key == ord('s'):      # 's' ํ‚ค๋กœ ์ด๋ฏธ์ง€ ์ €์žฅ
        i += 1
        cv2.imwrite('c_%03d.jpg' % i, img)

# ๋ฆฌ์†Œ์Šค ํ•ด์ œ
cap.release()
cv2.destroyAllWindows()

ํ•ต์‹ฌ ๊ธฐ๋Šฅ ๋ถ„์„

์›น์บ  ์ œ์–ด

์ดˆ๊ธฐํ™” ๋ฐ ํ•ด์ œ:

  • cv2.VideoCapture(0): ๊ธฐ๋ณธ ์นด๋ฉ”๋ผ ์—ฐ๊ฒฐ

  • cap.read(): ํ”„๋ ˆ์ž„ ๋‹จ์œ„๋กœ ์ด๋ฏธ์ง€ ์ฝ๊ธฐ

  • cap.release(): ์นด๋ฉ”๋ผ ๋ฆฌ์†Œ์Šค ํ•ด์ œ

QR ์ฝ”๋“œ ์ธ์‹ ํ”„๋กœ์„ธ์Šค

์ฒ˜๋ฆฌ ๋‹จ๊ณ„:

  1. ์ปฌ๋Ÿฌ ์ด๋ฏธ์ง€๋ฅผ ๊ทธ๋ ˆ์ด์Šค์ผ€์ผ๋กœ ๋ณ€ํ™˜

  2. pyzbar๋กœ QR ์ฝ”๋“œ ์ •๋ณด ์ถ”์ถœ

  3. ์ธ์‹๋œ ๊ฐ QR ์ฝ”๋“œ์— ๋Œ€ํ•ด ๋ฐ˜๋ณต ์ฒ˜๋ฆฌ

  4. ์‹œ๊ฐ์  ํ‘œ์‹œ (์‚ฌ๊ฐํ˜•, ํ…์ŠคํŠธ) ์ถ”๊ฐ€

์‚ฌ์šฉ์ž ์ธํ„ฐํŽ˜์ด์Šค

ํ‚ค๋ณด๋“œ ์ œ์–ด:

  • q ํ‚ค: ํ”„๋กœ๊ทธ๋žจ ์ข…๋ฃŒ

  • s ํ‚ค: ํ˜„์žฌ ํ™”๋ฉด ์ด๋ฏธ์ง€ ์ €์žฅ

  • cv2.waitKey(1): ํ‚ค ์ž…๋ ฅ ๋Œ€๊ธฐ (1ms)

๋ฌธ์ œ ํ•ด๊ฒฐ

์ผ๋ฐ˜์ ์ธ ์˜ค๋ฅ˜

์นด๋ฉ”๋ผ ์ ‘๊ทผ ์‹คํŒจ:

if not cap.isOpened():
    print("์นด๋ฉ”๋ผ๋ฅผ ์—ด ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.")
    exit()

QR ์ฝ”๋“œ ์ธ์‹ ์•ˆ๋จ:

  • ์กฐ๋ช… ์กฐ๊ฑด ํ™•์ธ

  • QR ์ฝ”๋“œ์™€ ์นด๋ฉ”๋ผ ๊ฑฐ๋ฆฌ ์กฐ์ ˆ

  • ์ด๋ฏธ์ง€ ํ’ˆ์งˆ ๋ฐ ํ•ด์ƒ๋„ ํ™•์ธ

์„ฑ๋Šฅ ์ตœ์ ํ™”

ํ”„๋ ˆ์ž„ ์ฒ˜๋ฆฌ ์ตœ์ ํ™”:

  • ํ•„์š”์‹œ ์ด๋ฏธ์ง€ ํฌ๊ธฐ ์กฐ์ •์œผ๋กœ ์ฒ˜๋ฆฌ ์†๋„ ํ–ฅ์ƒ

  • ROI(Region of Interest) ์„ค์ •์œผ๋กœ ์ฒ˜๋ฆฌ ์˜์—ญ ์ œํ•œ

ํ™•์žฅ ๊ฐ€๋Šฅ์„ฑ

์ถ”๊ฐ€ ๊ธฐ๋Šฅ ์•„์ด๋””์–ด

๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ:

  • QR ์ฝ”๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ํŒŒ์ผ์— ์ €์žฅ

  • ์ค‘๋ณต QR ์ฝ”๋“œ ํ•„ํ„ฐ๋ง

  • ํžˆ์Šคํ† ๋ฆฌ ๊ด€๋ฆฌ

UI ๊ฐœ์„ :

  • ์ธ์‹ ์ƒํƒœ ํ‘œ์‹œ

  • ์„ค์ • ๋ฉ”๋‰ด ์ถ”๊ฐ€

  • ๋‹ค์–‘ํ•œ ์‹œ๊ฐ์  ํšจ๊ณผ

๋‹ค์ค‘ ํฌ๋งท ์ง€์›:

  • ๋ฐ”์ฝ”๋“œ ์ธ์‹ ์ถ”๊ฐ€

  • ๋‹ค์–‘ํ•œ 2D ์ฝ”๋“œ ์ง€์›

์ด ๊ฐ€์ด๋“œ๋ฅผ ํ†ตํ•ด OpenCV์™€ pyzbar๋ฅผ ์‚ฌ์šฉํ•œ QR ์ฝ”๋“œ ์ธ์‹ ์‹œ์Šคํ…œ์„ ๋‹จ๊ณ„์ ์œผ๋กœ ๊ตฌ์ถ•ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.