Zernike moment as Shape descriptor doesn't work

387 Views Asked by At

I m working to create a CBIR, I want to use Zernike moment to get shape feature of a query image.

I try to implement it by following this tutoriel: https://pyimagesearch.com/2014/05/19/building-pokedex-python-comparing-shape-descriptors-opencv/

But actually it doesn't work.

After some hours a investigating, I found that outline image doesn't get (or barely) get shape; instead, I get all of the image as a the shape. So all of the image gives me a big square as the shape.

Screenshots here :

Gray Query Image

Query Thresh

Outline

Here is my code:

from statistics import mode
from turtle import width
from scipy.spatial import distance, distance_matrix #pip install scipy
import matplotlib.pyplot as plt

from skimage.feature import hog #pip3 install scikit-image matplotlib
from skimage import data, exposure 
from cv2 import waitKey

import mahotas #pip install mahotas
import cv2
import matplotlib.pyplot as plt
import numpy as np
import imutils  #pip install imutils.#

#Search Shape
def SearchShape(raduis, image):
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # pad the image with extra white pixels to ensure the
    # edges of the earplane are not up against the borders
    # of the image
    image = cv2.copyMakeBorder(image, 15, 15, 15, 15, cv2.BORDER_CONSTANT, value = 255)
    # invert the image and threshold it
    thresh = cv2.bitwise_not(image)
    thresh[thresh > 0] = 255

    # initialize the outline image, find the outermost
    # contours (the outline) of the earplane, then draw
    outline = np.zeros(image.shape, dtype = "uint8")
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    cnts = imutils.grab_contours(cnts)
    cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[0]

    cv2.drawContours(outline, [cnts], -1, 255, -1) 
    # compute Zernike moments to characterize the shape
    # of earplane outline, then update the index
    
    momentsArray = mahotas.features.zernike_moments(thresh, raduis) #outline, raduis)
    
    #Convert : array([....]) to [...]
    moments = []
    for m in momentsArray:
        moments.append(m)

    cv2.imshow("", image)
    cv2.waitKey(0)
    cv2.imshow("", thresh)
    cv2.waitKey(0)
    cv2.imshow("", outline)
    cv2.waitKey(0)

    #print("MOMENT Liste IS: ", moments)
    return moments


#Shape descriptor
#tres proche#
queryFeatures = SearchShape(21, cv2.imread("./JPEGImages/2007_000033.jpg"))
features = SearchShape(21, cv2.imread("./JPEGImages/2007_000738.jpg"))
d = distance.euclidean(queryFeatures, features)
print("distance is: ", d)

Edited: After more test, i found that some value given to cnts (which i suppose should help to draw shape) are pretty close to image shape (here a value in cnts). May be there is some probleme with cv2.findContours or cv2.bitwise_not, or imutils.grab_contours or cv2.drawContours

but thresh look good because if i change the value thresh[thresh > 0] = 255 to thresh[thresh > 100] = 255 ; cv.imshow show some shape in thresh

1

There are 1 best solutions below

0
On

I finally, i get the answer. i just changed the thresh[thresh > 0] = 255 to thresh[thresh > 15] = 255 and reverse the color just after. the solved code is here:

from statistics import mode
from turtle import width
from scipy.spatial import distance, distance_matrix #pip install scipy
import matplotlib.pyplot as plt

from skimage.feature import hog #pip3 install scikit-image matplotlib
from skimage import data, exposure 
from cv2 import waitKey

import mahotas #pip install mahotas
import cv2
import matplotlib.pyplot as plt
import numpy as np
import imutils  #pip install imutils.#

#Search Shape
def SearchShape(raduis, image):
    image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    # pad the image with extra white pixels to ensure the
    # edges of the pokemon are not up against the borders
    # of the image
    #image = cv2.copyMakeBorder(image, 15, 15, 15, 15, cv2.BORDER_CONSTANT, value = 255)
    # invert the image and threshold it
    thresh = cv2.bitwise_not(image)

    thresh[thresh > 15] = 255 #> 15] = 255
    thresh = cv2.bitwise_not(thresh)
    
    # initialize the outline image, find the outermost
    # contours (the outline) of the pokemone, then draw
    outline = np.zeros(image.shape, dtype = "uint8")
    cnts = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
#    print("cnts 1: ", cnts)
    cnts = imutils.grab_contours(cnts)
#    print("cnts 2: ", cnts)
    momentsArray = []
    try:
        cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[0]
#    print("cnts 3: ", cnts)
    

        #image source, liste contours, -1 pour dessiner, couleur, epaisseur
        cv2.drawContours(outline, [cnts], -1, 255, -1) 
        # compute Zernike moments to characterize the shape
        # of pokemon outline, then update the index
    
        momentsArray = mahotas.features.zernike_moments(outline, raduis) #thresh, raduis) #outline, raduis)
    except:
        print("shape error")
    #Convert : array([....]) to [...]
    moments = []
    for m in momentsArray:
        moments.append(m)

    #"""
    cv2.imshow("", image)
    cv2.waitKey(0)
    cv2.imshow("", thresh)
    cv2.waitKey(0)
    cv2.imshow("", outline)
    cv2.waitKey(0)
    #"""

    #print("MOMENT Liste IS: ", moments)
    return moments


#Shape descriptor

#tres tres proche
queryFeatures = SearchShape(21, cv2.imread("./JPEGImages/2007_000033.jpg"))
features = SearchShape(21, cv2.imread("./JPEGImages/2007_000256.jpg"))
d = distance.euclidean(queryFeatures, features)
print("distance is proche: ", d)

I get the shape of some object, this method don t get needed shape for all images. the precision given to pretty low on my cbir system.

Image of result: Gray image

Threash image

Outline image