JS.19でフォトギャラリーを作成したので、これを用いてアノテーションを楽にするツールの作成を試みる。
キーボードから楽にアノテーションができるようにするため、属性(attribute
)に対して(複数の)ラベル(label
)とそれに対応するキー(key
)用意し、それぞれキーボードから指定可能にした。
- Registration で属性(
attribute
)に対する(複数の)ラベル(label
)とそれに対応するキー(key
)を登録する。 - 登録した内容は、Registered Attributesに記載されています。
- 全ての属性の登録が済んだら、画像をクリックしてフォーカス状態にする。
- その状態で先ほど登録したキーを入力すれば、対応するラベルが対応する属性に付加される。
- download ボタンを押せば、json形式でダウンロード可能。
Registration
Registered Attributes
attribute | label | command |
---|
コード
html
<div class="annotation-attrs">
<div class="registration-col" style="margin-left: 10px; float: left;">
<h2>Registration</h2>
<form id="registration-attrs-form" action="#" onsubmit="return false;">
<table>
<thead id="registration-attrs-thead">
<tr><th colspan="4">Attributes: <input id="attr-name" type="text" class="form-control" placeholder="attribute" required></th></tr>
<tr>
<th>Key</th>
<th>Label</th>
<th>Copy</th>
<th>Delete</th>
</tr>
</thead>
<tbody id="registration-attrs-tbody">
<tr>
<td><input type="text" class="command-key" maxlength="1" size="1"></td>
<td><input type="text" class="label-name" placeholder="label name"></td>
<td><input type="button" value="+" class="add" tabindex="-1"></td>
<td><input type="button" value="-" class="del" tabindex="-1"></td>
</tr>
</tbody>
<tbody id="registration-attrs-ttail">
<tr><td colspan="4">
<input id="form-reset" type="reset" name="reset" value="Clear"/>
<input type="submit" value="Register" onclick="return register_attrs();">
</td></tr>
</tbody>
</table>
</form>
</div>
<div class="registration-col" style="margin-right: 10px; float: right;">
<h2>Registered Attributes</h2>
<table id="registered-attrs">
<thead id="registered-attrs-thead">
<tr>
<th>attribute</th>
<th>label</th>
<th>command</th>
</tr>
</thead>
<tbody id="registered-attrs-tbody">
</tbody>
</table>
</div>
</div>
<div class="sample-box" style="text-align: center; width: 50%; margin: 0 auto;">
<a href="#" id="img-wrapper">
<img id="sample-img" src="https://iwasakishuto.github.io/images/profile/twitter.png">
</a>
<table id="annotation-label">
<thead id="annotation-label-thead">
<tr>
<th>attribute</th>
<th>current label</th>
</tr>
</thead>
<tbody id="annotation-label-tbody">
</tbody>
</table>
<button onclick="download_json();">download</button>
</div>
js
※ jqueryを使用した。
// Add and Delete input tags.
$(document).on("click", ".add", function() {
var target = $(this).parent().parent()
target.clone(true).insertAfter(target);
});
$(document).on("click", ".del", function() {
var target = $(this).parent().parent();
if (target.parent().children().length > 1) {
target.remove();
}
});
var sample_img_annotation = {}
$("#img-wrapper").keydown(function(event) {
const key = event.key;
if (command_keys.hasOwnProperty(key)){
attr_label = command_keys[key]
sample_img_annotation[attr_label[0]] = attr_label[1]
}
generate_annotation_table()
});
// Cancel Default event.
document.getElementById("img-wrapper").addEventListener("click", function(e){
e.preventDefault();
}, false);
// Change background-color when input is focused.
$(function() {
$('input[type="text"]')
.focusin(function(e) {
$(this).css('background-color', '#ffc');
})
.focusout(function(e) {
$(this).css('background-color', '');
});
});
// Register Annotation labels.
var anno_dataset = {}
var command_keys = {}
function register_attrs(){
var attr_name = $("#attr-name")[0].value;
var msg = "";
if (attr_name!=""){
if (attr_name in anno_dataset){
msg += "[×] ".concat("Attribute ", attr_name, " is already registered.", "\n");
}else{
var attrs = {}
msg += "[○] ".concat("Attribute ", attr_name, " is registered.", "\n");
$("#registration-attrs-tbody").children("tr").each(function(i, e) {
let inputs = e.getElementsByTagName("input");
let key = inputs[0].value;
let label = inputs[1].value;
if (key!="" && label!=""){
if (command_keys.hasOwnProperty(key)){
msg += "\t[×] ".concat(key, " is already used.", "\n");
}else{
command_keys[key] = [attr_name, label];
attrs[key] = label;
msg += "\t[○] ".concat(key, " is registered to mean label: ", label, "\n");
}
}
});
anno_dataset[attr_name] = attrs;
}
}else{
msg += "[×] Please enter 'Attribute name'.\n";
}
alert(msg);
reset_registration_table();
generate_registered_table();
generate_annotation_table();
return false;
}
// Reset Registration Form table.
function reset_registration_table(){
$("#form-reset").click();
del_btns = $("input[class='del']");
for (let i=0; i<del_btns.length; i++){
del_btns[i].click();
console.log(del_btns[i])
}
}
// Generate Registered Attributes table.
function generate_registered_table(){
var registered_tbody = $("#registered-attrs-tbody");
registered_tbody.empty();
for (let attr_name in anno_dataset){
let attrs = anno_dataset[attr_name];
let num_labels = Object.keys(attrs).length;
var row = "";
var is_attr_added = false;
for (let key in attrs){
row += "<tr>"
if (!is_attr_added){
row += "<td rowspan='" + num_labels + "'>" + attr_name + "</td>";
is_attr_added = true;
}
row += "".concat("<td>", attrs[key], "</td>");
row += "".concat("<td>", key, "</td>");
row += "</td>"
}
registered_tbody.append(row);
}
}
// Generate annotation table.
function generate_annotation_table(){
var annotation_tbody = $("#annotation-label-tbody");
annotation_tbody.empty();
var row = ""
for (let attr_name in anno_dataset){
var label = sample_img_annotation[attr_name];
var label = typeof(label) == "undefined" ? "" : label
row += "".concat("<tr>", "<td>", attr_name, "</td>", "<td>", label, "</td>");
}
annotation_tbody.append(row);
}
function download_json(){
var jsonString = JSON.stringify(sample_img_annotation, null, '\t');
var blob = new Blob([jsonString], {type: 'application/json' });
let link = document.createElement('a');
link.href = window.URL.createObjectURL(blob);
link.download = 'annotations.json';
link.click();
}