라벨이 Flutter인 게시물 표시

Flutter 에서 yolov8 사용하기 - 마무리

이번에 진행하면서 느낀 점은 "샘플 소스가 너무 없다" 주로 찾은 소스는 2년 전 지원이 끊긴 라이브러리 소스였으며 ssd_mobilenet 소스는 제공이 잘 되었는데 카메라 해상도를 높이면 작동이 되지 않았다. 나중에 공부가 깊어지다 보니 input 텐서의 값을 수정했어야 했는데 샘플소스에 input & output 텐서 속성이 박혀 있어서 그런 오류가 났었다. 혹시나 이 글이 뒤에 오는 사람에게 도움이 될까 하여 기록으로 남긴다. 전체 소스는 다음에서 볼 수 있다. https://github.com/whenyourapprun/AreYouElf.git

Flutter 에서 yolov8 사용하기 - 3

이제 카메라를 구동하고 거기서 모델을 생성하여 결과를 추출하는 모듈을 만들 것이다. 카메라 구동은 기존에 하는 방법과 비슷하다. 모델을 생성하여 탐지하는 모듈은 스레드를 4개를 설정하여 비동기 독립으로 실행하도록 한다. isolate 를 사용한다. 이렇게 하면 속도가 빠르다. 대부분 정형화 된 상태로 샘플 코드가 되어 있으니 이걸 사용하면 된다. 이제 다음과 같이 사용한다.

Flutter 에서 yolov8 사용하기 - 2

이제 생성된 모델을 가져왔으니 모델에서 값을  추출하는 nms 함수를 살펴보아야 한다. 3차원 배열로 들어온 값에서 각각 맞는 값을 추출해야 한다. 이 부분은 텐서에서 추출한 값으로 설정되도록 함수를 조정하였다. 소스는 다음과 같으니 참고하기 바란다. 이 함수에서 주목할 점은 텐서로 전달 받은 값에서 각 항목을 지정하여 추출하는 방법이다.

Flutter 에서 yolov8 사용하기 - 1

Flutter 에서 yolov8 을 사용하려면 이 방법이 가장 주효하다. 물론 더 나은 라이브러리가 제공된다면 좋을 것이다. tflite_flutter 라이브러리가 현재 공식 지원이다. 예제 등을 찾아보면 없을 것이다. 어찌하여 이미지를 추론하는 것은 찾을 수 있다. 문제는 동영상을 추론하는 게 필요하다. 그에 대한 예제는 ssd_mobilenet 모델을 제공하고 있다. ultralytics 에서 제공하는 export 방법을 사용하여 모바일용 모델을 생성한다. format='tflite' 옵션을 사용하면 3가지 속성이 default 로 설정된다. half=False, int8=False, imgsz=640 이다. 각각의 설정을 변경하면서 테스트 하여 속도를 체감해보길 권장한다. 나의 경우는 half 와 int8 은 양자화인데 속도에 크게 좌우되지 않고 정밀도를 낮추는거 같았다. imgsz 를 조정하였는데 224, 320, 4800, 640 순으로 진행하였는데 224 가 속도가 가장 빨랐다. export 하는 중에 input 텐서의 속성을 알려주는데 이게 중요하다. [1, 224, 224, 3] 으로 나올 것이다. output 텐서는 [1, 6, 1029] 이런 식으로 나올 것이다. 이 텐서의 3 ~ 4차원 행렬을 무시해서 삽질을 오래 하였다. 그리고 이런 부분에 대한 설명이 자세하지 않아서 인공지능 및 딥러닝을 전공하지 않은 개발자들에게 허들로 작용할 수 있다. 모델은 여기서 가져와서 이렇게 사용한다.

부품 인식에 관하여 그동안 일 정리

스마트폰을 활용하여 부품인식을진행하고 있다. 현재 우리 회사의 문제점은 경험 많은 퇴직자와 신입 사원 사이에 중간 사원이 없다. 그리하여 많은 문제점들이 발생하고 있다. 그에 대한 대안으로 부품 인식을 진행하고 있다. yolov8 을 활용하여 부품에 대한 이미지와 라벨링을 한다. 그 결과 .pt 파일이라는 파이토치 모델 파일을 얻는다. yolov8n.pt 를 기준으로 새로 취득한 이미지와 라벨링을 통해 학습을 진행해보았다. 처음엔 13개의 부품으로 진행했다. 이미지는 부품당 20장 정도 찍었다. 학습를 300회 정도 진행하니 그 이상은 무의미 하다 하여 그 모델 파일을 가지고 테스트 모듈을 만들었다. 테스트 모듈은 opencv 를 활용하니 잘 되었다. 하지만 한글로 부품 명칭을 보여주자는 의견에 opencv 한글 표시 하는 걸 시도하였다. 이게 조금 어려웠다. 어찌하여 그렇게 개발하여 발표를 하게 되었다. 이제 모바일에 적용 하는 게 관건이었다. 노트북에서는 GPU 환경이라 그런지 속도가 정말 빨랐다. 주로 많이 걸리는 시간이 inference 라 불리는 추론 시간인데  GPU 환경에서 20ms 정도 걸렸다. 모바일 환경으로 가니 3500ms 정도 걸려서 사용할 수 없을 정도 였다. 속도는 둘째이고 정확도도 엉망이었다. 예제 소스도 없었다. 진짜 어렵게 진행해서 이런 문제들을 해결하였다. 이제 그걸 하나 씩 정리하고자 한다.

prototype 웹 모듈에 대하여

  항상 고민한 주제가 있습니다. 선로 주변에서 작업하는 사람들의 안전문제입니다. 열차의 정확한 위치를 알아야 작업자 안전이 강화됩니다. 열차에 내비게이션을 달아서 위치를 확보했습니다. 하지만 GPS 에 의존하는 위치정보는 의외로 많은 음영구간을 가지고 있습니다. 음영구간에도 위치를 알 수 있는 데이터가 필요했습니다. 그리고 그 데이터를 찾았습니다. 정말 많은 시간이 걸렸습니다. 데이터를 패킷 단위에서 분석하여 유효한 정보를 추출합니다. 이 정보를 활용하여 좀 더 안전한 작업환경을 만들 수 없을까 고민하고 있습니다. 프로토타입 화면을 하나 만들어 보았습니다. 이 데이터를 활용하면 열차가 정확히 어느 지점을 지나는 지 알 수 있습니다. 음영구간에서도 위치정보를 알 수 있는 데이터입니다. Flutter 로 작성하여 웹 어셈블리로 포팅한 모듈입니다. 앞으로 가야할 길이 많이 남았지만 그래도 묵묵히 주어진 길을 가야겠습니다. 이게 운명이라면..., https://ctc.whenyourapprun.com

Flutter 에서 수면 알람 기능 제작

 기존에 만들어 놓은 앱이 있다. 안드로이드 코틀린으로 아이폰 스위프트로 작성하였다. 이를 수정하기로 하였다. Flutter 로 작성해서 개발 공수를 줄이자고 결정했다. 다른 화면은 모두 순조롭게 구현이 되었지만 알람 기능이 문제였다. flutter package 에 alarm 이 있다. 이를 적용하고자 했으나 추가하면 오류가 났다. 깃헙에 이슈에 등록해도 클린 후 다시 적용만 하라고 했는데 개발자 중에 그걸 안하고 이슈에 올릴 사람은 적을 것이다. 이후 답변이 없고 마냥 기다릴 수 없어서 다른 패키지를 검색했다. flutter_local_notification 패키지를 이용하는 것이다. 안드로이드를 대상으로 하니 잘 된다. 문제는 아이폰에서 음원 반복이 되지 않았다. 아침에 깨워주는 기능인데 30초 음원으로 만들어서 등록해도 30초만 플레이 되는 문제가 발생되었다. 이를 해결하고자 아이폰 알람은 직접 제작하기로 했다. 메소드 채널이란 기능을 이용하면 플러터에서 스위프트 함수를 호출할 수 있다. 스위프트로 로컬 노티피케이션 기능을 활용하여 알람을 구현하였다. 앱이 켜져 있는 상태인 Foreground 에서 알람 기능은 잘 작동하였다. 하지만 꺼져 있는 상태인 Background 에서는 잘 작동하지 않았다. 아침에 알람이 30초 동안만 울린다. 음원 반복재생이 되지 않았다. 백그라운드 패치를 이용해서 매분마다 체크를 구현하다가 너무 이건 아닌가 싶어 포기했다. 푸시 기능을 활용하면 didReceiveRemoteNotification 에서 이벤트를 잡을 수 있다. 나머지 이벤트는 알람창을 터치하면 뜨는 이벤트이다. 고로 알람 시간을 추가하면 서버에 토큰과 시간을 등록하고 그 시간이 되면 서버에서 Slient Push Message 를 보낸다. 앱은 알람을 즉시 작성해서 창을 띄우고 음원을 반복재생한다. 이런 구성으로 구현하니 잘 작동된다.

Flutter 에서 Alarm 모듈 사용하기

일단 Alarm 이 안드로이드 와 아이폰 작동방식이 다르다. 양쪽 다 지원하려면 모듈을 사용해야 한다. 먼저 alarm 1.0.3 모듈이 보인다. 이를 적용하려고 보니 오류가 난다. 깃헙에 이슈 사항에 올렸는데 해결했는지 모르겠다. 좀 더 찾아보니 flutter_local_notification 13.0.0 이 나온다. 이 모듈이 좀 더 안정적이다. 샘플 코드가 너무 복잡해서 난해하다. 하나씩 해보니 되긴 된다. 사운드를 기본 음에서 변경할 때 매뉴얼 대로 했는데 되지 않는다. 채널 아이디를 테스트 할 때마다 변경하거나 앱을 삭제 한 후 해야 한다. 매뉴얼에 영어로 한줄 있었는데 간과해서 몇시간을 고통 속에 있었다.

Flutter 에서 confetti 사용할 때 Fatal signal 5 오류 조치 방법

이미지
Flutter 에서 Confetti 를 사용하면 축하 애니메이션을 만들기 쉽다. 안드로이드에서만 아래와 같은 오류가 난다. Confetti 를 play 하면 오류가 나는데 안드로이드에서만 난다. 아이폰과 웹에서는 오류가 나지 않는다. 관련 자료를 검색해도 나오지 않는데 이곳에서 이런 논쟁이 있었다. https://github.com/funwithflutter/flutter_confetti/issues/78 뭐 대충 보면 Flutter 버전을 올리면 되는거 같다. 현재 나의 Flutter 버전을 확인 해본 결과 이전 버전으로 나왔다. 비주얼 코드의 마켓 플레이스에서 설치했는데 이게 최신이 아닌가 보다. 일단 아래에서 권장 한대로 명령어를 실행해보았다. https://docs.flutter.dev/development/tools/sdk/upgrading $ flutter upgrade  현재 버전에서 더이상 할게 없다고 나온다. $ flutter channel 여러개가 나온다. 여기서 나는 main 으로 변경했다. $ flutter channel main $ flutter upgrade 업그레이드가 된다. 이후 실행해보니 오류가 나지 않는다.

모바일 앱 개발 언어 선정할때 검토사항

모바일 앱을 개발할 때 언어를 선택해야 하는 경우가 많습니다. 일단 저는 아이폰 개발을 시작으로 안드로이드로 확장된 경험을 가지고 있습니다. 아이폰은 Objective-C 부터 Swift 그리고 SwiftUI 까지 개발한 경험을 가지고 있습니다. 안드로이드는 Java 부터 Kotlin 그리고 Jetpack Compose 까지 개발한 경험을 가지고 있습니다. 아이폰으로 먼저 화면과 기능을 구현한 다음 안드로이드를 구현하는 방식으로 개발했습니다. 왜냐하면 아이폰이 저에게는 더 익숙한 환경이었거든요. 아무래도 조건이 더 까다로워서 이것부터 맞춘 다음 안드로이드 하면 더 쉬운 느낌이었으니까요. 이런 작업을 하다 보면 같은 작업을 반복한다는 생각이 많이 듭니다. 개발 도중 현타도 많이 옵니다. 이게 뭐하는 건지....., 그래서 시도한 방법은 KMM(Kotlin Multiplatform Mobile) 적용이었습니다. 이것도 굉장한 물건입니다. 공통 모듈로 뺄 수 있는 부분이 내부 데이터베이스와 API 호출 하는 모듈을 작성하였습니다. 이것만 되어도 굉장한 코딩 중복이 사라집니다. 큰 앱은 이방법을 사용하시면 됩니다. 각 디바이스 마다 적용해야 할 제약이 크다면 이정도만 해도 굉장한 중복제거 효과를 볼 수 있습니다. 만약 좀 더 나아가서 기획 쪽까지 틀 수 있는 정도면 Flutter 를 추천합니다. 저도 사실 예전에 Flutter 시도를 했었다 실패한 경험이 있습니다. 아직도 camera 부분이나 machine learning 부분 등이 버전이 1.0대가 아니어서 많이 느리지만 그래도 구현은 됩니다. Flutter 로 개발하면서 느낀 점은 아이폰과 안드로이드 설정은 똑같이 해야 한다는 점입니다. 그래들과 info.plist 등 설정이 필요합니다.

Flutter 에서 카메라로 찍은 이미지 사진첩에 저장하고 가져오는 방법

이미지
Flutter 에서 카메라로 사진을 찍고 저장해서 가져올때 getApplicationDocumentDirectory 의 위치가 실행때마다 달라진다. 내가 찾은 방법은 이렇다. 사진을 Photo Library 에 저장하고 가져와서 사용하면 된다. 일단 저장하는 방법은 이렇다. 이제 가져올때 이런 방법으로 가져온다. 앨범을 가져오고 앨범의 이미지들을 리스트에 넣고 거기서 ID 값을 비교하면 된다. 이미지 표출은 이런 방식으로 한다. 테스트 해보니 잘된다.

Flutter 로 iOS 에 저장된 getApplicationDocumentDirectory 위치가 달라짐

Flutter 에서 iOS 기기의 앱 도큐먼트에 사진을 저장하고 있다. 이 사진 파일의 패스와 이름을 내부 데이터베이스에 저장하여  필요할 때마다 불러와서 사용하고 있다. 갱신이 필요하여 소스를 수정 하였는데 이전에 저장한 이미지가 전부 오류났다. 확인해보니 패스가 달라진 것이다. 이전에 저장한 패스는  /var/mobile/Containers/Data/Application/89AB9292-2F1F-4B9C-8EC7-2A45E08DBC2D/Documents 소스 수정 후 불러온 패스는 /var/mobile/Containers/Data/Application/76D29B57-B94C-4281-B948-677590233200/Documents 찾아보니 Bundle ID 같으면 패스가 같다고 하는데 다르게 나온다. 아무래도 다른 방법을 찾아야 할 거 같다.

chatGPT 를 활용하여 Flutter 에서 Animated Button 작성하는 방법

이미지
chatGPT 에서 아래와 같이 질문을 해보았다. 예제 코드가 위와 같이 나오면서 한글로 설명도 나옵니다. 코드의 품질도 우수합니다. class AnimatedButton extends StatefulWidget {   final String buttonText;   AnimatedButton({this.buttonText});   @override   _AnimatedButtonState createState() => _AnimatedButtonState(); } class _AnimatedButtonState extends State<AnimatedButton> {   double _buttonSize = 150.0;   @override   Widget build(BuildContext context) {     return GestureDetector(       onTap: () {         setState(() {           _buttonSize = 200.0;         });       },       child: AnimatedContainer(         duration: Duration(milliseconds: 300),         width: _buttonSize,         height: _buttonSize,         decoration: BoxDecoration(           color: Colors.blue,           borderRadius: BorderRadius.circular(20.0),         ),         child: Center(           child: Text(             widget.buttonText,             style: TextStyle(color: Colors.white),           ),         ),       ),     );   } } 구글 검색에서 찾은 코드와 비교해도 손색이 없을 정도

Flutter 에서 Preview Page 내용 Update 하는 방법

이미지
Flutter 에서 Preview Page 를 Update 해야 하는 경우가 종종 발생한다. 나는 이런 경우가 그렇다. A 는 리스트뷰로 각 아이템들의 내용을 보여준다. B 는 A 의 하단 추가 버튼을 클릭 했을 때 나오는 화면으로 각 아이템 추가한다. 여기서 문제가 발생한다. B 에서 아이템을 추가 한 후 창을 닫으면 A 화면으로 간다. 근데 방금 추가된 아이템이 리스트에 반영이 안되어 있다. Flutter 에서 각 화면 이동을 go_router 를 사용해서 전환하고 있다. 화면 전환간 데이터 전달은 extra 를 사용했다. 하지만 extra 는 한번만 사용 가능하다. 예를 들어 A -> B 로 extra 를 사용했다면 A -> C 에서는 사용할 수 없다. 다른 화면 간에도 데이터 전달을 하려면 params 를 사용해야 한다. 라우터를 생성할 때 이렇게 한다. 전달할 때 refresh 를 문자열로 했다. 전달한 refresh 를 받아줄 변수를 클래스에 추가한다. 이제 클래스 파일 빌드 부분에 다음을 추가해서 가져온 값에 의해 자료가 갱신되도록 추가하자. 화면이 그려질 때 refresh 값이 'true' 이면 이를 'false' 로 변경 한 다음 데이터베이스에서 아이템을 가져와서 리스트를 갱신한다. 여기서 'false' 로 변경하지 않으면 무한 반복 되므로 한번만 실행하도록 사용했습니다. 이제 하위창에서 어떻게 호출하여 갱신을 시키는지 확인해보겠습니다. 이런 방식으로 하면 여러 화면에 걸쳐 파라미터를 전달 할 수 있고 전달된 파라미터 값에 따라 코딩을 할 수 있다. 사용법이 익숙해지면 Flutter 꽤 괜찮은 개발언어가 될 수 있을꺼 같다.