I'mMotivated
I'mMotivated

Reputation: 195

I can't see the problem with numpy array (image-processing)

I try to manually adjust the contrast of the image. The 2nd code works fine with me but it uses loop -> it's slow so I change to 1st code but get a different output.

I don't know what gone wrong because I assumed that these 2 are alike. Can somebody tell me what did I miss? Thanks a lot.

Code with no-loop

from PIL import Image

# level should be in range of -255 to +255 to decrease or increase contrast
def change_contrast(img, level):
    def truncate(v):
        v[v<0] = 0
        v[v>255] = 255
        return v

    img_c = np.reshape(img,(1,img.shape[0] *img.shape[1],3))[0].copy()
   
    

    factor = (259 * (level+255)) / (255 * (259-level))
    img_c = factor * (img_c-128) + 128
    img_c = truncate(img_c)

    return np.reshape(img_c,(img.shape[0] ,img.shape[1],3)).astype(np.uint8)

Code with loop

from PIL import Image

# level should be in range of -255 to +255 to decrease or increase contrast
def change_contrast(img, level):
    def truncate(v):
        v[v<0] = 0
        v[v>255] = 255
        return v

    img_c = np.reshape(img,(1,img.shape[0] *img.shape[1],3))[0].copy()
   
    factor = (259 * (level+255)) / (255 * (259-level))
    for x in range(img_c.shape[0]):
        color = img_c[x]
        new_color = truncate(np.asarray([int(factor * (c-128) + 128) for c in color]))
        img_c[x]=     new_color
    return np.reshape(img_c,(img.shape[0] ,img.shape[1],3))

Test code

a = PIL.Image.open('Test.jpg')
b = np.asarray(a)
Image.fromarray(change_contrast(b , 128))

Result:

Original

non_loop:

non_loop

loop:

loop

Solution: my original array has uint8 type which won't let you have negative numbers (ex: img_c - 128). Change to int should help!

Gotta check type more carefully.

Upvotes: 1

Views: 769

Answers (1)

perl
perl

Reputation: 9941

The problem is that img is an unsigned integer uint8, so when you subtract 128, it would get clipped at 0 instead of going negative.

If you convert the image to int, it would work as expected:

a = PIL.Image.open('test.jpeg')
b = np.asarray(a).astype(int)
Image.fromarray(change_contrast(b, 128))

Here's also a shorter version of your function (you can use np.clip instead of a custom truncate function):

def change_contrast(img, level):
    img_c = img.astype(int).copy()
    factor = (259 * (level+255)) / (255 * (259-level))
    img_c = factor * (img_c - 128) + 128
    img_c = np.clip(img_c, 0, 255)
    return img_c.astype(np.uint8)

P.S. I've included astype(int) inside this function to ensure that it's signed integer, so you don't have to convert before passing the image to the function.

Upvotes: 1

Related Questions