Leaderboard Register and Join the Competition

Stars Finder

Find stars in noisy images.

Description
Dataset
Metric
Evaluation
Baseline
Functional Check
Limits
Rules
Timeline
Prizes
Links
Discussion
Contacts
FAQ

Description

Задача: определить координаты центра нарисованной звезды на зашумлённом изображении.

В социальных сетях люди часто играют в игру "Найди кота!", где предлагается на реальной фотографии указать местоположение зверя защитного окраса. Человеку это удаётся не всегда. В реальных задачах искать требуется не кота, а, например, заблудившихся людей по фото с дронов. Человеку нужно максимально быстро глазами просмотреть много кадров, а цена ошибки высока.

Попробуйте разработать решение, которое в поиске объектов на изображении обгонит человека по скорости и точности.

Для обучения модели за приемлемое время скорее всего понадобится GPU. Если у Вас его нет, может выручить бесплатный Google Colaboratory.

Dataset

Датасет сгенерённый. Сначала на чистом изображении произвольного размера случайным оттенком серого рисовалось множество случайных линий. Потом опять случайным оттенком серого рисовалась звезда. Потом ещё несколько линий поверх звезды для зашумления. И далее сохранялось в JPEG с низким качеством также для добавления шума. Параметры генерации датасета были подобраны так, чтобы на большинстве изображений человеческий глаз мог найти звезду.

Публичный и приватный датасет генерились с одинаковыми параметрами. Каждый содержит по 100 000 изображений.

Датасет состоит из изображений с одной звездой и целочисленных координат центра звезды. Звезда нарисована на каждом изображении. Радиусы и количество лучей у звёзд варьируются. Максимальный возможный размер изображений 256 на 256 точек. Все изображения монохромные в формате JPEG.

Публичный датасет можно скачать здесь: 3.zip.

Metric

Качество модели оценивается метрикой mean squared error (MSE). Итоговый скор считается как 100500 / (MSE + 1E-7). Чем меньше MSE, тем больше итоговый скор, и тем качество модели выше.

Пример расчёта скора. Центр звезды имеет координаты (23, 17). Модель предсказала координаты центра (20, 25). Тогда квадрат ошибки этого предсказания равен (23-20)^2 + (17-25)^2 = 9 + 64 = 73. Допустим, что у нас есть ещё два предсказания, и по ним квадраты ошибок равны 25 и 47. Тогда MSE = (73 + 25 + 47) / 3 = 48.3333333. Итоговый скор: 100500 / (48.3333333 + 0.0000001) = 2079.31034196.

Evaluation

Сабмит запускается на публичном и приватном датасетах. Оценка качества модели ведётся по второму датасету. Значения метрики доступны по обеим датасетам.

Сабмит - это zip-архив. Он состоит из текстовых файлов, описывающих необходимый для установки софт, и делающих предсказания скриптов.

Если Вам не важны подробности и не терпится сделать первый сабмит, то можно перейти в конец раздела, там взять рабочий вариант сабмита и сделать свою модель на его основе.

Довольно распространённый вариант запуска ML-решений - это демон, принимающий запросы и выдающий на них в ответ предикты. Демонов легко эксплуатировать, тестировать, мониторить и замерять их производительность. Такой демон уже написан на базе веб-сервера tornado и работает по HTTP. Если он чем-то Вас не устраивает, можно запускать любой другой веб-сервер. Например, Flask или aiohttp.

Часто демонов помещают внутрь Docker-контейнеров. Создание контейнеров требует определённых навыков, а хотелось простоты и универсальности. Поэтому предустановлено много потенциально полезного софта. Что именно - можно посмотреть, зайдя в любой сабмит соревнования. Ниже описано, как установить необходимый софт.

apt.txt описывает пакеты OS Ubuntu, которые будут установлены командой sudo apt install перед запуском кода сабмита. Если в сабмите нет apt.txt, то никакие дополнительные пакеты Ubuntu не устанавливаются. Пример содержимого apt.txt:

ffmpeg
graphviz

python.txt описывает модули языка Python 3, которые будут установлены командой pip3 install перед запуском кода сабмита. Если в сабмите нет python.txt, то никакие дополнительные модули Python-а не устанавливаются. Пример содержимого python.txt:

sklearn
scipy
scikit-image
tornado

run.sh - это shell-скрипт. Он автоматически начнёт выполняться после установки софта. В нём описывается, что ещё запустится для корректной работы сабмита. Содержимое сабмита разархивируется в директорию ~/submit/, поэтому остальные скрипты, например, написанные на Python-е, запускаются из этой директории. Также run.sh получает два параметра запуска: адрес и порт, на котором код сабмита будет принимать запросы. Пример содержимого скрипта run.sh:

#!/bin/sh -x

/usr/bin/python3 ~/submit/predict.py --model ~/submit/model.h5 --host $1 --port $2

Код сабмита должен принимать http-запросы на том адресе и порту, которые были переданы run.sh. Туда отправляются монохромные изображения размером не более 256х256 в формате jpeg.

Запросы и ответы делаются по протоколу HTTP 1.1. Правильный ответ на правильный запрос содержит 200-ый код. При ошибке в запросе или в переданных данных правильным будет ответ с 400-ым кодом. В случае любых других ошибок правильным будет ответ с 500-ым кодом.

Все выдаваемые сабмитом ответы имеют JSON-формат. Важно иметь ввиду, что может быть отправлено несколько параллельных запросов. Запросы делаются к трём url-ам: /status, /predict и /exit.

На GET-запросы с url-ом /status правильным будет ответ с содержимым {"status": "Ok"}. Смысл запросов к статусу в том, чтобы быстро убедиться, что сабмит запущен и отвечает на запросы.

В POST-запросах с url-ом /predict будут отправляться одно или несколько изображений с именем параметра x. На них сабмит должен отвечать массивом пар предсказанных x- и y-координат центров звезд в поле y. Пример ответа на запрос с тремя изображениями:

{
  "y": [
    {
      "x": 10,
      "y": 20
    },
    {
      "x": 30,
      "y": 40
    },
    {
      "x": 50,
      "y": 60
    }
  ]
}
Пример ответа на запрос с одним изображением:
{
  "y": [
    {
      "x": 70,
      "y": 80
    }
  ]
}

На GET-запросы с url-ом /exit корректным будет ответ с содержимым {"exit": "Ok"}. После отправки ответа сабмит должен тут же закончить свою работу с нулевым exit code, что скажет о нормальном завершении работы. Это нужно для быстрой и корректной остановки сабмита.

Для проверки работоспособности написанного кода сабмита можно использовать curl. Пример отправки запроса с двумя изображениями, находящихся в файлах 1.jpg и 2.png:

curl -v -F 'x=@1.jpg' -F 'x=@2.jpg' http://127.0.0.1:12345/predict

Пример predict.py, принимающего запросы и формирующего корректные ответы:

import argparse
import traceback
import sys

import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.log

import numpy as np
import PIL.Image
import io

import keras.models
import keras.backend


def preprocess(image):
    image = image.astype(keras.backend.floatx())
    image /= 255
    image = np.expand_dims(image, axis=-1)
    return image


class StatusHandler(tornado.web.RequestHandler):
    def get(self):
        self.write({"status": "Ok"})  # JSON


class ExitHandler(tornado.web.RequestHandler):
    def get(self):
        self.write({"exit": "Ok"})  # JSON

    def on_finish(self):
        sys.exit(0)


class PredictHandler(tornado.web.RequestHandler):
    def initialize(self, model):
        self.model = model

    def post(self):

        ### process request. Incorrect request produces a response with status 400.
        try:
            # prevent decompression bomb
            PIL.Image.MAX_IMAGE_PIXELS = 256 * 256

            # get images from HTTP-request
            images = []

            # https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPServerRequest.files
            for field_name, files in self.request.files.items():
                #print('Argument:', field_name, len(files))
                if field_name == 'x':
                    for info in files:

                        # https://www.tornadoweb.org/en/stable/httputil.html#tornado.httputil.HTTPFile
                        filename = info["filename"]
                        #content_type = info["content_type"]
                        image = info["body"]

                        #print("Image:", len(image), filename, content_type)

                        # decode image
                        image = PIL.Image.open(io.BytesIO(image))

                        if image.size != (28, 28):
                            raise ValueError("Incorrect image shape:",
                                             filename, image.size)

                        if image.mode != 'L':
                            raise ValueError("Image is not grayscale:",
                                             filename, image.mode)

                        image = np.asarray(image, dtype=np.uint8)

                        images.append(image)
        except:
            e = traceback.format_exc()
            self.set_status(400)
            self.write({"error": e})  # JSON
            return

        ### generate response. Any error produces a response with status 500.
        try:
            # predict coords
            coords = []
            for image in images:
                image = preprocess(image)
                image = np.expand_dims(image, axis=0)  # add batch dimension

                predict = self.model.predict(image)

                x = predict[0,0]
                y = predict[0,1]

                height, width = image.shape[:2]

                x = int(x*width)
                y = int(y*height)

                coord = {'x':x, 'y':y}

                coords.append(coord)

            # send JSON response
            # https://www.tornadoweb.org/en/stable/web.html#tornado.web.RequestHandler.write
            self.write({'y': coords})  # JSON

        except:
            e = traceback.format_exc()
            self.set_status(500)
            self.write({"error": e})  # JSON


def _main(args):
    modelPath = args.model
    host = args.host
    port = args.port

    # based on https://github.com/keras-team/keras/blob/master/examples/mnist_cnn.py
    model = keras.models.load_model(modelPath)

    # for debug only
    tornado.log.enable_pretty_logging()

    app = tornado.web.Application([
        (r"/predict", PredictHandler, dict(model=model)),
        (r"/status", StatusHandler),
        (r"/exit", ExitHandler),
    ])

    server = tornado.httpserver.HTTPServer(
        app,
        decompress_request=True,
        max_body_size=1 * 1024 * 1024 * 1024,  # 1Gb
    )

    server.listen(port=port, address=host)
    tornado.ioloop.IOLoop.current().start()


if __name__ == '__main__':
    parser = argparse.ArgumentParser()
    parser.add_argument(
        '--model',
        help='Path to the trained model. Example: --model 123.h5',
        required=True)
    parser.add_argument(
        '--host',
        help='Host to listen. Example: --host 127.0.0.1',
        required=True)
    parser.add_argument(
        '--port',
        help='Port to listen. Example: --port 12345',
        required=True)

    _main(parser.parse_args())

Полностью рабочий baseline-сабмит можно найти здесь: star-finder-baseline.zip.

Baseline

В качестве baseline-а взята самая простая модель, которая предполагает, что звезда всегда находится в центре изображения.

Functional Check

Все сабмиты перед запуском проходят функциональную проверку. Во время неё сабмит запускается и получает корректные и некорректные запросы. А ответы на них проверяются. Содержимое STDOUT и STDERR доступно Вам для отладки.

Так, например, могут быть отправлены одно или несколько нормальных изображений, некорректный HTTP-запрос, или битая картинка, или изображение некорректных размеров или числа цветовых каналов, или нормальные изображения вперемешку с битыми и т.д.

Limits

Architecture: x86_64.

OS: Linux.

Python version: 3.6 or higher.

Максимальный размер сабмита: 20Mb.

GPU может отсуствовать.

Время запуска (от старта run.sh до начала приёма запросов) не более 60 секунд.

Время каждого ответа на запрос ограничено разумными рамками и может меняться в зависимости от конфигурации сервера, на котором запускается сабмит.

OS memory: 2Gb.

CPU: 1 core of modern CPU.

GPU max memory: 2Gb.

Disk space: 1Gb.

Rules

Один участник соревнования - один аккаунт.

Не более 5 сабмитов за 24 часа. Учитываются только сабмиты, отработавшие до конца без ошибок и получившие score.

При запуске сабмита интернет не должен использоваться.

Не использовать лики, баги, эксплойты. Уведомлять обо всём подобном организаторов соревнования.

Timeline

Соревнование проводится постоянно и не имеет сроков окончания.

Prizes

Вы получите бесценный опыт создания новых архитектур нейросетей и сможете более не полагаться только на готовые решения. Также воочию увидите, где проходит грань применимости навыков человека, а где - нейросетей.

Convolutional neural network on Keras: keras.io/examples/mnist_cnn/;
EfficientNet for Keras and TensorFlow Keras: github.com/qubvel/efficientnet;
Convolutional neural network on Pytorch: github.com/pytorch/examples/blob/master/mnist/main.py;
Fast and easy image augmentation library for Python: albumentations.readthedocs.io.

If you find a useful link, contact us and we will add it.

Discussion

Telegramm: Ryxi Group

ODS каналы #theory_and_practice, #cv, #deep_learning.

Contacts

Skype: monashev
Telegramm: monashev
ODS: monashev

FAQ

По мере возникновения вопросов, тут будут публиковаться ответы самые часто задаваемые из них.