Arnaud Geotribu
Arnaud Geotribu

Reputation: 969

How to use openCV and HAAR Cascades to blur faces?

I would like to know is there is a way to blur the faces that have been automatically identify by the haarcascade face classifier.

using the code below, I'm able to detect the faces, crop the image around this face or draw a rectangle on it.

image = cv2.imread(imagepath)

# Specify the trained cascade classifier
face_cascade_name = "./haarcascade_frontalface_alt.xml"

# Create a cascade classifier
face_cascade = cv2.CascadeClassifier()

# Load the specified classifier
face_cascade.load(face_cascade_name)

#Preprocess the image
grayimg = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY)
grayimg = cv2.equalizeHist(grayimg)

#Run the classifiers
faces = face_cascade.detectMultiScale(grayimg, 1.1, 2, 0|cv2.cv.CV_HAAR_SCALE_IMAGE, (30, 30))

print "Faces detected"

if len(faces) != 0:            # If there are faces in the images
    for f in faces:         # For each face in the image

        # Get the origin co-ordinates and the length and width till where the face extends
        x, y, w, h = [ v for v in f ]

        # Draw rectangles around all the faces
        cv2.rectangle(image, (x,y), (x+w,y+h), (255,255,255))
        sub_face = image[y:y+h, x:x+w]
        for i in xrange(1,31,2):
            cv2.blur(sub_face, (i,i))
        face_file_name = "./face_" + str(y) + ".jpg"
        cv2.imwrite(face_file_name, sub_face)

But I would like to blur the face of the people so they can't be recognized.

Do you have an idea on how to do that?

Thanks for your help

Arnaud

Upvotes: 10

Views: 21126

Answers (3)

Jean-Francois T.
Jean-Francois T.

Reputation: 12950

Note: Neural Networks (like Resnet) are now more accurate than HAAR Cascade to detect the faces, and they are also now integrated in OpenCV. It might be better than using the solutions mentionned in this question.

However, the code to blur / pixelate a face is still applicable.


You can also pixelate the region of the face by adding squares that contain the average of RGB values of the zones in the face.

A function performing this could be like that:

def pixelate_image(image: np.ndarray, nb_blocks=5, in_place=False) -> np.ndarray:
    """Return a pixelated version of a picture (need to be fed with a face to pixelate)"""
    # To pixelate, we will split into a given number of blocks
    # For each block, we will compute the average of RGB values of the block
    # And then we can just replace with a rectangle of this color
    # divide the input image into NxN blocks
    if not in_place:
        image = np.copy(image)
    h, w = image.shape[:2]
    blocks = tuple(
        np.linspace(0, d, nb_blocks + 1, dtype="int") for d in (w, h)
    )

    for i, j in product(*[range(1, len(s)) for s in blocks]):
        # compute the starting and ending (x, y)-coordinates
        # for the current block
        start = blocks[0][i - 1], blocks[1][j - 1]
        end = blocks[0][i], blocks[1][j]
        # extract the ROI using NumPy array slicing, compute the
        # mean of the ROI, and then draw a rectangle with the
        # mean RGB values over the ROI in the original image
        roi = image[start[1]:end[1], start[0]:end[0]]
        bgr = [int(x) for x in cv2.mean(roi)[:3]]
        cv2.rectangle(image, start, end, bgr, -1)

    return image

You then just need to use it in a function like this (updated to Python 3 with pathlib and type hints):

from pathlib import Path
from typing import Union

import cv2
import numpy as np

PathLike = Union[Path, str]

face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml")

def pixelate_faces_haar(img_path: PathLike, dest: Path):
    """Pixelate faces of people with OpenCV and save to a destination file"""
    img = cv2.imread(str(img_path))
    # To use cascade, we need to use Grayscale images
    # We can then detect faces
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.1, 4)

    for (x, y, width, height) in faces:
        roi = img[y:y+height, x:x+width]
        pixelate_image(roi, 15, in_place=True)

    dest.parent.mkdir(parents=True, exist_ok=True)
    cv2.imwrite(str(dest), img)
    print(f"Saved pixelated version of {img_path} to {dest}")```

Upvotes: 0

ZettaCircl
ZettaCircl

Reputation: 875

The whole end of your code can be replaced by :

img[startX:endX, startY:endY] = cv2.blur(img[startX:endX, startY:endY], (23, 23))

instead of :

    # Get the origin co-ordinates and the length and width till where the face extends
    x, y, w, h = [ v for v in f ]

    # get the rectangle img around all the faces
    cv2.rectangle(image, (x,y), (x+w,y+h), (255,255,0), 5)
    sub_face = image[y:y+h, x:x+w]
    # apply a gaussian blur on this new recangle image
    sub_face = cv2.GaussianBlur(sub_face,(23, 23), 30)
    # merge this blurry rectangle to our final image
    result_image[y:y+sub_face.shape[0], x:x+sub_face.shape[1]] = sub_face

Especially because you don't request to have a circular mask, it's (to me) much easier to read.

PS : Sorry for not commenting, not enough reputation to do it. Even if the post is 5 years old, I guess this may be worth it, as found it for this particular question ..

Upvotes: 11

Arnaud Geotribu
Arnaud Geotribu

Reputation: 969

I finally succeeded to do what I want. To do that apply a gaussianblur as Hammer has suggested. The code is :

image = cv2.imread(imagepath)
result_image = image.copy()

# Specify the trained cascade classifier
face_cascade_name = "./haarcascade_frontalface_alt.xml"

# Create a cascade classifier
face_cascade = cv2.CascadeClassifier()

# Load the specified classifier
face_cascade.load(face_cascade_name)

#Preprocess the image
grayimg = cv2.cvtColor(image, cv2.cv.CV_BGR2GRAY)
grayimg = cv2.equalizeHist(grayimg)

#Run the classifiers
faces = face_cascade.detectMultiScale(grayimg, 1.1, 2, 0|cv2.cv.CV_HAAR_SCALE_IMAGE, (30, 30))

print "Faces detected"

if len(faces) != 0:         # If there are faces in the images
    for f in faces:         # For each face in the image

        # Get the origin co-ordinates and the length and width till where the face extends
        x, y, w, h = [ v for v in f ]

        # get the rectangle img around all the faces
        cv2.rectangle(image, (x,y), (x+w,y+h), (255,255,0), 5)
        sub_face = image[y:y+h, x:x+w]
        # apply a gaussian blur on this new recangle image
        sub_face = cv2.GaussianBlur(sub_face,(23, 23), 30)
        # merge this blurry rectangle to our final image
        result_image[y:y+sub_face.shape[0], x:x+sub_face.shape[1]] = sub_face
        face_file_name = "./face_" + str(y) + ".jpg"
        cv2.imwrite(face_file_name, sub_face)

# cv2.imshow("Detected face", result_image)
cv2.imwrite("./result.png", result_image)

Arnaud

Upvotes: 20

Related Questions