Обнаружение круговых форм на двоичных изображениях с большим количеством шума - PullRequest
0 голосов
/ 24 января 2019

Я пытаюсь обнаружить черно-белые футбольные мячи почти чисто, используя методы предварительной обработки изображений с OpenCV (в Python). Моя идея заключается в следующем;

  1. Обработка изображения (например, для размытой двоичной фотографии)
  2. Найти несколько «кандидатов» на футбольный мяч (например, по определению контура)
  3. Измените размер этих кандидатов (например, до 48x48 пикселей) и введите соответствующие ему булевы значения (0 = черный пиксель, 1 = белый пиксель) в очень простой нейронной сети, которая затем выдает достоверное значение для каждого кандидата
  4. Определите, присутствует ли футбольный мяч на фотографии и наиболее вероятное местоположение мяча

Я застрял в поиске подходящих кандидатов. В настоящее время это мой подход;

Шаг 1: Исходное изображение

Шаг 2: размытое изображение (medianblur, ядро ​​7)

Шаг 3: Сгенерированное двоичное изображение A Сгенерированное двоичное изображение B

Затем я использую findContours, чтобы найти контуры на двоичных изображениях. Если на двоичном изображении B не найдено ни одного кандидата (с использованием минимального и максимального порогового значения граничного блока), findContours будет работать на двоичном изображении A (и кандидаты будут возвращены). Если один или несколько кандидатов найдены в двоичном изображении B, то исходное изображение будет размыто (с ядром 15), а двоичное изображение C будет использовано для нахождения контуров и возврата кандидатов. См .: Сгенерированное двоичное изображение C

Это код для генерации этих двоичных изображений:

def generateMask(imgOriginal, rgb, margin):
  lowerLimit = np.asarray(rgb)
  upperLimit = lowerLimit+margin

  # switch limits if margin is negative
  if(margin < 0):
    lowerLimit, upperLimit = upperLimit, lowerLimit

  mask = cv.inRange(imgOriginal, lowerLimit, upperLimit)

  return mask

# generates a set of six images with (combinations of) mask(s) applied
def applyMasks(imgOriginal, mask1, mask2):
  # applying both masks to original image
  singleAppliedMask1 = cv.bitwise_and(imgOriginal, imgOriginal, mask = mask1) #res3
  singleAppliedMask2 = cv.bitwise_and(imgOriginal, imgOriginal, mask = mask2) #res1

  # applying masks to overlap areas in single masked and original image
  doubleAppliedMaskOv1 = cv.bitwise_and(
    imgOriginal,
    singleAppliedMask1,
    mask = mask2
  ) #res4
  doubleAppliedMaskOv2 = cv.bitwise_and(
    imgOriginal,
    singleAppliedMask2,
    mask = mask1
  ) #res2

  # applying masks to joint areas in single masked and original image
  doubleAppliedMaskJoin1 = cv.bitwise_or(
    imgOriginal, 
    singleAppliedMask1, 
    mask = mask2
  ) #res7
  doubleAppliedMaskJoin2 = cv.bitwise_or(
    imgOriginal,
    singleAppliedMask2,
    mask = mask1
  ) #res6

  return (
    singleAppliedMask1, singleAppliedMask2,
    doubleAppliedMaskOv1, doubleAppliedMaskOv2,
    doubleAppliedMaskJoin1, doubleAppliedMaskJoin2
  )

def generateBinaries(appliedMasks):
  # variable names correspond to output variables in applyMasks()
  (sam1, sam2, damov1, damov2, damjo1, damjo2) = appliedMasks

  # generate thresholded images
  (_, sam1t) = cv.threshold(sam1, 0, 255, cv.THRESH_BINARY_INV)
  (_, sam1ti) = cv.threshold(sam1, 0, 255, cv.THRESH_BINARY_INV)
  (_, sam2t) = cv.threshold(sam2, 0, 255, cv.THRESH_BINARY)
  (_, sam2ti) = cv.threshold(sam2, 0, 255, cv.THRESH_BINARY_INV)

  (_, damov1t) = cv.threshold(damov1, 0, 255, cv.THRESH_BINARY)
  (_, damov2t) = cv.threshold(damov2, 0, 255, cv.THRESH_BINARY_INV)

  (_, damjo1t) = cv.threshold(damjo1, 0, 255, cv.THRESH_BINARY_INV)
  (_, damjo2t) = cv.threshold(damjo2, 0, 255, cv.THRESH_BINARY)

  # return differences in binary images
  return ((damov2t-sam2t), (sam1t-damov1t), (sam2ti-damjo2t))

Результат в этом примере изображения хорош и очень полезен, хотя выглядит довольно неправильно: см. Результат .

Очень легко получить результат этого примера изображения намного лучше (например, возвращая только одного или двух кандидатов, включая идеальную ограничивающую рамку для футбольного мяча), однако, после обширной настройки параметров I использованный в этом примере, кажется, дает лучший общий отзыв.

Однако я очень застрял на некоторых фотографиях, на которых я покажу исходные изображения, двоичные изображения A и B (сгенерированные на основе медианы исходного изображения, размытого с помощью ядра 7) и двоичное изображение C (ядро 15). , В настоящее время мой подход возвращает в среднем около 15 кандидатов на фотографию, из которых для 25% фотографий включена, по крайней мере, идеальная ограничивающая рамка мяча, а для примерно 75% фотографий , по крайней мере включена ограничивающая рамка, которая является частично правильной (например, наличие части шарика в ограничительной рамке или просто часть самого шарика).

Исходные изображения + двоичные изображения A

Двоичные изображения B + двоичные изображения C

(я мог опубликовать только до 8 ссылок)

Я надеюсь, что вы, ребята, могли бы дать мне несколько советов о том, как действовать.

Ответы [ 2 ]

0 голосов
/ 24 января 2019

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

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

Затем вы повторяете изображение и проверяете, соответствует ли шаблон. Конечно, это не будет работать на изображениях с окклюзией, но это может помочь получить некоторых кандидатов.

Более подробно об упомянутом процессе в статье здесь (https://ieeexplore.ieee.org/document/5375779) или слайды здесь (http://www.cse.psu.edu/~rtc12/CSE486/lecture07.pdf).

)

Я написал небольшой фрагмент кода, чтобы показать вам идею. Просто обрезал мяч с картинки (так что я изменял, но это просто для того, чтобы показать идею). Он также использует только разницу между шаром и изображением, в то время как более сложная мера (например, NCC) была бы лучше, но, как сказано, является примером.

The ball cropped <- шар обрезан </p>

import matplotlib.pyplot as plt
import numpy as np
import pdb
import cv2

def rgb2gray(rgb):

    r, g, b = rgb[:,:,0], rgb[:,:,1], rgb[:,:,2]
    gray = 0.2989 * r + 0.5870 * g + 0.1140 * b

    return gray

if __name__ == "__main__":

    ball = plt.imread('ball.jpg');
    ball = rgb2gray(ball);
    findtheballcol = plt.imread('findtheball.jpg');
    findtheball = rgb2gray(findtheballcol)
    matching_img = np.zeros((findtheball.shape[0], findtheball.shape[1]));

    #METHOD 1
    width = ball.shape[1]
    height = ball.shape[0]
    for i in range(ball.shape[0], findtheball.shape[0]-ball.shape[0]):
        for j in range(ball.shape[1], findtheball.shape[1]-ball.shape[1]):


            # here use NCC or something better
            matching_score = np.abs(ball - findtheball[i:i+ball.shape[0], j:j+ball.shape[1]]);
            # inverting so that max is what we are looking for
            matching_img[i,j] = 1 / np.sum(matching_score);


    plt.subplot(221);
    plt.imshow(findtheball); 
    plt.title('Image')
    plt.subplot(222);
    plt.imshow(matching_img, cmap='jet');
    plt.title('Matching Score')
    plt.subplot(223);
    #pick a threshold
    threshold_val = np.mean(matching_img) * 2; #np.max(matching_img - (np.mean(matching_img)))
    found_at = np.where(matching_img > threshold_val)
    show_match = np.zeros_like(findtheball)
    for l in range(len(found_at[0])):
        yb = round(found_at[0][l]-height/2).astype(int)
        yt = round(found_at[0][l]+height/2).astype(int)
        xl = round(found_at[1][l]-width/2).astype(int)
        xr = round(found_at[1][l]+width/2).astype(int)
        show_match[yb: yt, xl: xr] = 1;
    plt.imshow(show_match)
    plt.title('Candidates')
    plt.subplot(224)
    # higher threshold
    threshold_val = np.mean(matching_img) * 3; #np.max(matching_img - (np.mean(matching_img)))
    found_at = np.where(matching_img > threshold_val)
    show_match = np.zeros_like(findtheball)
    for l in range(len(found_at[0])):
        yb = round(found_at[0][l]-height/2).astype(int)
        yt = round(found_at[0][l]+height/2).astype(int)
        xl = round(found_at[1][l]-width/2).astype(int)
        xr = round(found_at[1][l]+width/2).astype(int)
        show_match[yb: yt, xl: xr] = 1;
    plt.imshow(show_match)
    plt.title('Best Candidate')
    plt.show()

Result on your image

Веселитесь!

0 голосов
/ 24 января 2019

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

...