How to use openCV and HAAR Cascades to blur faces?
Asked Answered
P

3

10

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

Pachalic answered 5/8, 2013 at 18:15 Comment(5)
Use one of these functions and pass in the subregion of your image containing a faceBeem
Hi Hammer, I thought to that but I dont know how to only blur the portion of the image where the face has been detected. Thanks.Pachalic
I finally succeed to do what I want. To do that apply a gaussianblur as you have suggested: sub_face = cv2.GaussianBlur(sub_face,(23, 23), 30) then I overlap this blurring image to a new one: result_image[y:y+sub_face.shape[0], x:x+sub_face.shape[1]] = sub_facePachalic
Sorry I should have been more explicit. Glad you figured it out :)Beem
@ArnaudGeotribu, please put your solution into an answer and accept it, so that people searching for the same problem can use it.Iodometry
P
20

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

Pachalic answered 6/8, 2013 at 12:28 Comment(3)
You don't need to if len(faces) != 0, the for loop with not iterate over an empty list. aka, there is effectively a built in if statement at the top of every for loop.Profitable
x,y,w,h = f is more pythonicProfitable
Hi Arnaud, i got an error above, can you help? Thanks. By the way, cv2.cv.CV_BGR2GRAY should be replaced with cv2.COLOR_BGR2HSVDamnable
P
11

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 ..

Phenol answered 12/4, 2019 at 8:25 Comment(0)
D
0

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}")```
Duvall answered 7/10, 2021 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.