Golang๊ณผ OpenCV๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ผ๊ตด์ธ์‹ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

Golang๊ณผ OpenCV๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ผ๊ตด์ธ์‹ ์‹œ์Šคํ…œ ๊ตฌ์ถ•
OpenCV๋Š” ์ปดํ“จํ„ฐ ๋น„์ „ ํ”„๋กœ์ ํŠธ๋ฅผ ์œ„ํ•ด ์„ค๊ณ„๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ž…๋‹ˆ๋‹ค. ๊ทธ๋…€๋Š” ๋ฒŒ์จ 20์‚ด์ฏค ๋์–ด์š”. ๋‚˜๋Š” ๋Œ€ํ•™์—์„œ ๊ทธ๊ฒƒ์„ ์‚ฌ์šฉํ–ˆ๊ณ  C++ ๋ฐ Python ํ”„๋กœ์ ํŠธ์—๋„ ์—ฌ์ „ํžˆ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์™œ๋ƒํ•˜๋ฉด ํ•ด๋‹น ์–ธ์–ด์— ๋Œ€ํ•œ ์ข‹์€ ์ง€์›์ด ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Go๋ฅผ ๋ฐฐ์šฐ๊ณ  ์‚ฌ์šฉํ•˜๊ธฐ ์‹œ์ž‘ํ•˜๋ฉด์„œ OpenCV๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด ์–ธ์–ด๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š”์ง€์— ๊ด€์‹ฌ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. ๋‹น์‹œ์—๋Š” ์ด๋ฏธ ํ†ตํ•ฉ์— ๋Œ€ํ•œ ์˜ˆ์ œ์™€ ํŠœํ† ๋ฆฌ์–ผ์ด ์žˆ์—ˆ์ง€๋งŒ ๋„ˆ๋ฌด ๋ณต์žกํ•ด ๋ณด์˜€์Šต๋‹ˆ๋‹ค. ์กฐ๊ธˆ ํ›„์— ์ €๋Š” The Hybrid Group ํŒ€์ด ๋งŒ๋“  ๋ž˜ํผ๋ฅผ ๋ฐœ๊ฒฌํ–ˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ธฐ์‚ฌ์—์„œ๋Š” Haar Cascades๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ„๋‹จํ•œ ์–ผ๊ตด ์ธ์‹ ์‹œ์Šคํ…œ์„ ๊ฐœ๋ฐœํ•˜์—ฌ GoCV๋ฅผ ์‹œ์ž‘ํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ๋ณด์—ฌ ๋“œ๋ฆฌ๊ฒ ์Šต๋‹ˆ๋‹ค.

Skillbox๋Š” ๋‹ค์Œ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค. ์‹ค๊ธฐ ์ฝ”์Šค "์ฒ˜์Œ๋ถ€ํ„ฐ Python ๊ฐœ๋ฐœ์ž".

์•Œ๋ฆผ: "Habr"์˜ ๋ชจ๋“  ๋…์ž๋ฅผ ์œ„ํ•œ - "Habr" ํ”„๋กœ๋ชจ์…˜ ์ฝ”๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Skillbox ๊ณผ์ •์— ๋“ฑ๋กํ•  ๋•Œ 10 ๋ฃจ๋ธ” ํ• ์ธ.

ํ•„์š”ํ•œ ๊ฒƒ :

  • ๊ฐ€๋‹ค;
  • OpenCV(์•„๋ž˜ ์„ค์น˜ ํ”„๋กœ๊ทธ๋žจ ๋งํฌ);
  • ์›น ๋˜๋Š” ์ผ๋ฐ˜ ๋น„๋””์˜ค ์นด๋ฉ”๋ผ.

์„ค์น˜

์˜ˆ๋ฅผ ๋“ค์–ด 1

์ฒซ ๋ฒˆ์งธ ์˜ˆ์—์„œ๋Š” ์นด๋ฉ”๋ผ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์„ ํ‘œ์‹œํ•˜๋Š” ์ฐฝ์„ ์—ฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋งŒ๋“ค์–ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

๋จผ์ € ์ž‘์—…์— ํ•„์š”ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

์ˆ˜์ž… (
"ํ†ต๋‚˜๋ฌด"
โ€œgocv.io/x/gocvโ€
)

๊ทธ๋Ÿฐ ๋‹ค์Œ VideoCaptureDevice ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ VideoCapture ๊ฐœ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ํ›„์ž๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์นด๋ฉ”๋ผ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์„ ์บก์ฒ˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ํ•จ์ˆ˜๋Š” ์ •์ˆ˜๋ฅผ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค(์žฅ์น˜ ID๋ฅผ ๋‚˜ํƒ€๋ƒ„).

webcam, err := gocv.VideoCaptureDevice(0)
if err != nil {    log.Fatalf(โ€œerror opening web cam: %vโ€, err)
}
defer webcam.Close()

์ด์ œ n์ฐจ์› ํ–‰๋ ฌ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ์—์„œ ์ฝ์€ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.

img := gocv.NewMat()
defer img.Close()

๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์„ ํ‘œ์‹œํ•˜๋ ค๋ฉด ์ฐฝ์„ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” NewWindow ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

window := gocv.NewWindow(โ€œwebcamwindowโ€)
defer window.Close()

์ด์ œ ๊ฐ€์žฅ ํฅ๋ฏธ๋กœ์šด ๋ถ€๋ถ„์œผ๋กœ ๋„˜์–ด ๊ฐ‘์‹œ๋‹ค.

๋น„๋””์˜ค๋Š” ์ด๋ฏธ์ง€ ํ”„๋ ˆ์ž„์˜ ์—ฐ์† ์ŠคํŠธ๋ฆผ์ด๋ฏ€๋กœ ์นด๋ฉ”๋ผ์˜ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์„ ๋์—†์ด ์ฝ์œผ๋ ค๋ฉด ๋ฌดํ•œ ๋ฃจํ”„๋ฅผ ๋งŒ๋“ค์–ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ํ•˜๋ ค๋ฉด VideoCapture ์œ ํ˜•์˜ Read ๋ฉ”์„œ๋“œ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. VideoCapture์˜ ํ”„๋ ˆ์ž„์„ ์„ฑ๊ณต์ ์œผ๋กœ ์ฝ์—ˆ๋Š”์ง€ ์—ฌ๋ถ€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ถ€์šธ ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜๋Š” Mat ์œ ํ˜•(์œ„์—์„œ ๋งŒ๋“  ํ–‰๋ ฌ)์„ ์˜ˆ์ƒํ•ฉ๋‹ˆ๋‹ค.

for {     
        if ok := webcam.Read(&img); !ok || img.Empty( {
        log.Println(โ€œUnable to read from the webcamโ€)    continue
     }
.
.
.
}

์ด์ œ ์ƒ์„ฑ๋œ ์ฐฝ์— ํ”„๋ ˆ์ž„์„ ํ‘œ์‹œํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋‹ค์Œ ํ”„๋ ˆ์ž„์œผ๋กœ ์ด๋™ํ•˜๊ธฐ ์œ„ํ•œ ์ผ์‹œ ์ •์ง€๋Š” 50ms์ž…๋‹ˆ๋‹ค.

window.IMShow(img)
window.WaitKey(50)

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์‹คํ–‰ํ•˜๋ฉด ์นด๋ฉ”๋ผ์˜ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์ด ํฌํ•จ๋œ ์ฐฝ์ด ์—ด๋ฆฝ๋‹ˆ๋‹ค.

Golang๊ณผ OpenCV๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ผ๊ตด์ธ์‹ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

package main
 
import (
"log"
 
"gocv.io/x/gocv"
)
 
func main() {
webcam, err := gocv.VideoCaptureDevice(0)
if err != nil {
    log.Fatalf("error opening device: %v", err)
}
defer webcam.Close()
 
img := gocv.NewMat()
defer img.Close()
 
window := gocv.NewWindow("webcamwindow")
defer window.Close()
 
for {
if ok := webcam.Read(&img); !ok || img.Empty() {
log.Println("Unable to read from the webcam")
continue
}
 
window.IMShow(img)
window.WaitKey(50)
}
}

์˜ˆ๋ฅผ ๋“ค์–ด 2

์ด ์˜ˆ์—์„œ๋Š” ์ด์ „ ์˜ˆ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Haar Cascades๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ผ๊ตด ์ธ์‹ ์‹œ์Šคํ…œ์„ ๊ตฌ์ถ•ํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

Haar ์บ์Šค์ผ€์ด๋“œ๋Š” Haar ์›จ์ด๋ธ”๋ฆฟ ๊ธฐ์ˆ ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ›ˆ๋ จ๋œ ์บ์Šค์ผ€์ด๋“œ ๋ถ„๋ฅ˜๊ธฐ์ž…๋‹ˆ๋‹ค. ํŠน์ • ํŠน์ง•์„ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฏธ์ง€์˜ ํ”ฝ์…€์„ ๋ถ„์„ํ•ฉ๋‹ˆ๋‹ค. Haar Cascades์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณด๋ ค๋ฉด ์•„๋ž˜ ๋งํฌ๋ฅผ ํด๋ฆญํ•˜์„ธ์š”.

Viola-Jones ๊ฐ์ฒด ๊ฐ์ง€ ํ”„๋ ˆ์ž„์›Œํฌ
๊ณ„๋‹จ์‹ ๋ถ„๋ฅ˜๊ธฐ
Haar์™€ ์œ ์‚ฌํ•œ ๊ธฐ๋Šฅ

์ด๋ฏธ ํ•™์Šต๋œ ์บ์Šค์ผ€์ด๋“œ ๋‹ค์šด๋กœ๋“œ ์—ฌ๊ธฐ์žˆ์„ ์ˆ˜์žˆ์–ด. ํ˜„์žฌ ์˜ˆ์—์„œ๋Š” ์บ์Šค์ผ€์ด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ •๋ฉด์—์„œ ์‚ฌ๋žŒ์˜ ์–ผ๊ตด์„ ์‹๋ณ„ํ•ฉ๋‹ˆ๋‹ค.

์ด๋ฅผ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ๋ถ„๋ฅ˜์ž๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ์ด๋ฏธ ํ›ˆ๋ จ๋œ ํŒŒ์ผ์„ ์ œ๊ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค(๋งํฌ๋Š” ์œ„์— ์ œ๊ณต๋จ). ์šฐ๋ฆฌ ํ”„๋กœ๊ทธ๋žจ์ด ์žˆ๋Š” ๋””๋ ‰ํ† ๋ฆฌ์— ์ด๋ฏธ pencv_haarcascade_frontalface_default.xml ํŒŒ์ผ์„ ์—…๋กœ๋“œํ–ˆ์Šต๋‹ˆ๋‹ค.

harrcascade := โ€œopencv_haarcascade_frontalface_default.xmlโ€classifier := gocv.NewCascadeClassifier()classifier.Load(harrcascade)
defer classifier.Close()

์ด๋ฏธ์ง€์—์„œ ์–ผ๊ตด์„ ๊ฐ์ง€ํ•˜๋ ค๋ฉด ๋‹ค์Œ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ฉ€ํ‹ฐ์Šค์ผ€์ผ ๊ฐ์ง€. ์ด ํ•จ์ˆ˜๋Š” ์นด๋ฉ”๋ผ์˜ ๋น„๋””์˜ค ์ŠคํŠธ๋ฆผ์—์„œ ๋ฐฉ๊ธˆ ์ฝ์€ ํ”„๋ ˆ์ž„(Mat ์œ ํ˜•)์„ ๊ฐ€์ ธ์™€ Rectangle ์œ ํ˜•์˜ ๋ฐฐ์—ด์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ๋ฐฐ์—ด ํฌ๊ธฐ๋Š” ๋ถ„๋ฅ˜๊ธฐ๊ฐ€ ํ”„๋ ˆ์ž„์—์„œ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ์–ผ๊ตด ์ˆ˜๋ฅผ ๋‚˜ํƒ€๋ƒ…๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ ๋‹ค์Œ ์ฐพ์€ ๋‚ด์šฉ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์ง์‚ฌ๊ฐํ˜• ๋ชฉ๋ก์„ ๋ฐ˜๋ณตํ•˜๊ณ  Rectangle ๊ฐœ์ฒด๋ฅผ ์ฝ˜์†”์— ์ธ์‡„ํ•˜์—ฌ ๊ฐ์ง€๋œ ์ง์‚ฌ๊ฐํ˜• ์ฃผ์œ„์— ํ…Œ๋‘๋ฆฌ๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค. ์ด๋Š” Rectangle ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์นด๋ฉ”๋ผ๊ฐ€ ์ฝ์€ Mat, DetectMultiScale ๋ฉ”์„œ๋“œ์—์„œ ๋ฐ˜ํ™˜๋œ Rectangle ๊ฐœ์ฒด, ํ…Œ๋‘๋ฆฌ์˜ ์ƒ‰์ƒ ๋ฐ ๋‘๊ป˜๋ฅผ ํ—ˆ์šฉํ•ฉ๋‹ˆ๋‹ค.

for _, r := range rects {
fmt.Println(โ€œdetectedโ€, r)
gocv.Rectangle(&img, r, color, 2)
}

Golang๊ณผ OpenCV๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ผ๊ตด์ธ์‹ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

Golang๊ณผ OpenCV๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์–ผ๊ตด์ธ์‹ ์‹œ์Šคํ…œ ๊ตฌ์ถ•

package main
 
import (
"fmt"
"image/color"
"log"
 
"gocv.io/x/gocv"
)
 
func main() {
webcam, err := gocv.VideoCaptureDevice(0)
if err != nil {
log.Fatalf("error opening web cam: %v", err)
}
defer webcam.Close()
 
img := gocv.NewMat()
defer img.Close()
 
window := gocv.NewWindow("webcamwindow")
defer window.Close()
 
harrcascade := "opencv_haarcascade_frontalface_default.xml"
classifier := gocv.NewCascadeClassifier()
classifier.Load(harrcascade)
defer classifier.Close()
 
color := color.RGBA{0, 255, 0, 0}
for {
if ok := webcam.Read(&img); !ok || img.Empty() {
log.Println("Unable to read from the device")
continue
}
 
rects := classifier.DetectMultiScale(img)
for _, r := range rects {
fmt.Println("detected", r)
gocv.Rectangle(&img, r, color, 3)
}
 
window.IMShow(img)
window.WaitKey(50)
}
}

๊ทธ๋ฆฌ๊ณ ... ๋„ค, ๋ชจ๋“  ์ผ์ด ์ž˜ ํ’€๋ ธ์–ด์š”! ์ด์ œ Go๋กœ ์ž‘์„ฑ๋œ ๊ฐ„๋‹จํ•œ ์–ผ๊ตด ์ธ์‹ ์‹œ์Šคํ…œ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค. ์กฐ๋งŒ๊ฐ„ ์ด๋Ÿฌํ•œ ์‹คํ—˜์„ ๊ณ„์†ํ•˜๊ณ  Go์™€ OpenCV๋ฅผ ๊ฒฐํ•ฉํ•˜์—ฌ ์ƒˆ๋กญ๊ณ  ๋ฉ‹์ง„ ๊ฒƒ๋“ค์„ ๋งŒ๋“ค ๊ณ„ํš์ž…๋‹ˆ๋‹ค.

๊ด€์‹ฌ์žˆ์œผ์‹œ๋ฉด ํ‰๊ฐ€ ๋ถ€ํƒ๋“œ๋ฆฝ๋‹ˆ๋‹ค gRPC ์›น ์„œ๋ฒ„, Python๊ณผ OpenCV๋กœ ์ž‘์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค. ์–ผ๊ตด์ด ๊ฐ์ง€๋˜๋Š” ์ˆœ๊ฐ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ŠคํŠธ๋ฆฌ๋ฐํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ๋‹ค์–‘ํ•œ ํ”„๋กœ๊ทธ๋ž˜๋ฐ ์–ธ์–ด๋กœ ๋‹ค์–‘ํ•œ ํด๋ผ์ด์–ธํŠธ๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ์ดˆ์ž…๋‹ˆ๋‹ค. ์„œ๋ฒ„์— ์—ฐ๊ฒฐํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ์ฝ์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ์‚ฌ๋ฅผ ์ฝ์–ด์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!

Skillbox๋Š” ๋‹ค์Œ์„ ๊ถŒ์žฅํ•ฉ๋‹ˆ๋‹ค.

์ถœ์ฒ˜ : habr.com

์ฝ”๋ฉ˜ํŠธ๋ฅผ ์ถ”๊ฐ€