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.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.