[PYTHON] Read digital image processing

I am studying with a book called Digital Image Processing. I implemented (contrast conversion) for the purpose of deepening understanding.

* Brightness contrast conversion *

Contrast is the degree of change in brightness and color. (Personal interpretation) Strong contrast → Easy to understand brightness and color transfer, easy to understand the outline of an object. Contrast is weak → It is difficult to understand the brightness and color transfer, and it is difficult to understand the outline of an object.

* Tone curve *

By changing the relationship between input and output, the color tone of the image can be changed. Therefore, it becomes easy to see or difficult to see.

* The first piece to use this time *

Use a dark sample like the one in the picture below to make it easier to see the effect.

Features: Pretty dark

python


#Required library
import cv2
import numpy as np
import matplotlib.pyplot as plt

#Photo path
pic_path = 'pet-bottle-pic/test/camera_pic/aquari3.jpg'

#Read
pic_image = cv2.imread(pic_path)

#Convert to gray
pic_image = cv2.cvtColor(pic_image, cv2.COLOR_BGR2GRAY)

#When read with cv, plt.cmap when using imshow='gray'Note that it will not be gray unless you specify
plt.imshow(pic_image, cmap='gray')
plt.title('sample_dark')
plt.savefig("aquari_dark.jpg ")

↓ It's dark and almost invisible

aquari_dark.jpg

python


#Pixel
pic_image.shape # (2464, 3280)

Pixel value distribution

python


#Flatten in one dimension to make a histogram
flat_data = np.ravel(pic_image)

#Bar span
bins_range = range(0, 260, 5)

#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_dark')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_dark.png ")

↓ There is a bias on the black side.

hist_aquari_dark.png

Implementation of contrast conversion

* Line type *

Depending on how it is made, the contrast in any range can be increased. (Although it is a little unnatural) Since the sample image is dense in the range of pixel values 0 to 50, the angle was set in that range.

python


def bend_line(X):
    y = X*5
    y = np.where(y>=255, 255, y)
    return y

Apply the function that outputs as shown in the graph below.

python


X = np.arange(0, 255, 1)
y = bend_line(X)

plt.title('sample_dark_bend_line')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')
plt.plot(X, y)
plt.savefig("bend_line.png ")

bend_line.png

Photo after application

python


pic_func1 = bend_line(pic_image)
plt.imshow(pic_func1, cmap='gray')
plt.title('sample_dark_after_bend_line')
plt.savefig("aquari_dark_after_bend_line.jpg ")

↓ You can now see PET bottles. (Although it looks a little dirty)

aquari_dark_after_bend_line.jpg

Let's look at the distribution.

python


flat_data = np.ravel(pic_func1)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_dark_after_bend_line')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_dark_after_bend_line.png ")

↓ The distribution has been smoothed out a little.

hist_aquari_dark_after_bend_line.png

* Exponentiation tone curve (gamma correction) *

Suitable for correcting images that are too dark or too bright to understand. It is suitable for increasing the contrast of images with a distribution near the edges.

Implement the following formula called the gamma function. $ y = \left( \frac{X}{255} \right)^\frac{1}{\gamma} \times 255\\ $ By the way, 1 / γ is introduced as γ on the Opencv official website.

python


def g_function(X, gamma):
    y = 255*(X/255)**(1/gamma)
    return y

Graph for each γ value

python


X = np.arange(0, 255, 1)
gamma = [3, 2, 1.5, 1, 0.5, 0.33]
labels = ["3", "2", "1.5", "1", "0.5", "0.33"]

plt.title('g_function')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')

for g, l in zip(gamma, labels):
    y = g_function(X, g)
    plt.plot(X, y, label = l)
plt.legend()


plt.savefig("g_function.png ")

g_function.png

Large γ → Increases the contrast of dark images overall. γ is small → Increases the contrast of bright images overall.

python


gamma = 3
pic_gfunc = g_function(pic_image, gamma)
plt.imshow(pic_gfunc, cmap='gray')
plt.title('sample_dark_after_gfunc')
plt.savefig("aquari_dark_after_gfunc.jpg ")

↓ It's pretty clean.

aquari_dark_after_gfunc.jpg

python


flat_data = np.ravel(pic_gfunc)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_dark_after_gfunc')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_dark_after_gfunc.png ")

↓ Similarly, the distribution is smoothed.

hist_aquari_dark_after_gfunc.png

* Next image to use

Features: Normal

python


#Photo path
pic_path = 'pet-bottle-pic/test/camera_pic/aquari.jpg'

#Read
pic_image = cv2.imread(pic_path)

#Convert to gray
pic_image = cv2.cvtColor(pic_image, cv2.COLOR_BGR2GRAY)

#When read with cv, plt.cmap when using imshow='gray'Note that it will not be gray unless you specify
plt.imshow(pic_image, cmap='gray')
plt.title('sample_normal')
plt.savefig("aquari.jpg ")

aquari.jpg

Pixel value distribution

python


#Flatten in one dimension to make a histogram
flat_data = np.ravel(pic_image)

#Bar span
bins_range = range(0, 260, 5)

#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_normal')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari.png ")

hist_aquari.png

* S-shaped curve type *

It is suitable for increasing the contrast of images with a distribution near the center. Implement the following formula $ y = \frac{255}{1 + \exp^{a\left(-X + 127\right)}}\\ $

python


def s_func(X, a):
    y = 255/(1 + np.exp(a*(-1*X + 127)))
    return y

python


X = np.arange(0, 255, 1)
a = 0.05
y = s_func(X, a)

plt.title('s_function')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')

plt.plot(X, y)
plt.savefig("s_function.png ")

s_function.png

python


a = 0.05
y = s_func(pic_image, a)
plt.imshow(y, cmap='gray')
plt.title('sample_after_sfunc')
plt.savefig("aquari_after_sfunc.jpg ")

↓ It was a little clear.

aquari_after_sfunc.jpg

python


flat_data = np.ravel(y)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_after_sfunc')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_sfunc.png ")

↓ The histogram became flat.

hist_aquari_sfunc.png

* Special effects *

There are some mysterious aspects to its usage, but I implemented it.

* Invert *

Bright → dark Dark → bright become.

python


def rev_func(X):
    y = -X + 255
    return y

python


X = np.arange(0, 255, 1)
y = rev_func(X)

plt.title('Rev_function')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')
plt.plot(X, y)
plt.savefig("rev_func.png ")

rev_func.png

python


y = rev_func(pic_image)
plt.imshow(y, cmap='gray')
plt.title('sample_after_rev_func')
plt.savefig("aquari_after_rev_func.jpg ")

aquari_after_rev_func.jpg

python


flat_data = np.ravel(y)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_after_rev_func')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_rev_func.png ")

↓ The distribution is also reversed on the left and right.

hist_aquari_rev_func.png

* Posterization *

It looks like a poster.

Brightness divided into 4 stages

Interval: 64 First stage: 64 Second stage: 128 Third stage: 192 Fourth stage: 255

python


def post_func(X):
    y = np.where(X>=192, 255, X)
    y = np.where((y >=128 ) & (y < 192), 170, y)
    y = np.where((y >=64 ) & (y < 128), 85, y)
    y = np.where(y<64, 0, y)
    return y

python


X = np.arange(0, 255, 1)
y = post_func(X)

plt.title('post_function')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')
plt.plot(X, y)
plt.savefig("post_func.png ")

post_func.png

python


y = post_func(pic_image)
plt.imshow(y, cmap='gray')
plt.title('sample_after_after_postfunc')
plt.savefig("aquari_after_postfunc.jpg ")

↓ Poster-like

aquari_after_postfunc.jpg

python


flat_data = np.ravel(y)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_after_postfunc')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_postfunc.png ")

hist_aquari_postfunc.png

* Binarization *

Black and white clear]

python


def to_func(X):
    y = np.where(X>=127, 255, X)
    y = np.where(y<127, 0, y)
    return y

python


X = np.arange(0, 255, 1)
y = to_func(X)

plt.title('2_function')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')
plt.plot(X, y)
plt.savefig("2_func.png ")

2_func.png

python


y = to_func(pic_image)
plt.imshow(y, cmap='gray')
plt.title('sample_after_after_tofunc')
plt.savefig("aquari_after_tofunc.jpg ")

↓ Is it hard to see on the contrary?

aquari_after_tofunc.jpg

python


flat_data = np.ravel(y)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_after_totfunc')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_tofunc.png ")

↓ It is binarized.

hist_aquari_tofunc.png

* Solarization *

It feels like a film when developing a photo.

python


from math import pi

def sora_func(X):
    X = (3*pi/255)*X
    y = -127.5*(np.cos(X))+127.5
    return y

python


X = np.arange(0, 255, 1)
y = sora_func(X)

plt.title('sora_function')
plt.xlabel('INPUT')
plt.ylabel('OUTPUT')
plt.plot(X, y)
plt.savefig("sora_func.png ")

sora_func.png

python


y = sora_func(pic_image)
plt.imshow(y, cmap='gray')
plt.title('sample_after_sorafunc')
plt.savefig("aquari_after_sorafunc.jpg ")

↓ Somehow it looks like that

aquari_after_sorafunc.jpg

python


flat_data = np.ravel(y)

bins_range = range(0, 260, 5)
 
#Output histogram
plt.hist(flat_data, bins = bins_range)
plt.title('sample_after_soratfunc')
plt.xlabel('pixel_value')
plt.ylabel('frequency')
plt.savefig("hist_aquari_sorafunc.png ")

hist_aquari_sorafunc.png

End

Recommended Posts

Read digital image processing
[Image processing] Posterization
Image processing 100 knocks ①
Image processing with MyHDL
Flat Field image processing
First Python image processing
Image processing with Python
Image Processing with PIL
Image processing with Python (Part 2)
opencv-python Introduction to image processing
Image processing with PIL (Pillow)
100 image processing knocks !! (011 --020) Early game
100 image processing knocks !! (001 --010) Carefully and carefully
Image processing with Python (Part 1)
Image processing with Python (Part 3)
Image processing by python (Pillow)
Image Processing Collection in Python
Image expansion and contraction processing
[Python] Image processing with scikit-image
Real-time image processing basics with opencv
Personal notes for python image processing
Image processing with Python 100 knocks # 3 Binarization
Image processing 100 knocks Q9, Q10 (filter) speedup
Environmentally friendly scraping using image processing
Image processing with Python 100 knocks # 2 Grayscale
Image processing | predicting species from images
Basics of binarized image processing with Python
Image processing by Python 100 knock # 1 channel replacement
Create an image processing viewer with PySimpleGUI
Image processing with Python 100 knocks # 8 Max pooling
Grayscale by matrix-Reinventor of Python image processing-
Image processing with Python & OpenCV [Tone Curve]
Image processing with Python 100 knock # 12 motion filter
100 image processing by Python Knock # 6 Color reduction processing
Drawing with Matrix-Reinventor of Python Image Processing-
Easy image processing in Python with Pillow
Image processing engineer certification (expert) pass strategy
Image processing with Python 100 knocks # 7 Average pooling
Image processing 100 knock Q.6. Color reduction processing explanation
Light image processing with Python x OpenCV
Image processing with Lambda + OpenCV (gray image creation)
Matrix Convolution Filtering-Reinventor of Python Image Processing-
Image processing with Python 100 knocks # 9 Gaussian filter