OpenCVMiddleSystem design

Как проектировать privacy-safe обработку изображений и видео при работе с персональными данными?

Privacy-safe pipeline включает анонимизацию лиц через блюр или пикселизацию, обработку в памяти без сохранения оригинала, маскировку приватных зон и логирование только агрегированных метрик без биометрических данных.

Privacy-safe обработка изображений с персональными данными

При работе с видеонаблюдением, распознаванием лиц или биометрией возникают требования GDPR (Европа), 152-ФЗ (Россия) и других регуляторов. Privacy-safe pipeline включает техническую анонимизацию, минимизацию хранения данных и документированное согласие субъектов.

1. Анонимизация лиц: блюр и пикселизация

import cv2
import numpy as np

def blur_faces(frame, blur_strength=51):
    """Гауссово размытие лиц через Haar cascade."""
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
    )
    faces = face_cascade.detectMultiScale(
        gray, scaleFactor=1.1, minNeighbors=5, minSize=(30, 30)
    )

    result = frame.copy()
    for (x, y, w, h) in faces:
        # Расширяем ROI для захвата краёв лица
        pad = int(0.1 * max(w, h))
        x1 = max(0, x - pad)
        y1 = max(0, y - pad)
        x2 = min(frame.shape[1], x + w + pad)
        y2 = min(frame.shape[0], y + h + pad)

        roi = result[y1:y2, x1:x2]
        # blur_strength должен быть нечётным
        k = blur_strength if blur_strength % 2 == 1 else blur_strength + 1
        result[y1:y2, x1:x2] = cv2.GaussianBlur(roi, (k, k), 0)

    return result


def pixelate_faces(frame, pixel_size=20):
    """Пикселизация — более надёжная анонимизация, чем блюр."""
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
    )
    faces = face_cascade.detectMultiScale(gray, 1.1, 5)

    result = frame.copy()
    for (x, y, w, h) in faces:
        roi = result[y:y+h, x:x+w]
        small = cv2.resize(roi, (pixel_size, pixel_size),
                           interpolation=cv2.INTER_LINEAR)
        pixelated = cv2.resize(small, (w, h),
                               interpolation=cv2.INTER_NEAREST)
        result[y:y+h, x:x+w] = pixelated

    return result

2. Сегментация без сохранения лиц

import cv2
import numpy as np

def extract_features_without_faces(frame):
    """
    Детекция людей по силуэту (HOG) без захвата лиц.
    Возвращает только bounding boxes, не исходные пиксели.
    """
    hog = cv2.HOGDescriptor()
    hog.setSVMDetector(cv2.HOGDescriptor_getDefaultPeopleDetector())

    boxes, weights = hog.detectMultiScale(
        frame, winStride=(8, 8), padding=(4, 4), scale=1.05
    )

    # Логируем только координаты и время, не изображение
    detections = []
    for (x, y, w, h) in boxes:
        detections.append({
            "bbox": [int(x), int(y), int(w), int(h)],
            "confidence": float(weights[len(detections)][0])
        })

    return detections  # Не возвращаем frame

3. Минимизация хранения: обработка в памяти без записи на диск

import cv2
import numpy as np
import hashlib

def process_frame_without_storage(frame):
    """
    Обработка кадра без сохранения оригинала.
    Хранит только агрегированные метрики.
    """
    # Детекция людей
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    face_cascade = cv2.CascadeClassifier(
        cv2.data.haarcascades + "haarcascade_frontalface_default.xml"
    )
    faces = face_cascade.detectMultiScale(gray, 1.1, 5)

    # Агрегированные метрики — без биометрии
    result = {
        "person_count": len(faces),
        "frame_hash": hashlib.sha256(frame.tobytes()).hexdigest()[:16],
        # Не сохраняем bbox, не сохраняем crop лица
    }

    # Явное удаление frame из памяти после обработки
    del frame
    return result


def anonymize_before_save(frame, output_path):
    """Записывает только анонимизированный кадр."""
    anonymized = blur_faces(frame)  # из предыдущего примера
    cv2.imwrite(output_path, anonymized)
    # Оригинальный frame никогда не записывается

4. Privacy zones — маскировка приватных зон

import cv2
import numpy as np
import json

def apply_privacy_zones(frame, zones_config_path):
    """
    zones_config.json:
    [{"name": "window", "rect": [100, 200, 300, 400]},
     {"name": "screen", "rect": [500, 100, 200, 150]}]
    """
    with open(zones_config_path) as f:
        zones = json.load(f)

    result = frame.copy()
    for zone in zones:
        x, y, w, h = zone["rect"]
        # Полная маскировка чёрным прямоугольником
        result[y:y+h, x:x+w] = 0

    return result

5. Логирование и аудит без ПДн

import logging
import hashlib
import time

def log_detection_event(frame, detection_type, bbox):
    """
    Логирует событие детекции без персональных данных.
    НЕ логирует изображение, НЕ логирует биометрию.
    """
    # Псевдоним события (не идентифицирует человека)
    event_id = hashlib.sha256(
        f"{time.time()}{bbox}".encode()
    ).hexdigest()[:12]

    logging.info(
        f"detection event_id={event_id} "
        f"type={detection_type} "
        f"bbox_area={bbox[2]*bbox[3]}"
        # Не логируем координаты, которые могут идентифицировать зону наблюдения
    )

Подводные камни

  • Гауссово размытие можно частично восстановить — при достаточно высоком разрешении и малом ядре blur алгоритмы deblurring могут восстановить черты лица. Для надёжной анонимизации используйте пикселизацию с pixel_size >= 8x8 или полную замену на чёрный прямоугольник.
  • Haar cascade пропускает профили и частично перекрытые лица — Haar cascade обнаруживает преимущественно фронтальные лица. Для production используйте DNN-детектор (SSD MobileNet, RetinaFace) с лучшим recall.
  • Метаданные изображения содержат GPS и время съёмки — EXIF в JPEG хранит координаты. При сохранении используйте cv2.imencode без EXIF или явно очищайте метаданные через Pillow/exiftool.
  • In-memory обработка не защищает от core dumps — данные в оперативной памяти могут попасть в crash dump. Ограничьте права на /proc/pid/mem и отключите core dumps для production-процесса.
  • Логи event_id могут коррелировать между событиями — если один человек появляется в 100 кадрах, event_id разные, но паттерны движения идентифицируют личность. Агрегируйте логи до уровня «N человек за период».
  • Privacy zones в конфиге хранятся открыто — файл zones_config.json раскрывает геометрию помещения. Шифруйте конфиг или храните в защищённом хранилище.
  • GDPR требует не только технической анонимизации — даже анонимизированные данные требуют документации: цель обработки, срок хранения, DPA-соглашение с subprocessors. Технические меры без правовой документации не освобождают от ответственности.

Common mistakes

  • Отвечать определением без production-сценария.
  • Не называть runtime boundary, security boundary или failure mode.
  • Игнорировать версию API, observability и тестовую проверку.

What the interviewer is testing

  • Объясняет механизм своими словами и без выдуманных API.
  • Называет реальные риски, диагностику и критерий корректности.
  • Связывает ответ с текущей документацией и миграционными ограничениями.

Sources

Related topics