Search code examples
pythonmatplotlibsequential

Unusual program flow and variable modification in Python


I am trying to fix my thresholding function as it did not appear to work correctly. This is when I came across a strange phenomena that really makes me think there is something I really don't understand about Python/programming. `

b = c
c = thresh(c)
plt.subplot(121),plt.imshow(b,cmap='gray'),plt.title('C')
plt.subplot(122),plt.imshow((c),cmap='gray'),plt.title('C tresh')   
plt.show()

This is the erroneous code, I was trying to compare side by side to see the effects my thresholding function tresh() had on the image I am manipulating.

Both images displayed below are the same even though the thresholding function has been applied to only one of the variables being displayed, namely c. Variable b is not modified in anyway before being displayed, yet it is displayed in same way as c which has been thresholded.

enter image description here

b = c

plt.subplot(121),plt.imshow(b,cmap='gray'),plt.title('C')
plt.subplot(122),plt.imshow(thresh(c),cmap='gray'),plt.title('C tresh') 
plt.show()

I tried to fix it by having the thresholding performed implicitly inside of the imshow() function and to my surprise it worked.

enter image description here

I can not think of a way to explain why the original code snippet produced same images and why my "fix" managed to produce two different images.

thresholding function,

def thresh(image):
    x = len(image)
    y = len(image[0])
    tresh = 180
    for ix in range(x):
        for iy in range(y):
            if image[ix][iy] <tresh:
                image[ix][iy] = 0   
    return image    

Solution

  • Your problem is due to the two facts: Python pass argument by assignment (in your case image inside your function, b and c refer to the same object), and lists are mutable object (you can modify to states of the object).

    So when you are modifying image inside thresh, your are modifying the underlying object and thus b and c.

    To avoid the issue, you need to pass a copy of b to the function (see this question and corresponding answers for more info on the different ways to achieve it).

    In your case, you need to change your code to:

    from copy import deepcopy
    b = deepcopy(c)
    c = thresh(c)
    plt.subplot(121),plt.imshow(b,cmap='gray'),plt.title('C')
    plt.subplot(122),plt.imshow((c),cmap='gray'),plt.title('C tresh')   
    plt.show()
    

    Also note that you don't need to return image at the end of your function in this case.