2016-04-08 4 views
3

Я новичок в обработке изображений и встал на проблему. У меня есть этот образ:Определение цвета внутри контура OpenCV

enter image description here

Моя цель состоит, чтобы превратить его в матрицу с 1, если есть красный внутри клетки и 0, если нет.

Так это будет

10000000000 
10001000000 
10001000000 
10001000000 
11111111111 
10000000101 
10111111101 
etc... 

Так у меня есть код, где он может извлечь контуры и использует approxPolyDP для определения (х, у) координаты из 4 угловых краев.

Contours shown in white

Теперь нужно выяснить, как определить, является ли определенный цвет (красный) находится в пределах каждого контура.

Вот некоторые из моего кода: надеюсь, кто-то может помочь!

def extract_cells(grid): 
    #convert to gray 
    image_gray = cv2.cvtColor(grid, cv2.COLOR_BGR2GRAY) 
    #creates a binary image from the gray scale image to use as input for findContours() 
    #thresh = cv2.adaptiveThreshold(image_gray,255,cv2.ADAPTIVE_THRESH_MEAN_C,cv2.THRESH_BINARY_INV,11,15) 

    #Find countors 
    tempimg, contours, hierarchy = cv2.findContours(image_gray, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) 

    #draw all countours 
    count = 0 
    max_size = 0 
    matrix = [] 
    new_contours = [] 
    grid_contour = 0 
    grid_contour_row = None 
    grid_contour_column = None 
    for each in enumerate(contours): 

     #used to find the midpoint of each cell 
     M = cv2.moments(contours[count]) 
     row = int(M['m10']/M['m00']) 
     column = int(M['m01']/M['m00']) 

     #find biggest box (this is the grid itself, so needs to be removed since it is not a cell) 
     size = cv2.contourArea(contours[count]) 
     if (size > max_size): 
      new_contours.append(contours[grid_contour]) 
      #put a marker in each cell for testing 
      #if (grid_contour_row != None and grid_contour_column != None): 
       #cv2.putText(grid, "0", (grid_contour_row, grid_contour_column), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255)) 
      grid_contour = count 
      grid_contour_row = row 
      grid_contour_column = column 
     else: 
      new_contours.append(contours[count]) 
      #put a marker in each cell for testing 
      #cv2.putText(grid, "0", (row, column), cv2.FONT_HERSHEY_SIMPLEX, 1, (255,255,255)) 

     #matrix = create_matrix(matrix,count) 
     count += 1 

    #draw white lines showing contours 
    cv2.drawContours(grid, new_contours, -1, (255,255,255)) 

    #approx contains x,y coordinates for the 4 corners of the cell 
    approx = cv2.approxPolyDP(contours[0],0.01*cv2.arcLength(contours[0],True),True) 

    cv2.imshow("test", grid) 
    cv2.waitKey(0) 
    return new_contours, approx 


def identify_colors(image, *colors): 
    colorlist = [] 
    #Add RGB values for each color specified when the function was called 
    #to the list colorlist 

    if "blue" in colors: 
     colorlist.append(([115,0,0], [255,100,100])) 
    if "white" in colors: 
     colorlist.append(([215, 215, 215], [255, 255, 255])) 
    if "red" in colors: 
     colorlist.append(([0,0,100], [100,100,255])) 
    if "green" in colors: 
     colorlist.append(([0,115,0], [100,255,100])) 

    #loop over the colorlist 
    for (lower, upper) in colorlist: 
     # create NumPy arrays from the colorlist 
     lower = np.array(lower, dtype = "uint8") 
     upper = np.array(upper, dtype = "uint8") 

     #econverts image to b/w with white being anything in the BGR value range 
     mask = cv2.inRange(image, lower, upper) 
     #converts that specified range back to its orginal color 
     output = cv2.bitwise_and(image, image, mask = mask) 

     #show the photos side by side 
     #cv2.imshow("images", np.hstack([image, output])) 
     #cv2.waitKey(0) 

    return output 
+0

Итак, в моем понимании, у вас есть каждый "белый ящик" край координат, верно? Вы можете зацикливаться на каждом белом поле, которое вы нашли, и для каждого окна вы просматриваете исходное изображение (используя координаты окна) для красного пикселя. Если вы его найдете, отметьте его как 1. Вы даже можете использовать координаты белых квадратов для обрезки (копирования) исходного изображения, создав новый Mat - тогда вы можете просто взглянуть на обрезанный Mat для красного пикселя ... –

+0

Это то, о чем я тоже думал. Однако я не уверен, как прокрутить изображение, используя координаты, которые у меня есть. Как вы думаете, вы могли бы привести пример? Извините, я новичок в этом :) – Ashley

+0

Используя краевые координаты небольшого окна, вы должны извлечь субимость/область interes/ROI, просто указав это в матрице изображения. В этом субимаге вы можете искать цвет (используя cv2.inrange). Взгляните на учебное пособие по адресу http://docs.opencv.org/3.1.0/d6/d00/tutorial_py_root.html#gsc.tab=0 – tfv

ответ

3

Это гораздо проще, если вы используете scipy.ndimage.label():

from scipy import ndimage 
import cv2 
import numpy as np 
import pandas as pd 

img = cv2.imread("image.png") 

blue = np.array([200, 70, 60]) 
red = np.array([30, 20, 220]) 

isblue = cv2.inRange(img, blue, blue+20) 
isred = cv2.inRange(img, red, red+20) > 0 

labels, count = ndimage.label(~isblue) 

loc = np.where(labels >= 2) #label 1 is the border 

# to get the location, we need to sort the block along yaxis and xaxis 
df = pd.DataFrame({"y":loc[0], "x":loc[1], "label":labels[loc], "isred":isred[loc]}) 

grid = df.groupby("label").mean().sort_values("y") 

def f(df): 
    return df.sort_values("x").reset_index(drop=True) 
res = grid.groupby((grid.y.diff().fillna(0) > 10).cumsum()).apply(f) 

print((res.isred.unstack(1) > 0).astype(np.uint8)) 

выход:

0 1 2 3 4 5 6 7 8 9 10 
y            
0 1 0 0 0 0 0 0 0 0 0 0 
1 1 0 0 0 1 0 0 0 0 0 0 
2 1 0 0 0 1 0 0 0 0 0 0 
3 1 0 0 0 1 0 0 0 0 0 0 
4 1 1 1 1 1 1 1 1 1 1 1 
5 1 0 0 0 0 0 0 1 1 1 1 
6 1 0 1 1 1 1 1 1 1 0 1 
7 0 0 0 0 0 0 0 0 0 0 1 
8 0 0 0 0 0 0 0 0 0 0 1 
9 0 0 0 0 0 0 0 0 0 0 1 
10 0 0 0 0 0 0 0 0 0 0 1 
+1

Мне нравится этот подход, спасибо, чему-то чему-то научился! – tfv

0

Вы можете выполнить следующие действия:

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

Результат выглядит так:

enter image description here

Код с комментариями прилагается.

Я только что видел, что вам нужно сгенерировать матрицу из списка, который печатается, для каждого квадрата список содержит 0 или 1 и координаты x/y центроида этого квадрата. К сожалению, контуры не сортируются в «нормальном» порядке из-за небольших нерегулярностей формы, поэтому вам все равно придется выполнять некоторую сортировку координат самостоятельно, извините за это.

Если вы хотите этого избежать, вы также можете сгенерировать mask2, выполнив заливку заливки семенем, которое генерируется из некоторой регулярной сетки и прокручивается через эту сетку.

import cv2 
import numpy as np 
img = cv2.imread('image.png') 


# Define range of blue color 
lower_limit = np.array([204,72,63]) 
upper_limit = np.array([204,72,63]) 

# Generate mask for the blue pixels 
blue_colour_mask = cv2.inRange(img, lower_limit, upper_limit) 

# Define range of red color 
lower_limit = np.array([36,28,237]) 
upper_limit = np.array([36,28,237]) 

# Generate mask for the red pixels 
red_colour_mask = cv2.inRange(img, lower_limit, upper_limit) 


# Remove outer black area 
flooded = img.copy() 
x = 5 
y = 5 
flooded = blue_colour_mask.copy() 
h, w = blue_colour_mask.shape[:2] 
mask = np.zeros((h+2, w+2), np.uint8) 
mask[:] = 0 
cv2.floodFill(flooded,mask,(x,y),(255,)*3, (40,)*3, (40,)*3, 4) 


# Loop through each single small rectange (contour # from 1 to 121, 0 ist image border) 
_, contours, hierarchy = cv2.findContours(flooded.copy(), cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE) 
h, w = img.shape[:2] 

result= np.zeros((h, w, 3), np.uint8) 
result[:] = 0 

mask2 = np.zeros((h, w), np.uint8) 
list =[] 

for i in range(1,122): 
    mask2[:] = 0 
    cv2.drawContours(mask2, contours, i, (255,255,255), cv2.FILLED) 
    mask3= cv2.bitwise_and(mask2, red_colour_mask) 
    pixnumber= cv2.countNonZero(mask3) 

    if pixnumber == 0: 
     cv2.drawContours(result, contours, i, (255,255,255), cv2.FILLED) 
     moments = cv2.moments(contours[i]) 
     cx = int(moments['m10']/moments['m00']) 
     cy = int(moments['m01']/moments['m00']) 
     print 0, i, cx, cy 
     list.append([0,cx, cy]) 
    else: 
     cv2.drawContours(result, contours, i, (0,0,255), cv2.FILLED) 
     moments = cv2.moments(contours[i]) 
     cx = int(moments['m10']/moments['m00']) 
     cy = int(moments['m01']/moments['m00'])  
     print 1, i, cx, cy 
     list.append([0,cx, cy]) 

cv2.imshow('Result',result) 

cv2.waitKey(0) 

print list 


cv2.imshow('image',img) 
cv2.imshow('Blue pixel mask',blue_colour_mask) 
cv2.imshow('Red pixel mask',red_colour_mask) 
cv2.imshow('Result',result) 

cv2.waitKey(0) 
0
import cv2 
import numpy as np 


def centroid(contour): 
    x,y,w,h = cv2.boundingRect(contour) 
    return (y+h/2.0, x+w/2.0) 

def contains_red(red_mask, tile): 
    tile_area = np.zeros_like(red_mask) 
    cv2.drawContours(tile_area, [tile[1]], 0, 255, -1) 
    red_tile_area = cv2.bitwise_and(tile_area, red_mask) 
    return (cv2.countNonZero(red_tile_area) > 0) 

def get_transform(grid_size, grid_contour): 
    x,y,w,h = cv2.boundingRect(grid_contour) 
    tile_w = float(w)/(grid_size[0]) 
    tile_h = float(h)/ (grid_size[1]) 
    return ((-y - tile_h/2, -x - tile_w/2), (1/tile_h, 1/tile_w)) 


img = cv2.imread("input.png") 

hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 
h, s, v = cv2.split(hsv) 

cv2.imwrite("out_1.png", np.hstack([h, s, v])) 

# Saturation mask to get rid of black 
s_mask = cv2.threshold(s, 10, 255, cv2.THRESH_BINARY)[1] 

# Pick out blue area 
blue_range = [110, 130] 
blue_mask = cv2.inRange(h, blue_range[0], blue_range[1]) 
blue_mask = cv2.bitwise_and(blue_mask, s_mask) 


# Pick out blue area 
red_range = [[170, 180], [0,10]] 
red_mask = cv2.bitwise_or(
    cv2.inRange(h, red_range[0][0], red_range[0][1]) 
    , cv2.inRange(h, red_range[1][0], red_range[1][1])) 
red_mask = cv2.bitwise_and(red_mask, s_mask) 

cv2.imwrite("out_2.png", np.hstack([s_mask, blue_mask, red_mask])) 


kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) 
# Remove noise 
blue_mask = cv2.morphologyEx(blue_mask, cv2.MORPH_OPEN, kernel) 
# Fill any small holes 
blue_mask = cv2.morphologyEx(blue_mask, cv2.MORPH_CLOSE, kernel) 


# Find outer contour, and fill area outside 
cnt_grid = cv2.findContours(blue_mask.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0] 
assert(len(cnt_grid) == 1) 
grid_area = np.zeros_like(blue_mask) 
cv2.drawContours(grid_area, cnt_grid, 0, 255, -1) 
grid_tiles = cv2.bitwise_and(cv2.bitwise_not(blue_mask), grid_area) 

cv2.imwrite("out_3.png", np.hstack([blue_mask, grid_area, grid_tiles])) 

# Find contours of our tiles 
cnt_tiles = cv2.findContours(grid_tiles.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0] 

# Find scaling parameters 
offset, scale = get_transform((11, 11), cnt_grid[0]) 

tiles = [[centroid(contour), contour, False] for contour in cnt_tiles] 
for tile in tiles: 
    # Rescale centroid 
    tile[0] = (
     int(round((tile[0][0] + offset[0]) * scale[0])) 
     , int(round((tile[0][1] + offset[1]) * scale[1])) 
    ) 
    tile[2] = contains_red(red_mask, tile) 

# Sort the tiles 
tiles = sorted(tiles, key=lambda x: x[0], reverse=False) 

# Extract the results 
result = np.array([int(t[2]) for t in tiles]) 

print result.reshape(11,11) 

Мы первый разбить изображение на HSV цветовое пространство.

оттенок, насыщенность, Value каналы:

enter image description here

Так как черный может в конечном итоге любой оттенок, мы должны игнорировать ненасыщенные пиксели.

Затем мы можем извлекать синие и красные маски, используя в радиусе действия, и побитовые и с маской насыщения.

Насыщение маска, синяя и красная маска:

enter image description here

Мы применяем морфологические открытия и закрытие, чтобы избавиться от шума.

Мы определяем область самой сетки, получая внешний контур.

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

Синяя маска, Сетка область маски, маски плитки:

enter image description here

Далее мы вычислим центроиды нашей плитки.

Мы предполагаем, что размер сетки сетки постоянный или введен пользователем. Основываясь на координатах прямоугольной рамки сетки, мы можем определить масштабирование и смещение, чтобы пересчитать наши центроиды до диапазона 0,10.

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

Результат:

[[1 0 0 0 0 0 0 0 0 0 0] 
[1 0 0 0 1 0 0 0 0 0 0] 
[1 0 0 0 1 0 0 0 0 0 0] 
[1 0 0 0 1 0 0 0 0 0 0] 
[1 1 1 1 1 1 1 1 1 1 1] 
[1 0 0 0 0 0 0 0 1 1 1] 
[1 0 1 1 1 1 1 1 1 0 1] 
[0 0 0 0 0 0 0 0 0 0 1] 
[0 0 0 0 0 0 0 0 0 0 1] 
[0 0 0 0 0 0 0 0 0 0 1] 
[0 0 0 0 0 0 0 0 0 0 1]]