1. package 설치

flutter pub add google_mlkit_face_detection
flutter pub add image

 

2. '얼굴'영역 감지하여 원형태로 모자이크 처리하는 함수

import 'package:google_mlkit_face_detection/google_mlkit_face_detection.dart';
import 'package:image/image.dart' as img;
import 'dart:math';

  Future<File?> _processMosaic(String imagePath) async {
    final FaceDetector faceDetector = FaceDetector(options: FaceDetectorOptions());
    
    // 1. ML Kit로 얼굴 감지
    final File imageFile = File(imagePath);
    final inputImage = InputImage.fromFile(imageFile);
    final List<Face> faces = await faceDetector.processImage(inputImage);
    faceDetector.close();

    // 2. 'image' 패키지로 원본 이미지 디코딩
    Uint8List bytes = await imageFile.readAsBytes();
    img.Image? image = img.decodeImage(bytes);
    if (image == null) return null;

    // 3. 감지된 각 얼굴 영역에 모자이크(Pixelate) 적용
    for (Face face in faces) {
      final rect = face.boundingBox;

      int x = rect.left.toInt();
      int y = rect.top.toInt();
      int w = rect.width.toInt();
      int h = rect.height.toInt();
      
      // 얼굴영역 중심점(centerX, centerY) 및 반지름(radius) 계산
      int centerX = (rect.left + rect.width / 2).toInt();
      int centerY = (rect.top + rect.height / 2).toInt();
      int radius = (max(rect.width, rect.height) / 2).toInt();

      debugPrint('x: $x, y: $y, w: $w, h: $h, centerX: $centerX, centerY: $centerY, radius: $radius');

      // 4. 얼굴 부분만 따로 추출 (Crop)
      img.Image mosaicPart = img.copyCrop(image, x: x, y: y, width: w, height: h);

      // 5. 추출된 얼굴 부분에 모자이크 적용 (size가 클수록 격자가 커짐)
      img.pixelate(mosaicPart, size: 20); 

      // 6. 모자이크 처리된 부분을 다시 원본 이미지 위에 덮어쓰기 (Composite)
      // 사각 형태
      //img.compositeImage(image, mosaicPart, dstX: x, dstY: y);

      // 원 형태
      for (int y = 0; y < mosaicPart.height; y++) {
        for (int x = 0; x < mosaicPart.width; x++) {
          // 해당 픽셀이 원 안에 있는지 거리를 계산 (피타고라스 정리)
          int absX = rect.left.toInt() + x;
          int absY = rect.top.toInt() + y;
          double distance = sqrt(pow(absX - centerX, 2) + pow(absY - centerY, 2));

          if (distance <= radius) {
            // 원 안에 있는 픽셀만 원본 이미지에 복사
            img.Color pixelColor = mosaicPart.getPixel(x, y);
            image.setPixel(absX, absY, pixelColor);
          }
        }
      }
    }

    // 7. 처리된 이미지를 인코딩하여 overwrite하여 저장
    await imageFile.writeAsBytes(img.encodeJpg(image));
    
    return imageFile;
  }

 

3. '눈'영역 감지하여 모자이크 처리하는 함수 

  Future<File?> _processMosaic(String imagePath) async {
    final FaceDetector faceDetector = FaceDetector(
      options: FaceDetectorOptions(
        enableLandmarks: true,
        performanceMode: FaceDetectorMode.accurate
      )
    );
    
    // 1. ML Kit로 얼굴 감지
    final File imageFile = File(imagePath);
    final inputImage = InputImage.fromFile(imageFile);
    final List<Face> faces = await faceDetector.processImage(inputImage);
    faceDetector.close();

    // 2. 'image' 패키지로 원본 이미지 디코딩
    Uint8List bytes = await imageFile.readAsBytes();
    img.Image? image = img.decodeImage(bytes);
    if (image == null) return null;

    // 3. 감지된 각 얼굴 영역에 모자이크(Pixelate) 적용
    for (Face face in faces) {
      final rect = face.boundingBox;

      // 4. 왼쪽 눈과 오른쪽 눈 랜드마크 가져오기
      final FaceLandmark? leftEye = face.landmarks[FaceLandmarkType.leftEye];
      final FaceLandmark? rightEye = face.landmarks[FaceLandmarkType.rightEye];

      if (leftEye != null && rightEye != null) {
        // 5. 눈 주위 모자이크 크기 설정 (얼굴 크기에 비례하게 설정하면 좋음)
        int blurSizeW = (rect.width * 0.4).toInt();
        int blurSizeH = (rect.height * 0.2).toInt();

        void applyEyeMosaic(FaceLandmark eye) {
          // 6. 눈의 중심에서 left, top 계산 (~/ 정수몫을 가지는 나누기)
          int x = eye.position.x.toInt() - (blurSizeW ~/ 2);
          int y = eye.position.y.toInt() - (blurSizeH ~/ 2);
          
          // 7. 눈 주변 영역만 Crop
          img.Image mosaicPart = img.copyCrop(image, x: x, y: y, width: blurSizeW, height: blurSizeH);
          
          // 8. 모자이크 처리
          img.pixelate(mosaicPart, size: 30);
          
          // 9. 원본에 다시 붙이기
          img.compositeImage(image, mosaicPart, dstX: x, dstY: y);
        }

        applyEyeMosaic(leftEye);
        applyEyeMosaic(rightEye);
      }
    }

    // 10. 처리된 이미지를 인코딩하여 overwrite하여 저장
    await imageFile.writeAsBytes(img.encodeJpg(image));
    
    return imageFile;
  }

 

4. '코'영역 감지하여 모자이크 처리하는 함수 

  Future<void> _processMosaic(String imagePath) async {
    final FaceDetector faceDetector = FaceDetector(
      options: FaceDetectorOptions(
        enableLandmarks: true,
        performanceMode: FaceDetectorMode.accurate
      )
    );
    
    // 1. ML Kit로 얼굴 감지
    final File imageFile = File(imagePath);
    final inputImage = InputImage.fromFile(imageFile);
    final List<Face> faces = await faceDetector.processImage(inputImage);
    faceDetector.close();

    // 2. 'image' 패키지로 원본 이미지 디코딩
    Uint8List bytes = await imageFile.readAsBytes();
    img.Image? image = img.decodeImage(bytes);
    if (image == null) return;

    // 3. 감지된 각 얼굴 영역에 모자이크(Pixelate) 적용
    for (Face face in faces) {
      final rect = face.boundingBox;

      // 4. 코의 중심점(코 끝/기둥) 랜드마크 가져오기
      final FaceLandmark? nose = face.landmarks[FaceLandmarkType.noseBase];

      if (nose != null) {
        // 5. 코 주위 모자이크 크기 설정 (얼굴 크기에 비례하게 설정하면 좋음)
        int blurSizeW = (rect.width * 0.8).toInt();
        int blurSizeH = (rect.height * 0.6).toInt();

        // 6. 코의 중심에서 left, top 계산 (~/ 정수몫을 가지는 나누기)
        int x = nose.position.x.toInt() - (blurSizeW ~/ 2);
        int y = nose.position.y.toInt() - (blurSizeH ~/ 2);

        // 7. 이미지 경계를 벗어나지 않도록 방어 코드 (Clamping)
        // 코가 이미지 가장자리에 있을 경우, 계산된 x, y 좌표가 이미지 범위를 벗어나 에러가 발생할 수 있으므로
        // 이를 방지하기 위해 0과 이미지 최대 크기 사이로 값을 제한.
        x = x.clamp(0, image.width - blurSizeW);
        y = y.clamp(0, image.height - blurSizeH);
        
        // 8. 코 주변 영역만 Crop
        img.Image mosaicPart = img.copyCrop(image, x: x, y: y, width: blurSizeW, height: blurSizeH);
        
        // 9. 모자이크 처리
        img.pixelate(mosaicPart, size: 20);
        
        // 10. 원본에 다시 붙이기
        img.compositeImage(image, mosaicPart, dstX: x, dstY: y);
      }
    }

    // 10. 처리된 이미지를 인코딩하여 overwrite하여 저장
    await imageFile.writeAsBytes(img.encodeJpg(image));
  }