# Segmented transformation of gray scale transformation

## 1. general ¶

In digital image processing, segmented transformation is to apply different transformation functions to different gray levels, so as to enhance the gray levels of interest and suppress the gray levels of interest

The advantage of piecewise linear function is that it can stretch the gray details of features according to needs. Some important transformations can only be described and realized by piecewise function. The disadvantage is that it is difficult to determine the parameters

Typical images of piecewise linear functions are as follows:

In [1]:
import numpy as np
import matplotlib.pyplot as plt

def func(value):
if value <= 3:
return 0
if value <= 6:
return 2*(value-3)
else:
return 6
plt.figure(figsize=(6,4))
x = np.linspace(0, 8, 100)
y = np.array([])
for v in x:
y = np.append(y,np.linspace(func(v),func(v),1))
l=plt.plot(x,y,'b',label='value')
plt.text(3.2, 0.2, r'$x_1,y_1$')
plt.text(6, 5.7, r'$x_2,y_2$')
plt.legend()
plt.show()

The general formula of piecewise linear function is as follows:

$$f(x) = \left\{ \begin{matrix} \frac {y_1}{x_1}x \ \ \ \ \ \ \ \ \ \ \ ,\ x<x_1\\ \frac {y_2-y_1}{x_2-x_1}x+y_1 \ \ \ \ \ \ \ \ , \ x_1<x<x_2 \\ \frac {y_{max} \ \ - y_2}{x_{max}\ \ -x_2}x + y_2 \ \ \ , \ x>x_2 \\ \end{matrix} \right.$$

By using segment transformation, the gray value of an image in a certain interval can be enlarged, and the image contrast can be enhanced by concentrating the gray value in a certain interval

## 2. disadvantages of gamma transform ¶

Non segmented transformation (such as gamma transformation) will have an interval with a slope of about 1, which makes the contrast of the image in this interval almost unchanged after gamma change

In [2]:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt

def gamma_trans(input, gamma=2, eps=0 ):
return 255. * (((input + eps)/255.) ** gamma)

gray_img = np.asarray(Image.open('./image/washed_out_pollen_image.tif').convert('L'))

# Create a display body and divide it into four display areas
fig = plt.figure()

# Show original
ax1.set_title("origin")
ax1.imshow(gray_img, cmap='gray', vmin=0, vmax=255)
ax1.axes.xaxis.set_visible(False)
ax1.axes.yaxis.set_visible(False)

# Display the gray distribution histogram of the original image
ax2.grid(True, linestyle=':', linewidth=1)
ax2.set_title('origin', fontsize=12)
ax2.set_xlim(0, 255)  # Set x-axis distribution range
ax2.set_ylim(0, 0.15)  # Set y-axis distribution range
ax2.hist(gray_img.flatten(), bins=50,density=True,color='r',edgecolor='k')
ax2.axes.xaxis.set_visible(False)
ax2.axes.yaxis.set_visible(False)

gamma_ = 2
# Execute on original drawing γ Transformation
output = gamma_trans(gray_img, gamma_, 0.2)

# display γ Transform result image
ax3.set_title("result")
ax3.imshow(output, cmap='gray',vmin=0,vmax=255)
ax3.axes.xaxis.set_visible(False)
ax3.axes.yaxis.set_visible(False)

# display γ Gray distribution histogram of transformed image
ax4.set_xlim(0, 255)  # Set x-axis distribution range
ax4.set_ylim(0, 0.15)  # Set y-axis distribution range
ax4.grid(True, linestyle=':', linewidth=1)
ax4.set_title('result', fontsize=12)
ax4.hist(output.flatten(),bins=50,density=True,color='r',edgecolor='k')
ax4.axes.xaxis.set_visible(False)
ax4.axes.yaxis.set_visible(False)

plt.show()

It can be seen that for an image whose gray value is concentrated in a certain part in the middle, the gamma change can hardly improve the contrast

## 3. segment transformation ¶

According to the piecewise function formula:

$$f(x) = \left\{ \begin{matrix} \frac {y_1}{x_1}x \ \ \ \ \ \ \ \ \ \ \ ,\ x<x_1\\ \frac {y_2-y_1}{x_2-x_1}x+y_1 \ \ \ \ \ \ \ \ , \ x_1<x<x_2 \\ \frac {y_{max} \ \ - y_2}{x_{max}\ \ -x_2}x + y_2 \ \ \ , \ x>x_2 \\ \end{matrix} \right.$$

We can write the simplest and most direct piecewise transformation function:

In [3]:
# Three segment contrast stretch transformation, where x1,y1, X2 and Y2 are segment points, and X is the input matrix
def three_linear_trans(x, x1,y1, x2,y2):
# 1. check parameters to avoid denominator being 0
if x1 == x2 or x2 == 255:
print("[INFO] x1=%d,x2=%d ->Calling this function must meet:x1≠x2 And x2≠255" % (x1, x2))
return None

# 2. perform piecewise linear transformation
out = np.zeros(x.shape)
for i in range(x.shape[0]):
for j in range(x.shape[1]):
if x[i,j] < x1:
out[i,j] = y1/x1*x[i,j]
elif x1 <= x[i,j] <= x2:
out[i,j] = (y2-y1)/(x2-x1)*(x[i,j]-x1)+y1
elif x[i,j] > x2:
out[i,j] = (255-y2)/(255-x2)*(x[i,j]-x2)+y2
return out

Segment the image just now:

In [4]:
# Read image in grayscale
gray_img = np.asarray(Image.open('./image/washed_out_pollen_image.tif').convert('L'))

# Create a display body and divide it into five display areas
fig = plt.figure()

# Display the original image and its gray distribution histogram
ax1.set_title("origin", fontsize=8)
ax1.imshow(gray_img,cmap='gray',vmin=0,vmax=255)

ax3.grid(True, linestyle=':', linewidth=1)
ax3.set_xlim(0, 255)  # Set x-axis distribution range
ax3.set_ylim(0, 0.15)  # Set y-axis distribution range
ax3.hist(gray_img.flatten(), bins=50, density=True, color='r', edgecolor='k')

# Perform piecewise linear transformation
x1,y1,x2,y2 = 90, 3, 140, 250
out = three_linear_trans(gray_img,x1,y1,x2,y2)

# Display transform result image
ax2.clear()
ax2.set_title("result", fontsize=8)
ax2.imshow(out,cmap='gray',vmin=0,vmax=255)

# Display gray distribution histogram of transformation results
ax4.clear()
ax4.grid(True, linestyle=':', linewidth=1)
ax4.set_xlim(0, 255)  # Set x-axis distribution range
ax4.set_ylim(0, 0.15)  # Set y-axis distribution range
ax4.hist(out.flatten(), bins=50, density=True, color='r', edgecolor='k')

plt.show()

It can be seen that the contrast is significantly enhanced, and the color distribution is no longer concentrated in a certain interval in the middle

Of course, this expanded distribution of colors concentrated in a certain interval is also called histogram equalization, which means that the color values of the image are redistributed to make the distribution of color values more uniform

## 4. algorithm optimization ¶

Here, we make full use of Numpy's broadcast mechanism and parallel computing to optimize the segment transformation just now

The calculation of each section of function is realized through three mask matrices, and the final transformation result is the sum of them:

In [5]:
# Three segment contrast stretch transformation, where x1,y1, X2 and Y2 are segment points, and X is the input matrix
def three_linear_trans2(x, x1,y1, x2,y2):
# 1. check parameters to avoid denominator being 0
if x1 == x2 or x2 == 255:
print("[INFO] x1=%d,x2=%d ->Calling this function must meet:x1≠x2 And x2≠255" % (x1, x2))
return None

# 2. perform piecewise linear transformation
out = np.zeros(x.shape)
m1=(x<x1)
m2=(x1<=x)&(x<=x2)
m3=(x>x2)

out = (y1/x1*x)*m1 + ((y2-y1)/(x2-x1)*(x-x1)+y1)*m2 + ((255-y2)/(255-x2)*(x-x2)+y2)*m3
return out

Execution time comparison:

In [6]:
import time

gray_img = np.asarray(Image.open('./image/washed_out_pollen_image.tif').convert('L'))

x1,y1,x2,y2 = 90, 3, 140, 250

ticks1 = time.time()
out = three_linear_trans(gray_img,x1,y1,x2,y2)
print("Elapsed time( s)by:", time.time()-ticks1)

ticks2 = time.time()
out = three_linear_trans2(gray_img,x1,y1,x2,y2)
print("Elapsed time( s)by:",  time.time()-ticks2)

Elapsed time( s)by: 7.202014446258545
Elapsed time( s)by: 0.017905712127685547

You can see a significant improvement in execution efficiency

## 5. references ¶

Tags: Python

Posted by radman08 on Thu, 30 Jun 2022 17:11:45 +0530