Search code examples
pythonimageopencvprocessingpixel

Finding boundary pixels with Opencv without for loop


I am trying to find boundary pixels of foreground object from binary image without using for loop. If anyone of the four neighbor of the specific pixel coordinate is zero and that pixel value is one, I will assign it as a boundary pixel. It works fine with for loop but I don't want to use a loop so is there anything I can do with it. Here is the code:

for i in range(len(PCR)):
    cr_h = PCR[i,0]
    cr_w = PCR[i,1]
    n1 = img_cap_copy[cr_h-1,cr_w]
    n2 = img_cap_copy[cr_h+1,cr_w]
    n3 = img_cap_copy[cr_h,cr_w-1]
    n4 = img_cap_copy[cr_h,cr_w+1]
    n=[n1,n2,n3,n4]
    if img_cap_copy[cr_h,cr_w]==1 and (n[0]==0 or n[1]==0 or n[2]==0 or n[3]==0):
        Xc.append(cr_w)
        Yc.append(cr_h)

This is what I am trying to do without for loop which is giving me error'The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().'

cr_h=PCR[:,0]
cr_h=PCR[:,0]
n1 = img_cap_copy[cr_h-1,cr_w]
n2 = img_cap_copy[cr_h+1,cr_w]
n3 = img_cap_copy[cr_h,cr_w-1]
n4 = img_cap_copy[cr_h,cr_w+1]
n=[n1,n2,n3,n4]
if img_cap_copy[cr_h,cr_w]==1 and (n[0]==0 or n[1]==0 or n[2]==0 or n[3]==0):
    Xc.append(cr_w)
    Yc.append(cr_h)

Solution

  • You may use np.logical_or and np.logical_and and logical indexing as follows:

    cr_h=PCR[:,0]
    cr_w=PCR[:,1] #cr_h=PCR[:,0]
    n1 = img_cap_copy[cr_h-1,cr_w]
    n2 = img_cap_copy[cr_h+1,cr_w]
    n3 = img_cap_copy[cr_h,cr_w-1]
    n4 = img_cap_copy[cr_h,cr_w+1]
    
    # Logical array with True where condition is met
    c = np.logical_and(img_cap_copy[cr_h,cr_w]==1, np.logical_or(np.logical_or(np.logical_or(n1==0, n2==0), n3==0), n4==0))
    
    # Use logical indexing - return elements when c is True.
    Xc = cr_w[c].tolist()
    Yc = cr_h[c].tolist()
    

    Testing code:

    import cv2
    import numpy as np
    
    # Build sample image
    img = np.random.randint(0, 255, (120, 160), np.uint8)
    img_cap_copy = np.minimum(cv2.threshold(img, 50, 255, cv2.THRESH_BINARY)[1], 1)
    
    # Build sample PRC
    PCR = np.vstack((np.r_[1:1001], np.r_[10:1010])).T % 100
    
    # Used as reference
    refXc = []
    refYc = []
    
    for i in range(len(PCR)):
        cr_h = PCR[i,0]
        cr_w = PCR[i,1]
        n1 = img_cap_copy[cr_h-1,cr_w]
        n2 = img_cap_copy[cr_h+1,cr_w]
        n3 = img_cap_copy[cr_h,cr_w-1]
        n4 = img_cap_copy[cr_h,cr_w+1]
        n=[n1,n2,n3,n4]
        if img_cap_copy[cr_h,cr_w]==1 and (n[0]==0 or n[1]==0 or n[2]==0 or n[3]==0):
            refXc.append(cr_w)
            refYc.append(cr_h)
    
    
    cr_h=PCR[:,0]
    cr_w=PCR[:,1] #cr_h=PCR[:,0]
    n1 = img_cap_copy[cr_h-1,cr_w]
    n2 = img_cap_copy[cr_h+1,cr_w]
    n3 = img_cap_copy[cr_h,cr_w-1]
    n4 = img_cap_copy[cr_h,cr_w+1]
    
    # Logical array with True where condition is met
    c = np.logical_and(img_cap_copy[cr_h,cr_w]==1, np.logical_or(np.logical_or(np.logical_or(n1==0, n2==0), n3==0), n4==0))
    
    # Use logical indexing - return elements when c is True.
    Xc = cr_w[c].tolist()
    Yc = cr_h[c].tolist()
    
    print(str(np.all(np.array(Xc) == np.array(refXc))))
    print(str(np.all(np.array(Yc) == np.array(refYc))))
    

    Note:

    • In case img_cap_copy has only ones and zeros, you may use math instead of logical operations, but using np.logical_or and np.logical_and is closer to your original loop code.