개요
학습된 머신러닝 모델들을 찾을 수 있는 tfhub에서 cartoongan 모델을 발견하게 되었습니다.
이미지를 만화같은 스타일로 변환해주는 모델인데 재미있을 것 같아서 모델 파일을 다운로드 받고 플러터 프로젝트에 적용 해봤습니다.
그런데 실제 적용 했을 때 몇 가지 문제가 있었습니다.
Flutter에는 공식적인 tflite 라이브러리가 없어서 서드파티 라이브러리를 사용하는데 실제 동작하지 않는 모델이 많이 있었습니다.
사용할 수 있는 모델인 경우에도 입력가능한 이미지 사이즈 크기 제한이 있어 이미지를 리사이징해서 사용하는데,
만화 스타일로 변화된 이미지도 똑같이 화질이 안좋아서 제대로 사용할 수 없는 상태였습니다.
마침 Colab으로 cartoongan을 적용할 수 있는 프로젝트가 있어서 해당 프로젝트를 기반으로 직접 파이썬 서버를 만들어서 Flutter에 적용 해보기로 했습니다.
파이썬 서버 만들어보기
파이썬으로 서버를 만들어 보는 것은 처음이라서 여러가지를 찾아보다가 Flask를 이용하여 API 서버를 만들어 보기로 했습니다.
Flask는 파이썬으로 웹 애플리케이션 개발을 위한 프레임워크로, 최소한의 구성 요소와 요구사항을 제공하기 때문에 필요한 기능만 간단하게 개발하기에 적합해서 선택하게 되었습니다.
Django도 유명한데 Flask보다 무겁고 제공하는 기능이 많아서 간단한 API 서버를 만드는 목적에는 적합하지 않은 것 같아서 제외 하게 되었습니다.
환경설정
Colab의 cartoongan 프로젝트의 경우 오래된 프로젝트라서 tensorflow 1로 구현이 되어 있습니다.
파이썬 최신버전을 사용하면 동작하지 않기 떄문에 다음과 같이 tensorflow 1.x 버전을 사용할 수 있는 파이썬의 가상환경을 만들고 환경설정을 진행합니다.
# Windows
virtualenv ml --python=3.7.9
cd ml
cd scripts
activate.bat
# Mac
source ml/bin/activate
프로젝트에서 필요한 라이브러리를 설치합니다.
(git에서 필요한 라이브러리가 설정 된 프로젝트를 확인할 수 있습니다.)
pip install -r requirements.txt
파이썬 Flask 프로젝트의 기본 설정을 합니다.
cartoongan 모델이 적용 된 API를 만들기 위해서 아래와 같이 틀을 잡았습니다.
@app.route로 API경로와 methods를 설정할 수 있습니다.
간단하게 /v1/image/convert_cartoon 으로 이름을 정하고 POST 방식으로 설정을 했습니다.
base64 형태로 인코딩 된 이미지 데이터를 JSON 형태로 받아서 image로 확인하고 있습니다.
# app.py
from flask import Flask, request, jsonify
from werkzeug.serving import WSGIRequestHandler
from v1.image.cartoongan import cartoongan
app = Flask(__name__)
@app.route('/v1/image/convert_cartoon', methods = ['POST'])
def convert_cartoon():
data = request.get_json()
if 'image' not in data:
return "", 400
cartoon_img_data = cartoongan.convert(data['image'])
return cartoon_img_data, 200
@app.route("/")
def index():
return "<h1>Welcome to puzzleleaf ml server !!</h1>"
if __name__ == "__main__":
# https://stackoverflow.com/questions/63765727/unhandled-exception-connection-closed-while-receiving-data
WSGIRequestHandler.protocol_version = "HTTP/1.1"
app.run(threaded=True, host='0.0.0.0', port=5000)
디코딩된 이미지 데이터를 사용하기 위해 프로젝트에 저장된 cartoongan 모델을 불러옵니다.
# cartoongan.py
import base64
from . import cartoonize
def convert(image):
model_path = './v1/image/cartoongan/saved_models'
imgdata = base64.b64decode(image)
cartoon_img_data = cartoonize.cartoonize(imgdata, model_path)
return base64.b64encode(cartoon_img_data)
이미지 데이터를 RGB 이미지로 변환하고 resize 과정을 수행합니다.
모델에 변환된 이미지를 적용하여 만화 스타일로 변환 된 이미지를 얻을 수 있습니다.
최종적으로 cv2로 이미지를 인코딩하는 작업을 거쳐서 반환하게 됩니다.
이렇게 만든 API 서버를 로컬 서버가 아닌 Heroku를 통해서 배포해봤습니다.
Heroku
Heroku는 컨테이너 기반 클라우드 PaaS (Platform as a Service)입니다.
개발자는 Heroku를 사용하여 최신 앱을 배포, 관리를 쉽게할 수 있습니다. 무료로 배포하고 사용할 수 있어서 Heroku를 적용하게 되었습니다.
(무료일 경우 500MB 용량 제한과 약 5~15분 동안 사용이 없으면 서버가 자동으로 정지되고 이후에 다시 재 시작하는데 20~30초 정도가 소요됩니다.)
파이썬 애플리케이션을 배포하기 위한 가이드를 제공하고 있어서 쉽게 배포할 수 있습니다. 이외에도 다양한 언어들을 지원하고 있습니다.
Heroku 페이지에서 프로젝트를 생성하고 Github과 연동해서 배포 설정을 할 수 있습니다.
master에 push가 되면 자동으로 배포가 되도록 설정했습니다.
API 서버
puzzleleaf-ml-server.herokuapp.com/
API 적용하기
실제 Flutter에서 다음과 같이 API 호출을 통해서 사용할 수 있습니다.
서버로 전달받은 이미지를 디코딩하는데 시간이 소요되어서 compute를 통해 백그라운드에서 처리할 수 있습니다.
https://flutter-ko.dev/docs/cookbook/networking/background-parsing
import 'dart:convert';
import 'dart:typed_data';
import 'package:dio/dio.dart';
import 'package:flutter/foundation.dart';
class MLService {
Dio dio = Dio();
// ml server
// https://github.com/PuzzleLeaf/tensorflow_flask_api_server
Future<Uint8List> convertCartoonImage (Uint8List imageData) async {
try {
var encodedData = await compute(base64Encode, imageData);
Response response = await dio.post('https://puzzleleaf-ml-server.herokuapp.com/v1/image/convert_cartoon',
data: {
'image': encodedData
}
);
String result = response.data;
return compute(base64Decode, result);
} catch (e) {
return null;
}
}
}
갤러리에서 이미지를 선택하고 서버에서 이미지를 처리 후 다시 전달하는 예제입니다.
전체적인 코드나 구현 방법은 아래의 링크를 통해서 확인할 수 있습니다.
Flask API Server
Flutter Client
'Flutter' 카테고리의 다른 글
Flutter(플러터) 클럽하우스 Clubhouse 클론 앱 만들기 (0) | 2021.03.17 |
---|---|
Flutter(플러터) 카카오뱅크 클론 앱 만들기 (2) | 2020.10.12 |
플러터(Flutter) - Gradient Button (1) | 2020.07.11 |
[Dart] 비동기 프로그래밍 (Isolates, Event Loops, Future) (0) | 2020.01.23 |
[Flutter] BoxDecoration으로 심플한 UI 만들기 (0) | 2020.01.15 |
댓글