機械学習のための画像ラベリングを支援するWebアプリケーションを作ってみた

# はじめに

画像から特定の部分を切り出して機械学習や深層学習をさせようとした時、対象の画像を切り出すための仕組みが必要だったので、ラベリングを支援するためのWebアプリケーションを作ってみた。

# 画像とラベルについて

画像に0からの連番で「xxx.png」というファイル名をつけて保存します。

ここでは、重量計のメーターをサンプルに使用し、下図のとおり左上と左下の座標とメーターの値をラベリングするものとします。

![ラベリング作業説明書.001.jpeg](https://qiita-image-store.s3.amazonaws.com/0/114240/af5f0ed3-b957-6916-3fcf-9f6d2c200fb7.jpeg)

ちなみに、画像のサイズは1280x720。

# 機能

対象物の左上をクリックし、右下をクリックするとクリックした座標を取得してテキストボックスに入力し、メーターの値を入力するテキストボックスにフォーカスを移す。
メーターの値を入力し終わったら、ボタンをクリックして送信することで、データを記録します。

新しいデータの設定が終わったら、次の画像を表示します。

# フォルダ構成

- index.rb  
  スクリプト用ファイル
- img  
  画像保存用フォルダ
- db
  ラベルデータ保存用フォルダ

# データベース準備

以下のコマンドを実行し、テーブルを作成。

```
$ sqlite3 db/param.db
SQLite version 3.6.20
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> CREATE TABLE `param` (
   ...>   id integer,
   ...>   left integer,
   ...>   right integer,
   ...>   top integer,
   ...>   bottom integer,
   ...>   val integer
   ...> );
sqlite> .exit
$ sudo chown -R apache db
```

# スクリプト作成

```rb:index.rb
#!/usr/bin/env ruby

require "cgi"
require "sqlite3"

cgi = CGI.new()

puts cgi.header()

# データベースへの接続
db = SQLite3::Database.new("db/param.db")

# パラメータの受取
$_GET = {}
cgi.params.each do |key, val|
  $_GET[key] = val[0]
end

# ファイル番号設定
id = 0
if $_GET.size > 0 then
  id = $_GET["id"].to_i
end

# ラベルデータ保存
if $_GET["submit"] == "set" then
  # 古いデータの削除（なくても実行）
  sql = "DELETE from param WHERE id=#{id}"
  db.execute(sql)
  
  # 新しいデータの保存
  sql = <<EOF
INSERT INTO param
VALUES (
  #{id},
  #{$_GET["left"]},
  #{$_GET["right"]},
  #{$_GET["top"]},
  #{$_GET["bottom"]},
  #{$_GET["val"]}
);
EOF
  db.execute(sql)
  id += 1
end

filename = sprintf("%03d.png",id)

# 既存データの取得
sql = "SELECT * FROM param WHERE id=#{id}"

left = 0
right = 0
top = 0
bottom = 0
val = 0

rs = db.execute2(sql)

flg = false

rs.each do |row|
  
  if flg then
    
    left = row[1].to_i
    right = row[2].to_i
    top = row[3].to_i
    bottom = row[4].to_i
    val = row[5].to_i
    
  end
  
  flg = true
  
end

# HTMLの作成
html = <<EOF
<html>
<head>
  <title>LABELING TOOL for ML</title>
  <style>
* {
	font-size: 15px;
	margin: 0px;
	padding: 0px;
}
div {
	border: 0px solid #F00;
}
form td {
	padding: 5px;
}

#page {
	width: 800px;
}
#head {
	background-color: #CCC;
	height: 100px;
}
#head .title {
	font-size: 30px;
	line-height: 100px;
	padding-left: 10px;
}
#body {
	background-color: #FFF;
}
#foot {
	background-color: #CCC;
	height: 50px;
}

  </style>
  <script>
var img;
var frm;

function init() {
	img = document.getElementById("img");
	frm = document.forms.params;
}

function get_point(e) {
	r = 1280 / 800
	x = e.pageX * r;
	y = (e.pageY - 100) * r ;
	
	if (frm.pos.value=="left") {
		frm.left.value = Math.round(x);
		frm.top.value = Math.round(y);
		frm.pos.value = "right";
	} else {
		frm.right.value = Math.round(x);
		frm.bottom.value = Math.round(y);
		frm.val.focus();
	}
}
  </script>
</head>
<body onload="init()">
  <div id="page">
    <div id="head">
      <div class="title">LABELING TOOL for ML</div>
    </div>
    <div id="body">
      <div id="img">
        <img src="img/#{filename}" width="100%" onclick="get_point(event);" />
      </div>
      <form name="params">
        <table>
          <tr>
            <td>&nbsp;</td>
            <td>ID:</td><td><input type="text" name="id" size=5 value="#{id}" /></td>
            <td><input type="submit" name="submit" value="load" /></td><td>&nbsp;</td>
          </tr>
          <tr>
            <td><input type="radio" name="pos" value="left" checked /></td>
            <td>LEFT:</td><td><input type="text" name="left" size=5 value="#{left}" /></td>
            <td>TOP:</td><td><input type="text" name="top" size=5 value="#{top}" /></td>
          </tr>
          <tr>
            <td><input type="radio" name="pos" value="right" /></td>
            <td>RIGHT:</td><td><input type="text" name="right" size=5 value="#{right}" /></td>
            <td>BOTTOM:</td><td><input type="text" name="bottom" size=5 value="#{bottom}" /></td>
          </tr>
          <tr>
            <td>&nbsp;</td>
            <td>VALUE:</td><td><input type="text" name="val" size=5 value="#{val}" /></td>
            <td>&nbsp;</td><td>&nbsp;</td>
          </tr>
          <tr>
            <td>&nbsp;</td>
            <td>&nbsp;</td><td><input type="submit" name="submit" value="set" /></td>
            <td>&nbsp;</td><td>&nbsp;</td>
          </tr>
        </table>
      </form>
    </div>
    <div id="foot">
    </div>
  </div>
</body>
</html>
EOF

# HTMLの出力
puts html
```

# 完成図

![スクリーンショット 2017-11-24 5.57.06.png](https://qiita-image-store.s3.amazonaws.com/0/114240/65b050f6-b431-a7a3-fb53-1b4100a0b7db.png)

作成したラベルデータで画像を切り出してみると、こんな感じ(^-^)

![Unknown.png](https://qiita-image-store.s3.amazonaws.com/0/114240/39383610-f38d-eb69-f714-e7d042ab1d2d.png)

これでラベリング作業を外注する準備ができました！