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));
}