Создание приложения, позволяющего отмечать и сохранять аннотации к данному изображению.
Аннотации к изображениям - это процесс маркировки различных объектов на изображении. Основное применение аннотации изображений - это генерация данных, которые можно использовать для обучения алгоритмов машинного обучения. Аннотации изображений - это задача, которая в основном выполняется вручную. Работа с аннотациями изображений теперь является неотъемлемой частью машинного обучения и искусственного интеллекта и в основном передается на аутсорсинг в такие страны, как Индия и Филиппины. В этой части мы создадим приложение для аннотации изображений, которое позволит вам отмечать и сохранять аннотации на данном изображении.
Существует множество библиотек и инструментов, которые позволяют нам делать аннотации к изображениям, в том числе многие платные варианты, такие как Labelbox, но для этого приложения мы будем использовать бесплатную библиотеку Annotorious.
Прежде чем приступить к разработке части аннотации изображений, давайте быстро взглянем на внутреннюю часть нашего приложения. Annotorious - это библиотека javascript, которую можно использовать с любыми серверными фреймворками. Поскольку мне удобнее работать с рельсами, мы создадим приложение для рельсов. Давайте быстро настроим наше приложение и серверную часть.
Создадим новое приложение. Введите Терминал:
rails new image_annotater
Наше приложение должно содержать две таблицы: таблицу элементов для хранения различных изображений и таблицу меток для хранения аннотаций. Цель состоит в том, чтобы создать несколько ярлыков на изображении предмета.
В наших товарах нам нужно загружать изображения. Для этого мы можем использовать драгоценный камень несущей волны. Добавьте в GemFile следующие строки:
gem ‘carrierwave’, ‘~> 0.11.2’ gem ‘mini_magick’, ‘~> 4.8’
Я не буду вдаваться в подробности реализации несущей волны.
Сгенерируем модель Items.
rails g model Item
Также добавьте следующую строку в routes.rb
resources :items
Миграция модели должна иметь следующие поля:
image_annotater / db / migrate / 20190123073338_create_items.rb
class CreateItems < ActiveRecord::Migration[5.2] def change create_table :items do |t| t.string :name t.text :description t.string :image t.timestamps end end end
Теперь давайте создадим модель Labels:
rails g model Label
Также добавьте это в routes.rb:
resources :labels
Это самая важная таблица в этом приложении. Координаты аннотаций или прямоугольников, созданных Annotorious, должны быть сохранены в этой модели. Итак, миграция должна выглядеть так:
class CreateLabels < ActiveRecord::Migration[5.2] def change create_table :labels do |t| t.string :text t.string :context t.decimal :x_value t.decimal :y_value t.decimal :width t.decimal :height t.references :item, index: true t.timestamps end end end
Мы видим, что координаты x и y, а также высота и ширина ожидаются в аннотации вместе с текстом. Контекст - это путь к изображению. Мы также можем видеть, что есть ссылка на Item. Это означает, что один элемент или изображение может иметь несколько меток.
Модель Item должна быть такой:
/image_annotater/app/models/item.rb
class Item < ApplicationRecord mount_uploader :image, ImageUploader has_many :labels end
Модель Label должна быть такой:
/image_annotater/app/models/label.rb
class Label < ApplicationRecord belongs_to :item end
ItemsController будет обычным контроллером CRUD.
LabelsController будет таким:
class LabelsController < ApplicationController before_action :set_label, only: [:show, :edit, :update, :destroy] def index @labels = label.all end # GET /labels/1.json def show end def new @label = label.new end def edit end def create @label = Label.new(label_params) @label.save render(:json => {}, :status => :created) end def update respond_to do |format| if @label.update(label_params) render(:json => {}, :status => :updated) else render(:json => {}, :status => :not_created) end end end def destroy @label.destroy render(:json => {}, :status => :removed) end private def set_label @label = Label.find(params[:id]) end def label_params params.require(:label).permit(:text, :context, :x_value, :y_value, :width, :height, :item_id) end end
Вы можете видеть, что наш LabelsController имеет возможность создавать, обновлять и удалять новые метки.
Наша серверная часть почти готова. Давайте создадим два пользовательских интерфейса.
Первой будет форма для создания предметов. Пользователи смогут загрузить изображение и создать элемент с этой страницы.
Следующей будет страница показа для отображения предметов. Эта страница важна, так как аннотации (создание ярлыков) будут сделаны на этой странице.
<p> <strong>Name:</strong> <%= @item.name %> </p> <p> <strong>Description:</strong> <%= @item.description %> </p> <p> <strong>Image:</strong> <div> <%= image_tag(@item.image.url, size: "800x500")%> </div> <%= hidden_field_tag 'item_id', @item.id %> </p>
Известная реализация
Чтобы установить Annotorious в нашем приложении, сначала загрузите последнюю версию Annotorious с официального сайта. Загруженный zip-архив содержит файл annotorious.min.js, папку CSS с файлом annotorious.css. Папка CSS также будет содержать некоторые необходимые файлы изображений.
Если мы добавляем аннотацию в простой HTML-файл, нам нужно только добавить следующие строки в заголовок страницы:
<link type=”text/css” rel=”stylesheet” href=”css/annotorious.css” /><script type=”text/javascript” src=”annotorious.min.js”></script>
Но поскольку мы используем это в приложении rails, разделите файлы и поместите файл js в папку app / assets / javascripts, файл CSS в app / assets / stylesheets и изображения в app / assets / images.
Теперь мы можем сделать наше изображение аннотируемым. Есть два способа сделать это. Annotorious Javascript API теперь доступен на наших страницах, которые можно вызывать с помощью переменной anno.
Вариант 1. Аннотируемый класс CSS
Добавьте аннотируемый класс CSS к тегу изображения. При загрузке страницы Annotorious автоматически просканирует вашу страницу на предмет изображений с этим классом и сделает их аннотируемыми.
Пример:
<img src="example.jpg" class="annotatable" />
Вариант 2: Использование JavaScript
Annotorious Javascript API можно использовать для создания аннотаций к изображениям «вручную».
Пример:
<script> function init() { anno.makeAnnotatable(document.getElementById('myImage')); } </script> ... <body onload="init();"> <img src="example.jpg" id="myImage" /> </body>
Мы будем использовать вариант 2.
Наше изображение находится в app / views / items / show.html.erb. Добавьте к нему идентификатор, чтобы мы могли однозначно идентифицировать его.
<%= image_tag(@item.image.url, size: "800x500", id: "annotatable")%>
Теперь о JS-части. Добавьте функцию, чтобы сделать изображение с "аннотируемым" идентификатором аннотируемым.
function init() { anno.makeAnnotatable(document.getElementById(‘annotatable’)); }
Вызовите эту функцию, когда документ будет готов.
$( document ).ready(function() { init(); function init() { anno.makeAnnotatable(document.getElementById('annotatable')); } }
Теперь, если мы снова проверим страницу показа элемента и перетащим мышь на изображение, мы увидим, что можем создавать аннотации на изображении.
Потрясающие!!
Библиотека Annotorious делает всю работу за нас, и есть возможность добавлять аннотации. Но остаются еще две вещи.
- Параметры для создания, обновления и удаления меток / аннотаций из пользовательского интерфейса
- Отображать все метки на изображении элемента при загрузке страницы.
Метки Create, Delete и Update могут обрабатываться обработчиками событий, предоставляемыми Annotorious.
Идея состоит в том, чтобы получить данные аннотации и передать их в качестве функции AJAX в LabelsController, когда происходят такие события, как создание, редактирование и удаление аннотации .
Весь следующий код должен быть написан внутри нашей init()
функции.
Создайте ярлыки
Каждый раз, когда рисуется аннотация и нажимается кнопка сохранения, мы можем запустить обработчик событий onAnnotationCreated(annotation)
для переменной anno
. Итак, в нашем случае мы можем написать обработчик события как:
anno.addHandler('onAnnotationCreated', function(annotation) { var text = annotation.text; var context = annotation.src; var x = annotation.shapes[0].geometry.x; var y = annotation.shapes[0].geometry.y; var width = annotation.shapes[0].geometry.width; var height = annotation.shapes[0].geometry.height; var id = $("#item_id").val(); $.ajax({ type: 'POST', url: "/labels/", data: { label :{ text:text,context:context, x_value:x,y_value:y,width:width, height:height,item_id:id } }, success: function(data) {} }); });
Мы можем видеть, как координаты и текст из объекта аннотации извлекаются и затем передаются как AJAX в функцию Create в LabelController
.
Показать все ярлыки
Перед обновлением и удалением мы можем использовать эту опцию для отображения всех аннотаций или меток на загруженной странице. Для этого нам нужно добавить новую функцию в ItemsController
, чтобы получить все ярлыки для элемента.
#app/controllers/items_controller.rb def get_labels labels = Item.find(params[:id]).labels render json: labels end
В части JS нам нужно вызвать CreateAnnotation
метод annotorious, чтобы перерисовать все сохраненные метки. Сделать это можно так:
$.ajax({ type: "POST", dataType: "json", url: "/items/get_labels", data: { id: 6 }, success: function(data){ $.each(data, function() { var myAnnotation = {} $.each(this, function(k, v) { if(k == 'text'){ myAnnotation["text"] = v; } if(k == 'id'){ myAnnotation["id"] = v; } if(k == 'context'){ myAnnotation["src"] = v; } if(k == 'x_value'){ myAnnotation['x_value'] = v; } if(k == 'y_value'){ myAnnotation['y_value'] = v; } if(k == 'height'){ myAnnotation['height'] = v; } if(k == 'width'){ myAnnotation['width'] = v; } }); var annotation = create_annotation(myAnnotation); anno.addAnnotation(annotation) }); } }); create_annotation = function(myAnnotation_hash){ var myAnnotation = { src : myAnnotation_hash["src"], text : myAnnotation_hash["text"], shapes : [{ type : 'rect', geometry : { x : parseFloat(myAnnotation_hash["x_value"]), y: parseFloat(myAnnotation_hash["y_value"]), width : parseFloat(myAnnotation_hash["width"]), height: parseFloat(myAnnotation_hash["height"]), label_id: myAnnotation_hash["id"] } }] } return myAnnotation; } } });
Обратите внимание, что мы создали хэш данных аннотаций из базы данных и использовали этот хеш для создания аннотаций. Кроме того, вы можете видеть, что я также добавил первичный ключ метки к геометрии аннотации как label_id
. Созданная аннотация добавляется к объекту аннотации как anno.addAnnotation(annotation)
.
Обновление этикеток
Чтобы обновить текст в существующей этикетке, щелкните значок карандаша на аннотации. Будет предложено добавить новый текст:
Когда мы нажимаем на опцию Edit, запускается обработчик событий onAnnotationUpdated(annotation)
. Мы можем использовать это для обновления метки.
anno.addHandler('onAnnotationUpdated', function(annotation) { var label_id = annotation.shapes[0].geometry["label_id"]; if(label_id == "" || label_id != null){ var text = annotation.text; var context = annotation.src; var x = annotation.shapes[0].geometry.x; var y = annotation.shapes[0].geometry.y; var width = annotation.shapes[0].geometry.width; var height = annotation.shapes[0].geometry.height var item_id = $("#item_id").val(); $.ajax({ type: 'PUT', url: "/labels/"+label_id, data: { label :{ text:text, context:context, x_value:x, y_value:y, width:width, height:height, item_id: item_id } }, success: function(data) {} }); } });
Удаление этикеток
Чтобы удалить сохраненную аннотацию, щелкните значок X на аннотации. Это вызовет anno.onAnnotationRemoved
обратный вызов .
anno.addHandler('onAnnotationRemoved', function(annotation) { var label_id = annotation.shapes[0].geometry["label_id"]; if(label_id == "" || label_id != null){ $.ajax({ type: 'DELETE', url: "/labels/"+label_id, data: { }, success: function(data) {} }); } });
Вот и все.
Теперь мы можем создавать, обновлять и удалять ярлыки и получать их при перезагрузке страницы. Одним из недостатков текущей реализации является то, что для удаления или редактирования созданной метки мы должны перезагрузить страницу сейчас, потому что создание происходит через AJAX.
В Annotorious есть много других обработчиков событий и функций, которые не обсуждаются в этой статье. Ознакомьтесь с их официальной документацией.
Кроме того, в Annotorious доступно множество плагинов, попробуйте и их. Создавать плагины так просто - я даже создал плагин, чтобы добавить раскрывающийся список в текстовое поле Аннотации.
Я надеюсь, что это было полезно!