note.nkmk.me

Image processing with Python, NumPy (read, process, save)

Posted: 2019-05-14 / Tags: Python, NumPy, Image Processing

By storing the images read by Pillow(PIL) as a NumPy array ndarray, various image processing can be performed using NumPy functions.

By the operation of ndarray, acquisition and rewriting of pixel values, trimming by slice, concatenating can be done. Those who are used to NumPy can do a lot of things without using libraries such as OpenCV.

Even when using OpenCV, Python's OpenCV treats image data as ndarray, so it is useful to remember the processing in NumPy (ndarray).

Here, the following contents will be described.

Read and write images:

  • How to read image file as NumPy array ndarray
  • How to save NumPy array ndarray as image file

Examples of image processing with NumPy (ndarray):

  • Generation of single color image and concatenation
  • Negative / positive inversion (inversion of pixel value)
  • Color reduction
  • Binarization
  • Gamma correction
  • Trimming with slice
  • Paste with slice
  • Alpha blending and masking

Here I will describe reading and saving of image files using Pillow. Refer to the following post about reading and saving image files with OpenCV.

See also the following post about Pillow.

Sponsored Link

How to read an image file as ndarray

Pass the image data read by PIL.Image.open() to np.array() to obtain ndarray.

RGB (color) images become 3D ndarray (row (height) x column (width) x color (3)), black and white (grayscale) images become 2D ndarray (row (height) x column (width)).

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'))

print(im.dtype)
# uint8

print(im.ndim)
# 3

print(im.shape)
# (512, 512, 3)

When converting from PIL.Image to ndarray, the color order is RGB (red, green, blue). Note that this is different from when reading image files with OpenCV (BGR). If you want to convert the order, see the following post.

For more information about image size acquisition, see the following post.

If you want to calculate with decimal point for image data, read in float.

im_f = np.array(Image.open('data/src/lena_square.png'), np.float)

print(im_f.dtype)
# float64

Because it is ndarray, acquisition of pixel value is easy. The origin (0, 0) is the upper left of the image. Of course, methods such as min() and max() can be used as they are.

In the following example, the pixel value [R, G, B] at the position of (x, y) = (256, 256) and the minimum value of R are acquired.

print(im[256, 256])
# [180  65  72]

print(im[:, :, 0].min())
# 54

Examples of other processes are described later.

How to save ndarray as image file

Pass ndarray to Image.fromarray() to obtain PIL.Image.

PIL.Image can be saved as an image file with save().

pil_img = Image.fromarray(im)
pil_img.save('data/temp/lena_square_save.png')

You can write it in one line.

Image.fromarray(im).save('data/temp/lena_square_save.png')

If the data type dtype of ndarray is float etc., convert it to uint8 (unsigned 8-bit integer) to save it as JPG or PNG.

pil_img_f = Image.fromarray(im_f.astype(np.uint8))
pil_img_f.save('data/temp/lena_square_save.png')

Note that if the pixel value is represented by 0.0 to 1.0, it is necessary to multiply by 255 and convert to uint8 and save.

Generation of single color image and concatenation

Generate single-color images by setting other color values to 0, and concatenate them horizontally with np.concatenate(). You can also concatenate images using np.hstack() or np.c_[]

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'))

im_R = im.copy()
im_R[:, :, (1, 2)] = 0
im_G = im.copy()
im_G[:, :, (0, 2)] = 0
im_B = im.copy()
im_B[:, :, (0, 1)] = 0

im_RGB = np.concatenate((im_R, im_G, im_B), axis=1)
# im_RGB = np.hstack((im_R, im_G, im_B))
# im_RGB = np.c_['1', im_R, im_G, im_B]

pil_img = Image.fromarray(im_RGB)
pil_img.save('data/dst/lena_numpy_split_color.jpg')
NumPy image processing split color

Negative / positive inversion (invert pixel value)

It is also easy to calculate and process pixel values.

A negative-positive inverted image can be generated by subtracting the pixel value from the max value (255 for uint8).

import numpy as np
from PIL import Image

im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))

im_i = 255 - im

Image.fromarray(im_i).save('data/dst/lena_numpy_inverse.jpg')

Python NumPy inverse

Color reduction

Cut off the remainder of the division using // and multiply again, the pixel values become discrete values and the number of colors can be reduced.

import numpy as np
from PIL import Image

im = np.array(Image.open('data/src/lena_square.png').resize((256, 256)))

im_32 = im // 32 * 32
im_128 = im // 128 * 128

im_dec = np.concatenate((im, im_32, im_128), axis=1)

Image.fromarray(im_dec).save('data/dst/lena_numpy_dec_color.png')

Python NumPy decrease color

Sponsored Link

Binarization

It is also possible to assign to black and white according to the threshold.

See the following articles for details.

Python NumPy OpenCV binarization

Gamma correction

You can do multiplication, division, exponentiation, anything.

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'), 'f')

im_1_22 = 255.0 * (im / 255.0)**(1 / 2.2)
im_22 = 255.0 * (im / 255.0)**2.2

im_gamma = np.concatenate((im_1_22, im, im_22), axis=1)

pil_img = Image.fromarray(np.uint8(im_gamma))
pil_img.save('data/dst/lena_numpy_gamma.jpg')
NumPy image processing split gamma

Trimming with slice

By specifying an area with slice, you can trim it to a rectangle.

from PIL import Image
import numpy as np

im = np.array(Image.open('data/src/lena_square.png'))

print(im.shape)
# (512, 512, 3)

im_trim1 = im[128:384, 128:384]
print(im_trim1.shape)
# (256, 256, 3)

Image.fromarray(im_trim1).save('data/dst/lena_numpy_trim.jpg')

numpy image trimming 1

It may be convenient to define a function that specifies the upper left coordinates and the width and height of the area to be trimmed.

def trim(array, x, y, width, height):
    return array[y:y + height, x:x+width]

im_trim2 = trim(im, 128, 192, 256, 128)
print(im_trim2.shape)
# (128, 256, 3)

Image.fromarray(im_trim2).save('data/dst/lena_numpy_trim2.jpg')

numpy image trimming 2

If an area outside the size of the image is specified, it is ignored.

im_trim3 = trim(im, 128, 192, 512, 128)
print(im_trim3.shape)
# (128, 384, 3)

Image.fromarray(im_trim3).save('data/dst/lena_numpy_trim3.jpg')

numpy image trimming 3

Paste with slice

Using slices, one array rectangle can be replaced with another array rectangle.

By using this, a part of the image or the entire image can be pasted to another image.

import numpy as np
from PIL import Image

src = np.array(Image.open('data/src/lena_square.png').resize((128, 128)))
dst = np.array(Image.open('data/src/lena_square.png').resize((256, 256))) // 4

dst_copy = dst.copy()
dst_copy[64:128, 128:192] = src[32:96, 32:96]

Image.fromarray(dst_copy).save('data/dst/lena_numpy_paste.jpg')

numpy image paste

dst_copy = dst.copy()
dst_copy[64:192, 64:192] = src

Image.fromarray(dst_copy).save('data/dst/lena_numpy_paste_all.jpg')

numpy image paste all

Note that an error will occur if the size of the area specified on the left side differs from the size of the area specified on the right side.

Alpha blending and masking

By the operation for each element (= pixel) of the array, two images can be alpha-blended or composited based on a mask image. See the following articles for details.

NumPy image alpha blend gradation

NumPy image blend blur

Sponsored Link
Share

Related Categories

Related Posts