My AI Smarteasy 사용자 정의 AI 에이전트 – 일타 강사 저스틴 – Flutter ML Kit FaceMesh 실시간 얼굴 인식 예제
VS Code라는 최첨단 주방에서, 플러터 요리의 가장 기본이자 첫걸음인 ‘헬로 월드’ 프로젝트를 만들고 실행하는 전체 과정을 저 일타 강사 저스틴이 처음부터 끝까지! 아주 상세하고 친절하게 알려드릴게요.
이건 마치 우리가 처음으로 요리를 배울 때 ‘계란 프라이’를 만들어보는 것과 같아요. 간단하지만 모든 기본기가 담겨 있거든요! 자, 저만 믿고 따라오시면 5분 안에 여러분의 폰 화면에 “Hello, World!”가 뜨는 마법을 보게 될 겁니다! 준비되셨나요? 🚀
STEP 1: 나만의 레스토랑(프로젝트) 만들기
모든 요리는 주방에서 시작하죠. 우리도 코딩을 위한 멋진 주방, 즉 플러터 프로젝트를 VS Code 안에서 만들어 봅시다.
- 프로젝트를 저장할 폴더 열기 먼저, 여러분의 프로젝트들을 모아둘 깔끔한 폴더를 하나 만드세요 (예:
C:\flutter_projects). 그리고 VS Code 상단 메뉴에서파일(File)>폴더 열기(Open Folder)를 선택해서 그 폴더를 열어주세요. - 주방의 지휘소, 터미널 열기 이제 명령을 내릴 시간입니다. VS Code 상단 메뉴에서
터미널(Terminal)>새 터미널(New Terminal)을 선택하거나, 단축키Ctrl+Shift+ ` (키보드 숫자 1 왼쪽에 있는~기호)를 눌러 터미널 창을 열어주세요. - “레스토랑 개업!” 명령어 외치기 활성화된 터미널 창에, 아래의 마법 같은 명령어를 입력하고
Enter키를 누르세요.hello_flutter_app이라는 이름의 멋진 레스토랑(프로젝트)이 순식간에 뚝딱 만들어질 거예요!
12flutter create hello_flutter_app - 새로 개업한 레스토랑으로 입장! (가장 중요! 🚨) 프로젝트 생성이 끝나면, 반드시 새로 만들어진 프로젝트 폴더를 열어야 합니다. VS Code 상단 메뉴
파일(File)>폴더 열기(Open Folder)를 다시 눌러, 방금 만든hello_flutter_app폴더를 선택해서 열어주세요. 이제 진짜 우리만의 주방에 제대로 들어온 겁니다!
STEP 2: 기본 메뉴판 갈아 끼우기 (코드 수정)
새로 개업한 레스토랑에는 복잡한 기본 메뉴판(카운터 앱 코드)이 걸려있어요. 이걸 우리가 원하는 아주 간단한 “Hello, World!” 메뉴판으로 바꿔봅시다.
- 메인 레시피 파일(
main.dart) 열기 VS Code 왼쪽의 파일 탐색기에서lib라는 폴더를 찾아 열고, 그 안에 있는main.dart파일을 클릭하세요. 여기가 우리 앱의 모든 것을 결정하는 핵심 레시피 파일입니다. - 기존 레시피 모두 삭제!
main.dart파일 안에 있는 코드를Ctrl+A로 전체 선택한 다음, 과감하게Delete키를 눌러 싹 다 지워주세요! 텅 빈 도화지에서 시작하는 겁니다. - 저스틴의 ‘헬로 월드’ 특제 레시피 붙여넣기 이제 텅 빈
main.dart파일에 아래의 간단한 코드를 그대로 복사해서 붙여넣으세요.
1234567891011121314151617181920212223242526272829303132333435// 1. 플러터의 기본 디자인 재료(머티리얼)를 가져옵니다.import 'package:flutter/material.dart';// 2. 모든 플러터 앱은 여기서부터 시작하라고 알려줍니다.void main() {runApp(const MyApp());}// 3. 우리 앱의 전체적인 구조를 담당하는 '설계도'입니다.class MyApp extends StatelessWidget {const MyApp({super.key});@overrideWidget build(BuildContext context) {// 4. 구글 스타일의 머티리얼 디자인 앱을 만듭니다.return MaterialApp(// 5. 앱 화면의 실제 뼈대를 구성하는 부분입니다.home: Scaffold(// 6. 화면 상단에 보이는 '제목 표시줄'입니다.appBar: AppBar(title: const Text('저스틴의 첫 앱'),backgroundColor: Colors.blue, // 색상도 한번 바꿔봤어요!),// 7. 화면의 본문 내용이 들어가는 '중심 공간'입니다.body: const Center(child: Text('Hello, World!',style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),),),),);}}
자, 여기 별표 세 개! ⭐⭐⭐ 위 코드는 플러터 앱의 가장 기본적인 구조예요. MaterialApp, Scaffold, AppBar, body 같은 위젯들이 마치 레고 블록처럼 조립되어 하나의 화면을 만드는 거죠. 지금은 “아~ 이런 블록들로 집을 짓는구나!” 하고 눈에 익혀두기만 해도 대성공입니다!
STEP 3: 드디어 시식 시간! (앱 실행하기)
자, 이제 모든 준비가 끝났습니다! 우리가 만든 따끈따끈한 ‘헬로 월드’ 요리를 직접 눈으로 확인할 시간이에요.
- 가스레인지(에뮬레이터) 켜기 앱을 실행할 가상 스마트폰, 즉 에뮬레이터가 필요해요. 안드로이드 스튜디오를 열고 AVD 매니저에서 에뮬레이터를 실행해주세요. 또는 개발자 모드를 켠 실제 폰을 USB로 컴퓨터에 연결해도 좋습니다.
- 냄비(기기) 선택하기 VS Code 창의 오른쪽 맨 아래를 보세요. ‘No Device’ 또는 다른 기기 이름이 보일 거예요.
- 이 글자를 클릭하세요!
- 화면 상단에 현재 실행 가능한 기기 목록이 나타나면, 방금 켜놓은 에뮬레이터나 연결된 실제 폰을 선택합니다.
- 마법의 F5 키, 요리 시작! 모든 준비는 끝났습니다. 키보드에서
F5키를 힘차게 누르세요!- (또는 VS Code 왼쪽의 ‘실행 및 디버그(Run and Debug)’ 아이콘을 누르고 녹색 재생 버튼을 눌러도 됩니다.)
이제 VS Code가 잠시 동안 열심히 요리(빌드)를 한 후, 여러분이 선택한 기기 화면에 “저스틴의 첫 앱”이라는 파란색 제목 표시줄과 함께, 화면 중앙에 “Hello, World!” 라는 글자가 멋지게 나타날 겁니다! 🎉
오늘 저와 함께 앱에 아주 신기하고 재미있는 마법을 부려볼 거예요. 바로 사용자의 얼굴 움직임을 실시간으로 착착 따라가는 ‘얼굴 인식’ 기능인데요. 구글의 강력한 도구, ML Kit FaceMesh를 이용하면 코딩 초보도 전문가처럼 구현할 수 있답니다.
복잡한 인공지능 이론은 잠시 접어두세요. 구글이라는 셰프가 차려놓은 근사한 밥상에 우리는 숟가락만 맛있게 얹으면 되거든요! 자, 그럼 시작해볼까요?
ML Kit? 그거 먹는 건가요? 🥪
여러분, “앱에 AI 기능을 넣고 싶은데… 어디서부터 시작해야 할지 막막해요.” 이런 고민 한 번쯤 해보셨죠? 바로 그럴 때 우리를 구해줄 영웅이 ML Kit입니다.
ML Kit을 아주 쉽게 비유하자면, ‘AI 요리 밀키트’ 같은 거예요. 구글이라는 세계 최고의 셰프가 이미 얼굴 인식, 글자 인식 같은 어려운 재료 손질(AI 모델 학습)을 다 끝내놓은 거죠. 우리는 그 밀키트(SDK)를 받아서 레시피대로 착착 조립만 하면, 아주 근사한 AI 요리(기능)가 뚝딱 완성되는 겁니다. 정말 간편하죠?
ML Kit vs MediaPipe, 자꾸 헷갈린다면? 🤔
“어? 선생님, 구글에 MediaPipe라는 비슷한 것도 있던데요?” 아주 좋은 질문이에요! 둘 다 구글에서 나왔지만 용도가 살짝 다르답니다.
자, 여기 별표 세 개! ⭐️⭐️⭐️ 이거 시험에 꼭 나와요! ML Kit이 ‘완성된 레고 세트’라면, MediaPipe는 ‘다양한 레고 부품이 담긴 통’이라고 생각하면 아주 쉬워요.
- ML Kit (레고 세트): 앱에서 얼굴 인식처럼 딱 정해진 기능을 간단하고 빠르게 구현하고 싶을 때 사용해요.
- MediaPipe (레고 부품 통): 앱뿐만 아니라 다른 플랫폼에서도 쓰고 싶거나, 기본 모델을 내 입맛대로 바꾸고 싶을 때처럼 더 높은 자유도가 필요할 때 사용하죠.
우리는 오늘 플러터(Flutter) 앱에 바로 써먹는 게 목표니까, 고민할 필요 없이 ‘완성된 레고 세트’인 ML Kit을 고르는 거예요. 감 오시나요?
플러터로 얼굴에 ‘투명 가면’ 씌우기! 🎭
자, 이제 진짜 실습 시간입니다! 플러터에서 얼굴 인식을 하려면 google_mlkit_face_mesh_detection이라는 패키지를 사용해야 해요.
이 패키지는 마치 ‘안드로이드 전문가 통역사’ 같은 역할을 해요. 우리가 플러터 언어로 “얼굴에 점 찍어줘!”라고 명령하면, 이 통역사가 안드로이드 세상으로 쪼르르 달려가 ML Kit에게 일을 시키고, 그 결과를 다시 우리에게 가져다주는 거죠. 그래서 아쉽게도 이 기능은 현재 안드로이드에서만 사용 가능하답니다.
이걸 사용하면 우리 얼굴 위에 무려 468개의 정교한 점(랜드마크)을 찍어서, 눈, 코, 입의 움직임을 실시간으로 따라가는 ‘투명 가면’을 만들 수 있어요.
468개의 점, 헷갈리지 않고 찾는 꿀팁! ✨
그런데 문제가 하나 있어요. 이 468개의 점 번호가 뒤죽박죽이라 “내 코끝이 몇 번 점이지?” 하고 찾기가 너무 어렵다는 거예요.
하지만 걱정 마세요! 우리에겐 비밀 무기가 있으니까요. 이 468개의 점들은 얼굴 위에 그려진 ‘보이지 않는 별자리’와 같아요. 그냥 번호만 봐서는 이게 무슨 별인지 알 수가 없죠. 이럴 때 필요한 게 바로 ‘별자리 지도’입니다!
개발자(cornpip)가 만든 ‘MediaPipe Face Landmark 3D Viewer’라는 사이트가 바로 그 지도 역할을 해줘요. 3D 모델을 돌려보면서 내가 원하는 위치의 점을 콕 찍으면, “그건 OOO번 별입니다!” 하고 친절하게 알려주거든요. 이제 이 지도를 보면서 원하는 부위의 번호를 쏙쏙 골라내기만 하면 되겠죠?
오늘의 정리 📝
자, 오늘 배운 내용 3줄로 요약하고 끝내겠습니다. 집중!
- 구글 ML Kit은 어려운 AI를 쉽게 쓰게 해주는 ‘AI 요리 밀키트’다.
- 플러터에서 특정 패키지를 쓰면, 안드로이드 앱에서 얼굴 위 468개 점을 실시간으로 추적할 수 있다.
- 점 번호가 헷갈릴 땐, ‘3D 뷰어’라는 별자리 지도를 활용하면 원하는 위치를 쉽게 찾을 수 있다.
여러분, 이제 AI 기능 개발이 더 이상 멀고 어렵게만 느껴지지 않으시죠? 중요한 건 모든 걸 바닥부터 만드는 게 아니라, 좋은 도구를 얼마나 잘 활용하느냐입니다!
오늘의 숙제! 본문에 소개된 깃허브 예제 코드를 꼭 다운받아서 직접 실행해보세요. 내 얼굴 위에 신기한 투명 가면이 씌워지는 걸 직접 경험하는 것만큼 좋은 공부는 없답니다!
플러터 얼굴 인식, 저만 따라오세요! (실습편)
안녕하세요, 여러분의 코딩 파트너, 일타 강사 저스틴입니다! 👨🏫✨
지난 시간에는 ML Kit이 무엇인지, 얼굴의 468개 점은 어떻게 찾는지 이론을 배웠죠? 오늘은 책상 앞에서 벗어나 직접 코드를 만져볼 시간입니다! 저스틴과 함께 한 단계 한 단계 따라오다 보면, 어느새 여러분의 앱에서도 실시간 얼굴 인식이 마법처럼 구현될 거예요.
자, 이걸 비유하자면 우리는 오늘 ‘얼굴을 따라 그림을 그리는 탐정’이 되어볼 겁니다. 준비물은 간단해요. 탐정 사무소(플러터 프로젝트), 사건 현장을 비출 손전등(카메라), 그리고 자동으로 얼굴을 본떠 주는 마법의 트레이싱지(ML Kit)만 있으면 됩니다. 준비되셨나요? 출동!
STEP 1: 탐정 사무소 차리기 (프로젝트 준비)
모든 탐정은 아늑한 사무소에서 시작하죠. 우리도 플러터 프로젝트라는 멋진 사무소를 차려봅시다.
- 프로젝트 생성: 터미널을 열고 익숙한 명령어로 새 프로젝트를 만들어주세요.
flutter create my_face_detector - 필수 도구(패키지) 빌려오기: 우리에겐 두 가지 핵심 도구가 필요해요. 바로 손전등 역할을 할
camera와, 마법의 트레이싱지인google_mlkit_face_mesh_detection이죠.pubspec.yaml파일을 열고dependencies:아래에 다음을 추가해주세요.
1234camera: ^0.10.5+9 # 최신 버전은 pub.dev에서 확인!google_mlkit_face_mesh_detection: ^0.1.0</code><code class="language-yaml">google_mlkit_commons: ^0.2.0 # InputImage 변환을 위해 필요할 수 있습니다.permission_handler: ^10.4.5 # 카메라 권한 요청을 위해 추가
이후 터미널에서 flutter pub get 명령어로 도구를 사무소에 들여놓는 것, 잊지 마세요!
STEP 2: 현장 출동 전 필수 절차! (안드로이드 설정)
탐정이 현장에 나가기 전, 허가증이 필요한 것처럼 우리 앱도 카메라 사용 권한을 미리 받아둬야 해요. 자, 여기 별표 세 개! ⭐️⭐️⭐️ 이 부분은 안드로이드 고유 설정이라 조금 낯설 수 있지만, 복사-붙여넣기만 잘하면 되니 겁먹지 마세요!
android/app/build.gradle파일을 열어서minSdkVersion을 최소 21 이상으로 설정해주세요. ML Kit이 요구하는 최소 체력 조건 같은 거예요.
123456android {defaultConfig {minSdkVersion 21}}android/app/src/main/AndroidManifest.xml파일을 열어<application>태그 바로 위에 카메라 사용 권한을 요청하는 코드를 추가합니다.
12<uses-permission android:name="android.permission.CAMERA"/>
이 두 가지만 끝내면 현장 출동 준비는 모두 완료입니다! 거의 다 왔어요!
STEP 3: 손전등(카메라) 켜고 현장 비추기 (main.dart 핵심!)
자, 이제 드디어 실제 코드를 만져볼 시간입니다! 준비된 플러터 프로젝트, 즉 우리 탐정 사무소의 lib 폴더에 main.dart 파일을 열어주세요. 이곳이 우리 앱의 심장이자, 카메라를 다루는 메인 작업 공간이 될 거예요.
3-1. 카메라 컨트롤러 초기화: 손전등에 배터리 넣고 스위치 켜기
가장 먼저, 카메라를 사용할 수 있도록 준비해야겠죠? _FaceDetectorViewState 클래스의 initState() 안에서 _initializeCamera()를 호출합니다. 이 함수 안에서 우리 앱에 달린 카메라 목록을 확인하고, 그중 첫 번째 카메라를 선택해서 CameraController를 초기화하는 거예요. 이건 마치 손전등에 배터리를 넣고 전원을 켜는 것과 똑같아요. 카메라가 준비되면 CameraPreview 위젯을 통해 화면에 카메라가 보고 있는 장면을 실시간으로 띄워줍니다. 찰칵! 📸
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
Future<void> _initializeCamera() async { if (cameras.isEmpty) { print('사용 가능한 카메라가 없습니다.'); return; } _controller = CameraController( cameras[0], // 첫 번째 카메라 사용 (보통 후면 카메라) ResolutionPreset.medium, // 해상도 설정 enableAudio: false, // 오디오는 필요 없으므로 비활성화 ); _controller!.initialize().then((_) { if (!mounted) { return; } setState(() {}); _startImageStream(); // 중요! 카메라 스트림 시작! }).catchError((Object e) { if (e is CameraException) { switch (e.code) { case 'CameraAccessDenied': print('카메라 접근이 거부되었습니다.'); break; default: print('카메라 초기화 오류: ${e.description}'); break; } } }); } |
3-2. 실시간 단서 수집: 연속 촬영 시작!
카메라가 준비되었으니, 이제 연속 촬영을 시작할 겁니다. _startImageStream() 함수를 호출하면, 카메라가 우리 눈에는 보이지 않을 정도로 빠르게 연속 사진(이미지 프레임)을 찍어서 우리에게 계속 보내주기 시작합니다. 이 사진 한 장 한 장이 바로 우리가 분석할 ‘단서’가 되는 거죠!
여기서 중요한 포인트는 _isDetecting 플래그예요. 이건 마치 “지금 탐정이 단서를 분석 중이야! 방해하지 마!” 라고 외치는 것과 같아요. 한 번에 하나의 단서만 처리해서 앱이 너무 바빠지지 않도록 조절해주는 똑똑한 장치입니다. 💡
|
1 2 3 4 5 6 7 8 9 10 |
void _startImageStream() { _controller?.startImageStream((CameraImage image) { if (!_isDetecting) { // 현재 감지 중이 아니면 _isDetecting = true; // 감지 시작 플래그 ON! _cameraImage = image; // 현재 카메라 이미지를 저장 _processCameraImage(image); // 이 이미지를 분석해! } }); } |
STEP 4: 마법의 트레이싱지로 얼굴 본뜨기 (coordinates_translator.dart와 main.dart 연동!)
자, 카메라가 끊임없이 보내주는 ‘단서 사진’들을 ML Kit이 이해할 수 있는 언어로 번역하고, 얼굴 분석을 의뢰할 시간입니다. 이 과정은 _processCameraImage() 함수 안에서 일어납니다.
4-1. 단서 형식 맞추기: ML Kit을 위한 통역사 고용!
카메라가 주는 사진(CameraImage)은 ML Kit이 바로 알아듣지 못해요. 그래서 우리는 이 사진을 InputImage라는 ML Kit 전용 형식으로 변환해주는 ‘번역’ 과정이 필요합니다. 이때 lib 폴더에 미리 만들어둔 coordinates_translator.dart 파일의 convertCameraImageToInputImage 함수가 아주 중요한 역할을 합니다. 이 친구가 카메라에서 온 복잡한 YUV 형식의 이미지를 ML Kit이 좋아하는 BGRA8888이나 NV21 같은 형식으로 번역해주는 똑똑한 통역사 역할을 해주거든요. 🗣️
|
1 2 3 4 5 6 7 8 9 10 11 |
Future<void> _processCameraImage(CameraImage cameraImage) async { final inputImage = convertCameraImageToInputImage(cameraImage, _controller!); // 통역사 등판! if (inputImage == null) { _isDetecting = false; return; } _inputImage = inputImage; // 번역된 이미지 저장! // ... (다음 단계로) } |
4-2. 얼굴 분석가 투입: 마법의 트레이싱지로 얼굴 본뜨기!
번역된 InputImage를 손에 쥐었으니, 이제 얼굴 분석가(FaceMeshDetector)를 투입할 차례입니다. 우리는 이 분석가에게 “이 이미지에서 얼굴 메시를 찾아줘!”라고 processImage 명령을 내립니다. ML Kit은 순식간에 얼굴 위에 468개의 점(랜드마크) 위치가 담긴 분석 보고서(List<FaceMesh>)를 우리에게 돌려줍니다. 와우! 🤩
이렇게 받아온 _faceMeshes 리스트는 setState()를 통해 화면을 다시 그리도록 명령하고, 다시 새로운 단서를 받을 준비를 위해 _isDetecting 플래그를 꺼줍니다. 자, 이제 이 분석 보고서를 가지고 그림을 그려볼까요?
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
// ... (이전 코드) try { final faces = await _faceMeshDetector.processImage(inputImage); // 분석가에게 의뢰! setState(() { _faceMeshes = faces; // 분석 결과 저장 }); } catch (e) { print('얼굴 메시 감지 오류: $e'); } finally { _isDetecting = false; // 다음 단서 받을 준비! } } |
STEP 5: 분석 보고서를 바탕으로 몽타주 그리기 (CustomPainter의 활약!)
마지막 단계입니다! 우리는 분석 보고서에 적힌 468개의 랜드마크 좌표를 보고, 화면 위에 점과 선을 직접 그려서 실시간 몽타주를 완성할 거예요. 이 그림을 그리는 마법의 붓은 바로 CustomPaint 위젯과 그 안에 들어가는 FaceMeshPainter입니다.
5-1. 나만의 캔버스에 그림 그리기: CustomPainter
main.dart의 build 메서드 안에서 CameraPreview 위에 CustomPaint 위젯을 겹쳐서 배치합니다. CustomPaint는 우리에게 ‘하얀 캔버스’를 제공하는 미술 도구라고 생각하면 쉬워요. 우리는 이 캔버스 위에 FaceMeshPainter를 통해 그림을 그릴 거예요.
여기서 중요한 건 좌표계의 마법! ✨ 카메라가 찍은 사진의 좌표계와 우리가 그림을 그릴 화면의 좌표계는 서로 다릅니다. 이걸 올바르게 맞춰주지 않으면, 눈은 코에 그려지고 코는 턱에 그려지는 엉뚱한 몽타주가 나올 수 있어요! 그래서 FaceMeshPainter에게 ML Kit이 분석한 이미지의 크기(mlKitImageSize)와 카메라 프리뷰 위젯의 실제 크기(previewSize), 그리고 이미지 회전 정보(rotation)까지 아주 자세하게 알려줍니다. 이 정보를 바탕으로 coordinates_translator.dart에 있는 translateX, translateY 함수들이 정확한 화면 위치를 계산해주는 거죠. 자, 여기 별표 세 개! ⭐️⭐️⭐️ 이 좌표 변환 과정이 바로 실시간 얼굴 인식의 핵심 중의 핵심입니다! 이거 시험에 나와요! 📣
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
// ... (이전 코드) return Scaffold( // ... body: Stack( children: <Widget>[ SizedBox( /* ... */ child: CameraPreview(_controller!)), // 카메라 프리뷰 if (_faceMeshes.isNotEmpty && _cameraImage != null && _inputImage != null) Positioned.fill( child: CustomPaint( // 마법 캔버스 등장! painter: FaceMeshPainter( // 우리 화가 등장! _faceMeshes, _cameraImage!, _inputImage!.metadata!.size, // ML Kit이 본 이미지 크기 _controller!.value.previewSize!, // 카메라가 보여주는 화면 크기 _inputImage!.metadata!.rotation, // 이미지 회전 정보 ), ), ), ], ), ); |
5-2. 랜드마크 점 찍고 선 잇기: 몽타주 완성!
FaceMeshPainter의 paint 메서드 안에서 우리는 드디어 몽타주를 그립니다. faceMeshes 리스트에서 각 얼굴의 468개 랜드마크(faceMesh.points)를 하나씩 꺼내옵니다. 그리고 아까 말씀드린 translateX, translateY 함수를 써서 화면의 정확한 위치를 계산한 뒤, canvas.drawCircle로 파란색 점을 콕콕 찍어주는 거예요! 지금은 점만 찍고 있지만, ML Kit 문서에서 제공하는 랜드마크 인덱스 맵을 참고하면 눈, 코, 입 같은 특정 부위를 정확하게 연결하는 빨간색 선까지 그려서 나만의 재미있는 얼굴 필터를 만들 수 있게 됩니다! 이게 바로 오늘 실습의 대미를 장식할 여러분의 숙제가 될 거예요! 🎨
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
class FaceMeshPainter extends CustomPainter { final List<FaceMesh> faceMeshes; final CameraImage cameraImage; final Size mlKitImageSize; // ML Kit이 분석한 이미지의 크기 final Size previewSize; // CameraPreview 위젯의 실제 크기 final InputImageRotation rotation; FaceMeshPainter(this.faceMeshes, this.cameraImage, this.mlKitImageSize, this.previewSize, this.rotation); @override void paint(Canvas canvas, Size size) { final Paint paint = Paint() ..color = Colors.red ..strokeWidth = 2 ..style = PaintingStyle.stroke; final Paint pointPaint = Paint() // 점 찍는 파란색 붓 ..color = Colors.blue ..strokeWidth = 3 ..style = PaintingStyle.fill; for (final faceMesh in faceMeshes) { for (final landmark in faceMesh.points) { final point = Offset( // 여기! 좌표 변환 마법이 일어나는 곳! translateX(landmark.x, rotation, size, mlKitImageSize), translateY(landmark.y, rotation, size, mlKitImageSize), ); canvas.drawCircle(point, 1, pointPaint); // 점 콕! } // TODO: 숙제를 위해 여기에 선 연결 로직을 추가해보세요! // 예시: 0번 점과 1번 점을 연결하려면... // if (faceMesh.points.length > 1) { // final p1 = Offset( // translateX(faceMesh.points[0].x, rotation, size, mlKitImageSize), // translateY(faceMesh.points[0].y, rotation, size, mlKitImageSize), // ); // final p2 = Offset( // translateX(faceMesh.points[1].x, rotation, size, mlKitImageSize), // translateY(faceMesh.points[1].y, rotation, size, mlKitImageSize), // ); // canvas.drawLine(p1, p2, paint); // } } } @override bool shouldRepaint(covariant CustomPainter oldDelegate) { return true; // 매 프레임마다 다시 그립니다. } } |
오늘의 정리 📝
자, 오늘 실습한 내용 3줄로 요약하고 마치겠습니다.
- 플러터 프로젝트에서 카메라 컨트롤러를 초기화하고,
startImageStream으로 실시간 영상 단서를 받는다. coordinates_translator.dart의 도움으로CameraImage를InputImage로 번역하고,FaceMeshDetector로 얼굴의 468개 점 좌표를 분석한다.CustomPainter와 정확한 좌표 변환을 통해 분석된 좌표를 화면 위에 점과 선으로 그려 실시간 추적을 완성한다.
어떠세요? 직접 해보니 생각보다 어렵지 않죠? 중요한 건 모든 원리를 아는 것보다, 좋은 도구를 가져와 제대로 ‘연결’하는 능력입니다.
오늘의 숙제! 완성된 앱에서 코, 입, 눈 주변의 랜드마크 번호를 직접 찾아서 그 부분만 선으로 연결해보세요! 나만의 재미있는 얼굴 필터를 만드는 첫걸음이 될 겁니다.
오늘도 정말 고생 많으셨습니다! 여러분은 이제 얼굴 인식 기능을 앱에 넣을 수 있는 멋진 개발자로 한 걸음 더 성장했습니다. 파이팅! 💪
