In [2]:
'''This is a definition script, so we do not have to rewrite code'''
import numpy as np
import os
import cv2
import matplotlib.pyplot as mplt
import random
import json
# set matplotlib to print inline (Jupyter)
%matplotlib inline
# path prefix
pth = '../data/'
# files to be used as samples
# list *files* holds the names of the test images
files = sorted(os.listdir(pth))
print files
# Usefull function
def rg(img_path):
return cv2.imread(pth+img_path, cv2.IMREAD_GRAYSCALE)
Write a function that describes each object in a binary image using the Hu statistical moments. The Hu moments are invariant to rotation, scale and translation. These moments can be defined for each region in a binary image. The OpenCV function to compute these moments is cv2.HuMoments. Write down the equations that compute the seven Hu moments for a region.
La siguiente función tiene como objetivo encontrar los Hu moments que describen a cada objeto de la imagen shapes.png. Para esto primero guardamos la matriz de la imagen en una variable. Luego definimos nuestra función que recibe una imagen como parámetro. Aplicamos threshold a la imagen para luego con la resultante hallar los objetos con la función cv2.findContours. Ya que tenemos los valores de los contours, podemos mostrar cuantos de estos encontró con su longitud y confirmamos que sean la misma cantidad de objetos que se encuentran en la imagen. Ahora, para cada uno de estos objetos aplicamos cv2.huMoments y mostramos en pantalla los resultados.
$ I_1 = n_{20} + n_{02} $
$ I_2 = (n_{20} - n_{02})^2 + 4n_{11}^2 $
$ I_3 = (n_{30} - n_{12})^2 + (n_{21} - n_{03})^2 $
$ I_4 = (n_{30} + n_{12})^2 + (n_{21} + n_{03})^2 $
$ I_5 = (n_{30} - 3n_{12})(n_{30} + n_{12})[(n_{30} + n_{12})^2 - 3(n_{21} + n_{03})^2] + (3n_{21} - n_{03})(n_{21} + n_{03})(3(n_{30} + n_{12})^2 - (n_{21} + n_{03})^2) $
$ I_6 = (n_{20} - n_{02})[(n_{30} + n_{12})^2 - (n_{21} + n_{03})^2] + 4n_{11}(n_{30} + n_{12})(n_{21} + n_{03}) $
$ I_7 = (3n_{21} - n_{03})(n_{30} + n_{12})[(n_{30} + n_{12})^2 - 3(n_{21} + n_{03})^2] - (n_{30} - 3n_{12})(n_{21} + n_{03})(3(n_{30} + n_{12})^2 - (n_{21} + n_{03})^2) $
In [15]:
image = rg(files[-11])
def huMoments(image):
ret, threshold = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY+cv2.THRESH_OTSU)
_, contours, _ = cv2.findContours(threshold, cv2.RETR_LIST,cv2.CHAIN_APPROX_NONE)
print "Number of contours detected = " + str(len(contours))
for i in range(len(contours)):
Hu = cv2.HuMoments(cv2.moments(contours[i])).flatten()
print str(i) + ' = ' + str(Hu)
mplt.figure()
mplt.imshow(image, cmap='gray')
mplt.title(files[-11])
huMoments(image)
Write a function that detects corners on an image using the Harris corner detection method. You can use the OpenCV built-in functions. Your function should output the $N$ detected corner locations in a $2 \times N$ matrix. Visualize your results by plotting the corners on top of the input image. Apply your function to the binary image shapes.png and to the grayscale image face.tif.
Primeramente cargamos las dos imágenes de interés. Luego creamos la función cornerDetection que recive como argumento una imagen. Guardamos los corners que hayamos por la función cv2.goodFeaturesToTrack en una variable y luego iteramos sobre estos para hayar las posiciones x,y donde los mostraremos en forma de círculo en la imagen a través de .ravel. Finalmente mostramos la imagen resultante.
In [19]:
img = rg(files[-11])
img_2 = rg(files[18])
def cornerDetection(img):
corners = cv2.goodFeaturesToTrack(img,100,0.01,10)
corners = np.int0(corners)
for k in corners:
x,y = k.ravel()
cv2.circle(img, (x,y), 3, 255, -1)
print x,y
mplt.figure()
mplt.imshow(cv2.cvtColor(img, cv2.COLOR_GRAY2BGR))
cornerDetection(img)
cornerDetection(img_2)
A company that bottles a variety of industrial chemicals has heard of your success solving imaging problems and hires you to design an approach for detecting when bottles are not full. The bottles appear as shown below as they move along a conveyor line past an automatic filling and capping station. A bottle is considered imperfectly filled when the level of the liquid is below the midway point between the bottom of the neck and the shoulder of the bottle.The shoulder is defined as the region of the bottle where the sides and slanted portion of the bottle intersect. The bottles are moving, but the company has an imaging system equipped with an illumination flash front end that effectively stops motion, so you will be given images that look very close to the sample shown below.
Propose a solution for detecting bottles that are not filled properly. State clearly all assumptions that you make and that are likely to impact the solution you propose. Implement your solution and apply it to the images bottles.tif, new_bottles.jpg and three_bottles.jpg. Visualize the results of your algorithm by highlighting with false colors the regions that are detected as correctly filled bottles and the regions that are detected as not properly filled bottles.
La idea es encontrar las botellas que no están llenas apropiadamente. No se requirió de comandos demasiado complejos de OpenCV, de hecho ninguno además de threshold
, filter2D
y erode
. La idea se puede resumir como sigue:
Para empezar se suaviza la imagen con un filtro2D
y se umbraliza la misma para separar las botellas del fondo; este valor se almacena. Luego la idea es encontrar el número de botellas que hay y su ancho; para esto creamos una regla (un vector de ceros) que sumamos con cada fila de la imagen. Lo resultante de la operación es un vector con intervalos de ceros y unos (unos son regiones de las botellas), se miden las regiones de unos y el número de las mismas; la moda del número de regiones es el número de botellas que hay, la media de máximo de cada medición es el ancho de las botellas. Debido a que este ancho hallado es ciertamente menor que el ancho real, sirve como el ancho de la botella en el que ocurre la altura de líquido mínima.
Cabe notar que en la medición de las regiones hecha anteriormente se calcularon los centros de las botellas (centros de cada región) y se estimó la altura mínima del líquido a través del ancho promedio.
Luego de esto se umbralizó la imagen inicial con el fin de obtener las regiones de aire únicamente (fue necesario hacer erosión para remover elementos pequeños). Esta imagen se evaluó en la fila de altura de agua mínima y se extrajeron las regiones de aire a esta altura; el centro de estas regiones se contrasto con el centro de las botellas para saber a qué botella pertenecían y de esta forma identificarlas.
In [121]:
# Images to test
MIN_DIST = 30
LEVEL_OFFSET_INV = 10
LEVEL_OFFSET = 90
imgs = [files[i] for i in [5, -3, -20]]
# Bad bottle detector
def badBottleDetector(img):
h, w = img.shape
# Smooth
kernel = np.ones((3,3),np.float32)/9
simg = cv2.filter2D(img, -1, kernel)
simg_c = simg.copy()
_, simg_ct = cv2.threshold(simg_c, np.mean(simg_c)+LEVEL_OFFSET, 1, cv2.THRESH_BINARY)
_, simg_cn = cv2.threshold(simg_c, LEVEL_OFFSET_INV, 1, cv2.THRESH_BINARY)
kernel = np.ones((5,5), np.uint8)
simg_ct = cv2.erode(simg_ct, kernel, iterations=1)
bottles = list()
sizes = list()
indices = list()
centers = list()
for i in range(h):
ruler = np.zeros([1, w]) + simg_cn[i: i+1]
ruler = ruler[0]
seed = 0
break_points = list()
for j in range(len(ruler)):
if (ruler[j] != seed) or (j == len(ruler)-1 and ruler[j] == 1):
break_points.append(j)
seed = int(not seed)
dist = list()
center = list()
for j in range(0, len(break_points)-1, 2):
if break_points[j+1] - break_points[j] > MIN_DIST:
dist.append(break_points[j+1] - break_points[j])
center.append(int((break_points[j+1] + break_points[j])/2))
if dist:
bottles.append(len(dist))
sizes.append(np.max(dist))
indices.append(i)
centers.append(center)
counts = np.bincount(bottles)
num_bottles = np.argmax(counts)
base_size = int(np.mean(sizes))
centers = [c for c in centers if len(c) == num_bottles]
center = list()
for i in range(num_bottles):
bt = [c[i] for c in centers]
center.append(int(np.mean(bt)))
index = 0
for i in range(len(sizes)):
if sizes[i] > base_size:
index = i
break
liquid_min_limit = indices[index]
# Check which bottle has air at index liquid_min_limit
ruler = np.zeros([1, w]) + simg_ct[liquid_min_limit: liquid_min_limit+1]
ruler = ruler[0]
seed = 0
break_points = list()
for j in range(len(ruler)):
if (ruler[j] != seed) or (j == len(ruler)-1 and ruler[j] == 1):
break_points.append(j)
seed = int(not seed)
dist = list()
centers = list()
for j in range(0, len(break_points)-1, 2):
if break_points[j+1] - break_points[j] > MIN_DIST:
dist.append(break_points[j+1] - break_points[j])
centers.append(int((break_points[j+1] + break_points[j])/2))
final = [0]*len(center)
for c in centers:
ct = [np.abs(cc - c) for cc in center]
final[np.argmin(ct)] = 1
print('Final decision. Bottles not correct are marked with 1s: ')
print(final)
printer([img, simg_cn, simg_ct], ['Original image', 'inv', 'liquid'])
def printer(iss, des):
# Printing
f, ax = mplt.subplots(1, len(iss), figsize=(10,10))
for i in range(len(iss)):
ax[i].imshow(iss[i], cmap='gray')
ax[i].set_title(des[i])
for i in imgs:
badBottleDetector(rg(i))
Suppose that you are observing objects in the night sky. Suppose that only ‘big’ objects are important to your observation. In this scenario, ‘small’ objects are considered noise. Write a python function that processes the image as follows:
Use a 15x15 averaging filter to blur the image.
Apply a threshold of 0.25 to binarize the resulting blurred image.
Use the binary image to ‘mask’ the noise of the original image: simply perform an element-wise multiplication of the binary image and the original image.
Use connected component analysis on the binary image to count the number of ‘big’ objects found.
The function should take three inputs: an image matrix, the size of the averaging filter and threshold value. Make sure your function displays the intermediary results of each step outlined above.
Apply your function to the input image ‘hubble-original.tif’. Try different values of smoothing kernel size and threshold value. Analyze the relationship between number of objects found and smoothing kernel size and threshold value. In particular, you might want to observe the result when using an averaging filter of size n=1 (i.e. no smoothing).
La idea del programa siguiente es encontrar cuerpos grandes en las imágenes; para ello se realizan los procedimientos anteriormente pedidos, esto es el filtro 15x15, la aplicación de la umbralización para crear la máscara, la eliminación de ruido de la imagen original y el análisis de cuerpo conexo. Estos pasos son sencillos de realizar y ya se han implementado anteriormente, a excepción del análisis de cuerpo conexo. Para comprobar conexión en un cuerpo, se elige un punto de semilla (aquel punto con intensidad de 1), a partir de este punto se analizan los vecinos de forma recursiva, esto es, se vuelve a llamar la función sobre los vecinos si estos tienen intensidad de 1. Esto arroja la cantidad de cuerpos conexos en la imagen, sin embargo, no hay garantía que estos cuerpos sean grandes. Para asegurar lo anterior se cuentan también el número de miembros de cada cuerpo, si este exede cierto umbral establecido, se cuenta como cuerpo grande.
In [174]:
img_origin = rg('hubble-original.tif')
BIG_OBJECT_COUNT = 80
def printer(iss, des):
# Printing
f, ax = mplt.subplots(1, len(iss), figsize=(15,15))
for i in range(len(iss)):
ax[i].imshow(iss[i], cmap='gray')
ax[i].set_title(des[i])
def connectedLabeling(mat, i, j, h, w):
mat[i][j] = 0
if i+1 < h and mat[i+1][j] == 1:
return 1 + connectedLabeling(mat, i+1, j, h, w)
elif i-1 > -1 and mat[i-1][j] == 1:
return 1 + connectedLabeling(mat, i-1, j, h, w)
elif j+1 < w and mat[i][j+1] == 1:
return 1 + connectedLabeling(mat, i, j+1, h, w)
elif j-1 > -1 and mat[i][j-1] == 1:
return 1 + connectedLabeling(mat, i, j-1, h, w)
elif i-1 > -1 and j-1 > -1 and mat[i-1][j-1] == 1:
return 1 + connectedLabeling(mat, i-1, j-1, h, w)
elif i-1 > -1 and j+1 < w and mat[i-1][j+1] == 1:
return 1 + connectedLabeling(mat, i-1, j+1, h, w)
elif i+1 < h and j-1 > -1 and mat[i+1][j-1] == 1:
return 1 + connectedLabeling(mat, i+1, j-1, h, w)
elif i+1 < h and j+1 < w and mat[i+1][j+1] == 1:
return 1 + connectedLabeling(mat, i+1, j+1, h, w)
else:
return 1
def countObjects(img, avg_size, th):
kernel = np.ones((avg_size,avg_size),np.float32)/(avg_size*avg_size)
fimg = cv2.filter2D(img, -1, kernel)
_, timg = cv2.threshold(fimg, 255*th, 1, cv2.THRESH_BINARY)
masked = img * timg
# Count the objects in the masked image
timgc = timg.copy()
h, w = maskc.shape
for i in range(h):
for j in range(w):
if timgc[i][j] == 1:
neighbors = connectedLabeling(timgc, i, j, h, w)
if neighbors > BIG_OBJECT_COUNT:
# print(neighbors)
timgc[i][j] = 1
count = np.sum(timgc)
print('Objetos grandes encontrados: ' + str(count))
print('Se definió objeto grande aquel cuyo conjunto conexo tiene más de ' + str(BIG_OBJECT_COUNT) + ' miembros')
print('Este parámetro se puede variar')
printer([img, timg, timgc, maskc], ['Original', 'Threshold', 'Counting', 'Masked'])
sizes = [1, 5, 15, 25]
ths = [0.5, 0.5, 0.25, 0.3]
for i in range(len(sizes)):
countObjects(img_origin, sizes[i], ths[i])
Write a function that extracts local interest points and computes their descriptors using the SIFT transform. You can find implementations of the SIFT transform in OpenCV.
Your function should return two matrices: A first matrix of size $3 \times N$, where $N$ is the number of detected points in the image, and the 3 elements correspond to the $x$, $y$ locations and $s$ size of the detected points. A second matrix of size $128 \times N$ that contains the SIFT descriptor of each interest point.
Apply your function to all car images image_00XX.jpg. Store the results of each image in a separate data file.
La Siguiente función hace uso de la implementación de la transformada de SIFT que tiene OpenCV. La función utilizada fue detectAndCompute. La función que se creó, recibe la array de una imagen en escala de grises y regresa los puntos (x,y) y la escala de la imagen en la que fueron encontrados como una matriz de numpy 3xN y también los respectivos descriptores de SIFT para cada punto en otra matriz. También se puede pasar como segundo parametro a la función un booleano que determina si se imprime o no la imagen con los puntos sobre ella, en el caso por defecto, que es False, no imprime nada. Los datos se guardan en archivos de texto en formato json.
In [2]:
def getPointsAndDescriptors(img,show_img = False):
# Getting Keypoint structure object and Descriptor Array
sift = cv2.SIFT()
kp, D = sift.detectAndCompute(img,None)
# Getting the array of points (x,y,s)
points = np.zeros((3,len(kp)))
for i in range(len(kp)):
points[0][i] = kp[i].pt[0]
points[1][i] = kp[i].pt[1]
points[2][i] = kp[i].size
if show_img == True:
img_s = cv2.drawKeypoints(img, kp, flags=cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)
mplt.imshow(img_s), mplt.xticks([]), mplt.yticks([]), mplt.figure()
return points, D
# 25 -45 ind of cars images
for i in range(25,45):
img_name = files[i]
img = rg(img_name)
points, D = getPointsAndDescriptors(img)
f = open("data_image_"+str(i-24)+".json","w")
data = {"points":points.tolist(),"Descriptors": D.tolist()}
json.dump(data,f, sort_keys=True, indent=4)
f.close()
print "Points and SIFT descriptors for image "+str(i-24)+" extracted"
print "Done!"
In [ ]: