Last active
December 4, 2018 10:40
-
-
Save deadprogram/6a665db6b77acc4eb5caaa0cf9c9aca8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// What it does: | |
// | |
// This example uses a deep neural network to perform background blurring somewhat like Microsoft Teams does. | |
// You can see the faces of meeting participants, but not the background, for privacy during videoconferences. | |
// | |
// It can be used with either the Caffe face tracking or Tensorflow object detection models that are | |
// included with OpenCV 4.0 | |
// | |
// To perform blurring with the Caffe model: | |
// | |
// Download the model file from: | |
// https://github.com/opencv/opencv_3rdparty/raw/dnn_samples_face_detector_20170830/res10_300x300_ssd_iter_140000.caffemodel | |
// | |
// You will also need the prototxt config file: | |
// https://raw.githubusercontent.com/opencv/opencv/master/samples/dnn/face_detector/deploy.prototxt | |
// | |
// To perform blurring with the Tensorflow model: | |
// | |
// Download and extract the model file named "frozen_inference_graph.pb" from: | |
// http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v1_coco_2017_11_17.tar.gz | |
// | |
// You will also need the pbtxt config file: | |
// https://gist.githubusercontent.com/dkurt/45118a9c57c38677b65d6953ae62924a/raw/b0edd9e8c992c25fe1c804e77b06d20a89064871/ssd_mobilenet_v1_coco_2017_11_17.pbtxt | |
// | |
// How to run: | |
// | |
// go run dnn-backgroundblur.go [videosource] [modelfile] [configfile] ([backend] [device]) | |
// | |
package main | |
import ( | |
"fmt" | |
"image" | |
"os" | |
"path/filepath" | |
"gocv.io/x/gocv" | |
) | |
func main() { | |
if len(os.Args) < 4 { | |
fmt.Println("How to run:\ndnn-backgroundblur [videosource] [modelfile] [configfile] ([backend] [device])") | |
return | |
} | |
// parse args | |
deviceID := os.Args[1] | |
model := os.Args[2] | |
config := os.Args[3] | |
backend := gocv.NetBackendDefault | |
if len(os.Args) > 4 { | |
backend = gocv.ParseNetBackend(os.Args[4]) | |
} | |
target := gocv.NetTargetCPU | |
if len(os.Args) > 5 { | |
target = gocv.ParseNetTarget(os.Args[5]) | |
} | |
// open capture device | |
webcam, err := gocv.OpenVideoCapture(deviceID) | |
if err != nil { | |
fmt.Printf("Error opening video capture device: %v\n", deviceID) | |
return | |
} | |
defer webcam.Close() | |
window := gocv.NewWindow("DNN Face Blurring") | |
defer window.Close() | |
img := gocv.NewMat() | |
defer img.Close() | |
// open DNN object tracking model | |
net := gocv.ReadNet(model, config) | |
if net.Empty() { | |
fmt.Printf("Error reading network model from : %v %v\n", model, config) | |
return | |
} | |
defer net.Close() | |
net.SetPreferableBackend(gocv.NetBackendType(backend)) | |
net.SetPreferableTarget(gocv.NetTargetType(target)) | |
var ratio float64 | |
var mean gocv.Scalar | |
var swapRGB bool | |
if filepath.Ext(model) == ".caffemodel" { | |
ratio = 1.0 | |
mean = gocv.NewScalar(104, 177, 123, 0) | |
swapRGB = false | |
} else { | |
ratio = 1.0 / 127.5 | |
mean = gocv.NewScalar(127.5, 127.5, 127.5, 0) | |
swapRGB = true | |
} | |
fmt.Printf("Start reading device: %v\n", deviceID) | |
for { | |
if ok := webcam.Read(&img); !ok { | |
fmt.Printf("Device closed: %v\n", deviceID) | |
return | |
} | |
if img.Empty() { | |
continue | |
} | |
// convert image Mat to 300x300 blob that the object detector can analyze | |
blob := gocv.BlobFromImage(img, ratio, image.Pt(300, 300), mean, swapRGB, false) | |
// feed the blob into the detector | |
net.SetInput(blob, "") | |
// run a forward pass thru the network | |
prob := net.Forward("") | |
performBlurring(&img, prob) | |
prob.Close() | |
blob.Close() | |
window.IMShow(img) | |
if window.WaitKey(1) >= 0 { | |
break | |
} | |
} | |
} | |
// FaceData is used as temporary storage of each face detected. | |
type FaceData struct { | |
img gocv.Mat | |
rect image.Rectangle | |
} | |
// performBlurring analyzes the results from the detector network, | |
// which produces an output blob with a shape 1x1xNx7 | |
// where N is the number of detections, and each detection | |
// is a vector of float values | |
// [batchId, classId, confidence, left, top, right, bottom] | |
func performBlurring(frame *gocv.Mat, results gocv.Mat) { | |
// save all the faces | |
faces := make([]FaceData, 0) | |
for i := 0; i < results.Total(); i += 7 { | |
confidence := results.GetFloatAt(0, i+2) | |
if confidence > 0.5 { | |
left := int(results.GetFloatAt(0, i+3) * float32(frame.Cols())) | |
top := int(results.GetFloatAt(0, i+4) * float32(frame.Rows())) | |
right := int(results.GetFloatAt(0, i+5) * float32(frame.Cols())) | |
bottom := int(results.GetFloatAt(0, i+6) * float32(frame.Rows())) | |
if right > frame.Cols() { | |
right = frame.Cols() | |
} | |
if bottom > frame.Rows() { | |
bottom = frame.Rows() | |
} | |
// get the face | |
faceRect := image.Rect(left, top, right, bottom) | |
imgFace := frame.Region(faceRect) | |
faces = append(faces, FaceData{img: imgFace.Clone(), rect: faceRect}) | |
imgFace.Close() | |
} | |
} | |
// blur the background | |
gocv.GaussianBlur(*frame, frame, image.Pt(75, 75), 0, 0, gocv.BorderDefault) | |
// put the faces back in | |
for _, face := range faces { | |
replaceFace := frame.Region(face.rect) | |
face.img.CopyTo(&replaceFace) | |
replaceFace.Close() | |
face.img.Close() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment