쓰레기가 나는 곳을 알려줄 수 있는 소프트웨어를 개발하다

네, 그것도 좋아요.라고 보도한 지 20일째다.

TL;DR


쓰레기를 넣을 곳을 찾지 못해 괴로워한 적이 있습니까?
그때는 내 동료를 돕고 싶다는 생각에 앱을 만들기로 했다.

사용법


응용 프로그램을 시작합니다.카메라를 흔들어 주세요.
그림에서 화장실의 상형도를 발견할 때 소리와 화면이 모두 너에게 알려줄 것이다.
찾은 곳은 반짝반짝 효과가 있을 거예요.
android 버전은 아래 링크에서 다운로드할 수 있습니다.
소프트웨어 2 Advent Calendar 2020

데모


다음 제작 절차


데이터 준비


인근 백화점에서 60장가량 사진을 찍었다.여기에 필요한 것은 정신력이다.
주위 사람들에게 폐를 끼치지 않고 오해를 사지 않기 위해 최신 주의를 기울이며 촬영했다.
여기서 가장 좋은 실천은'평소에는 사용하지 않지만 절대 이상하지 않은 의상','인기가 적은 시간','인기가 적은 곳','온몸에 화장실 아이콘만 찍는다','사람을 전혀 찍지 않는다'다.
또 앞으로 전달할 실천은 없다.

프리 프로세싱


사전 처리는 labelImage를 활용합니다.철점 상자를 설치하여 xml 형식으로 저장합니다.
https://play.google.com/store/apps/details?id=com.twinkle_toilet

모형 구조


tensorflow의 튜토리얼을 참고하여 모델을 구축하였습니다.나는 구글 선생님에게 고개를 들 수가 없다.
https://github.com/tzutalin/labelImg
xml을 읽을 때 붙인 탭은 다음과 같은 느낌으로 처리됩니다.
import glob
import xml.etree.ElementTree as ET
files = glob.glob(annotations_dir)

image_width = 640
image_height = 853

gt_boxes = []
image_f_names = []
for i in files:
    tree = ET.parse(i)
    root = tree.getroot()
    image_f_names.append(os.path.basename(root[2].text))
    gt_boxes.append(np.array([[
        int(root[6][4][1].text) / image_width, # x
        int(root[6][4][0].text) / image_height, # y
        int(root[6][4][3].text) / image_width, # w
        int(root[6][4][2].text) / image_height # h
    ]], dtype=np.float32))

응용 프로그램 개발


취미는 터치flutter이기 때문에 아래 창고를 참고하여 앱 부분을 설치했습니다.
간단한 앱이기 때문에 하는 일은 아래 창고와 거의 같다.
https://github.com/tensorflow/models/blob/master/research/object_detection/colab_tutorials/eager_few_shot_od_training_tflite.ipynb

사운드 출력 담당 클래스


flutter_tts라는 도서관을 사용했습니다.
https://github.com/shaqian/flutter_realtime_detection
import 'dart:async';
import 'dart:math' as math;
import 'dart:io' show Platform;
import 'package:flutter_tts/flutter_tts.dart';
import 'package:flutter/foundation.dart' show kIsWeb;

const List<String> customaryEpithet = [
  "Buddy! I found the toilet.",
  "You're lucky. That's the goal.",
  "Maybe it's the toilet.",
  "You can trust me, or you can lose everything here."
];


enum TtsState { playing, stopped, paused, continued }

class Speecher {
  static var _instance;
  factory Speecher() {
    if (_instance == null) {
      _instance = new Speecher._internal();
      _instance.initTts();
    }
    return _instance;
  }

  Speecher._internal();

  FlutterTts flutterTts;
  dynamic languages;
  String language;
  double volume = 0.5;
  double pitch = 1.0;
  double rate = 0.5;
  final random = math.Random();

  TtsState ttsState = TtsState.stopped;

  get isPlaying => ttsState == TtsState.playing;

  get isStopped => ttsState == TtsState.stopped;

  get isPaused => ttsState == TtsState.paused;

  get isContinued => ttsState == TtsState.continued;

  initTts() {
    flutterTts = FlutterTts();

    _getLanguages();

    if (!kIsWeb) {
      if (Platform.isAndroid) {
        _getEngines();
      }
    }
    flutterTts.setVolume(volume);
    flutterTts.setSpeechRate(rate);
    flutterTts.setPitch(pitch);
  }

  Future _getLanguages() async {
    languages = await flutterTts.getLanguages;
  }

  Future _getEngines() async {
    var engines = await flutterTts.getEngines;
    if (engines != null) {
      for (dynamic engine in engines) {
        print(engine);
      }
    }
  }

  Future speakText() async {
    if (ttsState == TtsState.stopped) {
      ttsState = TtsState.playing;
      await flutterTts.awaitSpeakCompletion(true);
      await flutterTts.speak(customaryEpithet[random.nextInt(customaryEpithet.length)]);
      ttsState = TtsState.stopped;
    }
    return;
  }
}

반짝이는widget 추가


glitters라는 프로그램 라이브러리를 사용했습니다.반짝이는 범위를 줄일 때의 경계선은 원래의 기본과 같다.
https://pub.dev/packages/flutter_tts
import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:glitters/glitters.dart';
import 'package:quntum_mind/resources/models/results.dart';
import 'package:quntum_mind/widgets/components/speecher.dart';

class BndBox extends StatelessWidget {
  final BndBoxResult bndboxresult;

  BndBox(this.bndboxresult);

  
  Widget build(BuildContext context) {
    Size screen = MediaQuery.of(context).size;
    final List<dynamic> results = bndboxresult.results;
    final int previewH = bndboxresult.height;
    final int previewW = bndboxresult.width;
    final double screenH = math.max(screen.height, screen.width);
    final double screenW = math.min(screen.height, screen.width);

    List<Widget> _renderBox() {
      if (results.length == 0) return [];
      if (results[0]["detectedClass"] != "toilet") return [];
      var _x = results[0]["rect"]["x"];
      var _w = results[0]["rect"]["w"];
      var _y = results[0]["rect"]["y"];
      var _h = results[0]["rect"]["h"];
      double scaleW, scaleH, x, y, w, h;

      if (screenH / screenW > previewH / previewW) {
        scaleW = screenH / previewH * previewW;
        scaleH = screenH;
        var difW = (scaleW - screenW) / scaleW;
        x = (_x - difW / 2) * scaleW - 10;
        w = _w * scaleW + 20;
        if (_x < difW / 2) w -= (difW / 2 - _x) * scaleW;
        y = _y * scaleH - 10;
        h = _h * scaleH + 20;
      } else {
        scaleH = screenW / previewW * previewH;
        scaleW = screenW;
        var difH = (scaleH - screenH) / scaleH;
        x = _x * scaleW - 10;
        w = _w * scaleW + 20;
        y = (_y - difH / 2) * scaleH - 10;
        h = _h * scaleH + 20;
        if (_y < difH / 2) h -= (difH / 2 - _y) * scaleH;
      }

      Speecher().speakText();
      return [
        Positioned(
          left: math.max(0, x),
          top: math.max(0, y),
          width: w,
          height: h,
          child: Container(
            padding: EdgeInsets.only(top: 5.0, left: 5.0),
            child: Stack(
              children: const <Widget>[
                Glitters(
                  minSize: 8.0,
                  maxSize: 20.0,
                  interval: Duration.zero,
                  maxOpacity: 0.7,
                ),
                Glitters(
                  minSize: 10.0,
                  maxSize: 25.0,
                  interval: Duration(milliseconds: 20),
                  color: Colors.lime,
                ),
                Glitters(
                  minSize: 10.0,
                  maxSize: 25.0,
                  duration: Duration(milliseconds: 200),
                  inDuration: Duration(milliseconds: 500),
                  outDuration: Duration(milliseconds: 500),
                  interval: Duration(milliseconds: 30),
                  color: Colors.white,
                  maxOpacity: 0.7,
                ),
                Glitters(
                  minSize: 14.0,
                  maxSize: 30.0,
                  interval: Duration(milliseconds: 40),
                  color: Colors.orange,
                ),
              ],
            ),
          ),
        )
      ];
    }

    return Stack(
      children: _renderBox(),
    );
  }
}

잡감


매년 Kso app을 기대했기 때문에 참가할 수 있어서 정말 좋았어요.
한편 앱은 연초 안드로이드 버전을 발표했다.
관심이 있다면 이런 쓰레기를 설치할 수 있어서 기쁩니다.

좋은 웹페이지 즐겨찾기