How to Transform Normalization Image Math Equation to Python? - python

I try to learn how to transform equation to python script.
I choose to start it from FingerPrint Enhancement from Academic resources here.
to start learn i search a fingerprint image to be enhance. I choose this image:
so, i do the first step is converting to gray:
import cv2
import numpy as np
input = 'PATH OF IMAGE'
img = cv2.imread(input)
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
and below is the result:
ok the problem start from here...
please try to understood me, I try to learn how to convert math equation to python script.
not try to looking for another / existing script in Github (for example).
the equation is:
all detail from the academic research. Told that:
Let I(i, j) denote the gray-level value at pixel (i, j), M and
VAR denote the estimated mean and variance of I, respectively, and G(i, j) denote the normalized gray-level value at pixel (i, j).
A gray-level fingerprint image, I is defined as an N x N matrix, where I(i, j) represents the intensity of the pixel at the
i-th row and j-th column. We assume that all the images are
scanned at a resolution of 500 dots per inch (dpi). The mean and variance of a gray-level fingerprint image, I, are defined as
and
respectively
ok, we start to transform the equation:
def mean(gray):
rows, cols = gray.shape
sum = 0
for i in range(0,rows):
for j in range(0, cols):
pix = (gray[i,j].item())
sum += pix
M = sum/N
return M
def var(gray, M):
rows, cols = gray.shape
N = gray.size
sum = 0
for i in range(0,rows):
for j in range(0, cols):
vix = ((img[i,j].item()) - M)**2
sum += vix
VAR = sum/N
return VAR
def normalize(img, M0, VAR0):
M = mean(img)
VAR = var(img, M)
rows,cols = img.shape
normim = np.zeros((rows, cols))
for i in range(0, rows):
for j in range(0, cols):
if (gray[i,j].item()) > M:
G0 = M0 + ((((VAR0)*(((gray[i,j].item())-(M))**2))/(VAR))**(1/2))
normim[i,j] = int(G0)
else:
G1 = M0 - ((((VAR0)*(((gray[i,j].item())-(M))**2))/(VAR))**(1/2))
normim[i,j] = int(G1)
return normim
M0 = 100 #follow the academic research document
VAR0 = 100 #follow the academic research document
normgray = normalize(gray, 100,100)
cv2.imshow('test', normgray)
cv2.waitKey(1)
the result is out of expected:
all is white.
can somebody help me? please your advise.
to remind you, I'm not try to looking for the another script / another example. I try to understood how to transform a math equation to python script. about another script, i already have, even i already map it here.

This is a simple problem of not respecting the data types in between transformations. Specifically, when you load in the image, it is going to be unsigned 8-bit integer so the expected values should be within [0, 255], yet your calculations for the mean and variance will exceed this dynamic range and thus your calculations will overflow. The quickest way to resolve this problem is to convert your image so that it will respect a data type that can handle the precision of the calculations you want, like floating-point. Perform the calculations, and when you're done convert the image back to the expected data type, so unsigned 8-bit integer.
In addition, there are several errors in your code. For one thing, you didn't provide the variable N, which should be the total number of pixels in the image. In addition, your var function accepts gray as the variable yet you are using img to try and access pixel data, so this will also give off an error when you try and run it. Finally, you omitted the packages you're using so I added these in.
I've also downloaded your image locally so I can run the code to verify that it works. I've patched up the end of your code so that the image window that displays the result properly closes after you push a key and I've written the output image to file.
Therefore:
# Added so the code can run
import cv2
import numpy as np
# Added so the code can run
gray = cv2.imread('gnN4Q.png', 0)
gray = gray.astype(np.float) # Change to floating-point
N = gray.shape[0]*gray.shape[1]
def mean(gray):
rows, cols = gray.shape
sum = 0
for i in range(0,rows):
for j in range(0, cols):
pix = (gray[i,j].item())
sum += pix
M = sum/N # Added above
return M
def var(gray, M):
rows, cols = gray.shape
N = gray.size
sum = 0
for i in range(0,rows):
for j in range(0, cols):
vix = ((gray[i,j].item()) - M)**2 # Change
sum += vix
VAR = sum/N
return VAR
def normalize(img, M0, VAR0):
M = mean(img)
VAR = var(img, M)
rows,cols = img.shape
normim = np.zeros((rows, cols))
for i in range(0, rows):
for j in range(0, cols):
if (gray[i,j].item()) > M:
G0 = M0 + ((((VAR0)*(((gray[i,j].item())-(M))**2))/(VAR))**(1/2))
normim[i,j] = int(G0)
else:
G1 = M0 - ((((VAR0)*(((gray[i,j].item())-(M))**2))/(VAR))**(1/2))
normim[i,j] = int(G1)
return normim
M0 = 100 #follow the academic research document
VAR0 = 100 #follow the academic research document
normgray = normalize(gray, 100,100)
normgray = normgray.astype(np.uint8) # Added - convert back to uint8
cv2.imshow('test', normgray)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.imwrite('output.png', normgray)
The output image we get is:

I didn't run your code but make sure G0 or G1 doesn't get too big. It could be that your value is above 255, thus the resulting all-white image.

Related

What are the inaccuracies of this 'inverse map' function in OpenCV?

I am trying to horizontally stretch an image in a very specific way. Each x prime coordinate should follow a tangent path with respect to the original x coordinate. I believe there are two ways to do this:
Inverse the tangent function and map it normally
Map the tangent function and then inverse the mapping
Using this answer for map inversion, Im trying to figure out why the two images are not the same. I know that the first method gives me the correct image that I'm looking for, so why doesnt the second method work? Is it because of the "limited precision" that #ChristophRackwitz commented on the answer?
import cv2
import glob
import numpy as np
import math
A = -1010
B = -3.931
C = 5.258
D = 978.3
M = -193.8
N = 1740
def get_tan_func_value(x):
return A * math.tan((((x-N)/M)+B)/C) + D
def get_inverse_tan_func_value(x):
return M * (C*math.atan((x-D)/A) - B) + N
# answer from linked post
def invert_map(F, shape):
I = np.zeros_like(F)
I[:,:,1], I[:,:,0] = np.indices(shape)
P = np.copy(I)
for i in range(10):
P += I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
return P
# import image
images = glob.glob('*.jpg')
img = cv2.imread(images[0])
h, w = img.shape[:2]
map_x_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_x_inverse_tan = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_y = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
# x tan function map
for i in range(map_x_tan.shape[0]):
map_x_tan[i,:] = [get_tan_func_value(x) for x in range(map_x_tan.shape[1])]
# x inverse tan function map
for i in range(map_x_inverse_tan.shape[0]):
map_x_inverse_tan[i,:] = [get_inverse_tan_func_value(x) for x in range(map_x_inverse_tan.shape[1])]
# default y map
for j in range(map_y.shape[1]):
map_y[:,j] = [y for y in range(map_y.shape[0])]
# convert x tan map to 2 channel (x,y) map
(xymap_tan, _) = cv2.convertMaps(map1=map_x_tan, map2=map_y, dstmap1type=cv2.CV_32FC2)
# invert the 2 channel x tan map
xymap_inverted = invert_map(xymap_tan, (h,w))
# remap and write the target image (inverse tan function with normal map)
target = cv2.remap(img, map_x_inverse_tan, map_y, cv2.INTER_LINEAR)
cv2.imwrite("target.jpg", target)
# remap and write the attempted image (normal tan function with inverted map)
attempt = cv2.remap(img, xymap_inverted, None, cv2.INTER_LINEAR)
cv2.imwrite("attempt.jpg", attempt)
Method 1: Target Image
Method 2: Attempt Image
The results show that the attempt (normal tan function with inverted map) has less stretching near the edges of the image than expected. Almost everywhere else on the images are identical except the edges. I did not post the original picture to save space.
I've played around with that invert_map procedure. It seems slightly susceptible to oscillation.
use this instead:
def invert_map(F):
(h, w) = F.shape[:2] # (h, w, 2), "xymap"
I = np.zeros_like(F)
I[:,:,1], I[:,:,0] = np.indices((h,w)) # identity map
P = np.copy(I)
for i in range(10):
correction = I - cv2.remap(F, P, None, interpolation=cv2.INTER_LINEAR)
P += correction * 0.5
return P
I simply damped the correction by 0.5, which makes the fixed point iteration tamer, converging a lot faster too.
In my experiments with your tan map, I've found that 5-10 iterations are good enough already, and there's no further progress in further iterations.
Entire notebook of my explorations: https://gist.github.com/crackwitz/67f76f8a9eff21476b080c06d20660d0
Feature request: https://github.com/opencv/opencv/issues/22120

Python GDAL, how to change brightness without parsing each pixel

I'm currently trying to figure out how to increase/decrease the brightness of a .tiff file without parsing each pixel (too high power consumption). Right now, using the front micro-service, the user uses a ng-slider to change the value of the desired brightness,which goes directly to the back where it is parsed to try to compute a new .tiff.
So, I'm wondering if there isn't a gdal function I can't find to directly alter the images and increase/decrease the brightness at will!
The code currently looks like this (also trying to change the contrast, but I could find my way if I understand how to change the brightness) :
# Contrast & Luminosity
def get_correctMap(path, luminosity, contrast):
ds = gdal.Open(image_path)
#To normalize
band1 = ds.GetRasterBand(1)
#Get the max value
maxValue = int(2**16 -1)
if band1.DataType == gdal.GDT_UInt16:
maxValue = int(2**16 -1)
elif band1.DataType == gdal.GDT_Byte:
maxValue = int(2**8 -1)
else:
LOGGER.info(f"band type {band1.DataType} not handled: use default size of value (16 bits)")
band1 = ds.ReadAsArray(0,0,ds.RasterXSize,ds.RasterYSize)[0]
band2 = ds.ReadAsArray(0,0,ds.RasterXSize,ds.RasterYSize)[1]
band3 = ds.ReadAsArray(0,0,ds.RasterXSize,ds.RasterYSize)[2]
for x in range(0,ds.RasterXSize):
for y in range(0,ds.RasterXSize):
r = float(band1[x,y]) / maxValue
g = float(band2[x,y]) / maxValue
b = float(band3[x,y]) / maxValue
#Convert to HLS them apply luminosity and contrast
(h,l,s) = colorsys.rgb_to_hls(r, g, b)
l = min(max(0, l + (l - 0.5)*(luminosity - 0.5)) , 1)
s = min(max(0, s + (s - 0.5)*(contrast - 0.5)) , 1)
(r,g,b) = colorsys.hls_to_rgb(h, l, s)
band1[x,y] = int(r * maxValue)
band2[x,y] = int(g * maxValue)
band3[x,y] = int(b * maxValue)
#Need to save the changes, but obviously already too long to pursue this way
#and save the news bands
ds.flushCache()
return path
Hope you know a better way I can't find!
Thanks in advance.
A first lead could be to use the last features provide by OpenLayer for me, but it is not a back solution anymore, I'm digging it.
https://geoadmin.github.io/ol3/apidoc/ol.layer.Tile.html
EDIT: The constrast and luminosity feature are only implemented on OpenLayer 3 but not in the next version (including mine OL 5), so the proper answer is : it is not possible.

Compute a least square only on n best values of an array

I have :
print(self.L.T.shape)
print(self.M.T.shape)
(8, 3)
(8, 9082318)
self.N = np.linalg.lstsq(self.L.T, self.M.T, rcond=None)[0].T
which is working fine and return
(9082318, 3)
But
I want to perform a kind of sort on M and compute the solution only on the best 8 - n values of M.
Or ignore values of M below and/or higher than a certain value.
Any pointer on how to do that would be extremely appreciated.
Thank you.
Tried to copy this solution exactly but it return an error
The original working function but basically it's just one line.
M is a stack of 8 grayscale images reshaped.
L is a stack of 8 light direction vectors.
M contains shadows but not always at the same location in the image.
So I need to remove those pixel from the computation but L must retains its dimensions.
def _solve_l2(self):
"""
Lambertian Photometric stereo based on least-squares
Woodham 1980
:return: None
Compute surface normal : numpy array of surface normal (p \times 3)
"""
self.N = np.linalg.lstsq(self.L.T, M.T, rcond=None)[0].T
print(self.N.shape)
self.N = normalize(self.N, axis=1) # normalize to account for diffuse reflectance
Here is the borrowed code from the link for trying to resolve this :
L and M as previously used
Ma = self.M.copy()
thresh = 300
Ma[self.M <= thresh] = 0
Ma[self.M > thresh] = 1
Ma = Ma.T
self.M = self.M.T
self.L = self.L.T
print(self.L.shape)
print(self.M.shape)
print(Ma.shape)
A = self.L
B = self.M
M = Ma #http://alexhwilliams.info/itsneuronalblog/2018/02/26/censored-lstsq/
# else solve via tensor representation
rhs = np.dot(A.T, M * B).T[:,:,None] # n x r x 1 tensor
T = np.matmul(A.T[None,:,:], M.T[:,:,None] * A[None,:,:]) # n x r x r tensor
self.N = np.squeeze(np.linalg.solve(T, rhs)).T # transpose to get r x n
return
numpy.linalg.LinAlgError: Singular matrix

Can I do a mathematical-shape mask for a (Nx,Ny) matrix?

I have a matrix of numbers (Nx,Ny) and I would like to select from that matrix a mathematical shape coordinates/components as it could be a line with a given slope.
I learned how to create a mask and how to do it in a random way but I cannot think of how to generate a mathematical-shape mask in python.
This is some of the code that I've been able to develop. If you know a better way to do this I will be also grateful of being told.
import random
import numpy as np
threshold = 0.85
radius=40
sq7=1/(radius*radius)
matrix=np.zeros((Nx,Ny))
for i in range(0,Nx):
for j in range(0,Ny):
if ((i-Nx*0.5)*(i-Nx*0.5)*sq7+(j-Ny*0.5)*(j-Ny*0.5)*sq7<=1.0):
matrix[i,j]= 1.0 - 0.1*random.random();
else:
matrix[i,j]=-1.0 + random.random();
randoms = np.random.normal(0,scale=0.002, size=matrix[mask].shape)
mask = matrix**2 < threshold
matrix[mask] += randoms * (1 - matrix[mask]**2)
At the end I found a very easy way of doing so. What I just did was to create a new matrix of the same dimensions as the one that I want to mask and then just by going through the matrix itself and comparing with the values of my function I could do it so easily. I will leave the code here.
def func_normaldist(x,Ny):
y = np.exp(-0.5*(x-int(Ny/2))**2)/np.sqrt(np.pi*2.)
return y
def mask_uvalues_centered_geometry(Nx, Ny): #u
mask = np.zeros((Nx,Ny))
# Initial configuration: rectangle of Nx x Ny
for j in range(0,Ny):
for i in range(0,Nx):
if (i < Ny*Nx*func_normaldist(j,Ny)):# and (i > int(Nx/2 + 1)):
mask[j,i] = True
else:
mask[j,i] = False;
return mask
Nx = 50
Ny = 50
a = mask_uvalues_centered_geometry(Nx,Ny)
print(a)

Non local maxima suppression in python

Goal: To input an image (2d numpy array) and a window size, and output the same array with the local maxima remaining, but 0 elsewhere.
What I am struggling with: I think I made a stupid mistake in my code, maybe a few typos in my loop but I am not sure (the local maxima are only on the left side of the image, which is not true). As I note below I would also welcome any easy tricks with OpenCV or numpy to make this solution shorter.
similar questions: Finding Local Maxima in an Image
and Find local maxima in grayscale image using OpenCV
Mine is different because: I am looking to surpress all but local maxima. I don't have to use my code below, I haven't been able to find a built in function of opencv or numpy to do what I need to (harris corners in cv will implicitly do this as one of the steps, but I need something to perform the sole operation I need). I read a little bit on dilate and wasn't sure if that would be useful here as well.
What I have tried so far.
def nonMaximalSupress(image,NHoodSize):
#For
for x in range(0,image.shape[0]-1):
if x+NHoodSize[0]<image.shape[0]:
#while we can still take a square
#print "AHH ", image.shape
startWindow=0
for y in range(startWindow,image.shape[1]-NHoodSize[1]):
#try:
if np.sum(image[x:x+NHoodSize[0]][y:y+NHoodSize[1]])==0:
localMax=0
else:
localMax = np.amax(image[x:x+NHoodSize[0]][y:y+NHoodSize[1]])
#except ValueError:
#localMax=0
#print "local max is ", localMax
maxCoord=np.unravel_index(np.argmax((image[x:x+NHoodSize[0],y:y+NHoodSize[1]])),
image.shape)+np.array((x,y))
#print "X is %r, Y is %r, max coord is %r \n y+nhood is %r" %(x,y,maxCoord,y+NHoodSize[1])
#suppress everything
image[x:x+NHoodSize[0]][y:y+NHoodSize[1]]=0
#reset only the max
#print maxCoord
if localMax > 0:
print localMax
print "max coord is ", maxCoord[0], maxCoord[1]
image[maxCoord[0]][maxCoord[1]]=localMax
#increment y
x+=NHoodSize[0]
return image
How about something like this:
# Use the max filter to make a mask
roi = 3
size = 2 * roi + 1
image_max = ndimage.maximum_filter(image, size=size, mode='constant')
mask = (image == image_max)
image *= mask
# Remove the image borders
image[:roi] = 0
image[-roi:] = 0
image[:, :roi] = 0
image[:, -roi:] = 0
# Optionally find peaks above some threshold
image_t = (image > peak_threshold) * 1
# get coordinates of peaks
f = np.transpose(image_t.nonzero())
This may not be entirely correct, but it works better on a small test case
def nonMaximalSupress1(image,NHoodSize):
#
dX, dY = NHoodSize
M, N = image.shape
for x in range(0,M-dX+1):
for y in range(0,N-dY+1):
window = image[x:x+dX, y:y+dY]
if np.sum(window)==0:
localMax=0
else:
localMax = np.amax(window)
maxCoord=np.unravel_index(np.argmax(window), window.shape) + np.array((x,y))
#suppress everything
image[x:x+dX, y:y+dY]=0
#reset only the max
if localMax > 0:
print localMax
print "max coord is ", maxCoord
image[tuple(maxCoord)] = localMax
return image
I've used local variables to make things easier to read, and tweaked the loop ranges. But the big change is in how I index image. Especially when indexing with slices, you must use one set of brackets.
image[x:x+dX, y:y+dY] is the correct way to select a window, not image[x:x+dX][y:y+dY].
It can be cleaned up a bit more by modifying the window. Since it is a view, changing it changes image.
def nonMaximalSupress2(image,NHoodSize):
#
dX, dY = NHoodSize
M, N = image.shape
for x in range(0,M-dX+1):
for y in range(0,N-dY+1):
window = image[x:x+dX, y:y+dY]
if np.sum(window)==0:
localMax=0
else:
localMax = np.amax(window)
maxCoord = np.argmax(window)
# zero all but the localMax in the window
window[:] = 0
window.flat[maxCoord] = localMax
return image
A different approach using peak_local_max from skimage.feature.peak and center_of_mass from scipy.ndimage.measurements for localizing the centers of the peaks if a peak may consist of multiple pixels with the same pixel intensity:
from skimage.feature.peak import peak_local_max
from scipy.ndimage.measurements import center_of_mass
from scipy.ndimage import label
from scipy.ndimage.morphology import generate_binary_structure
footprint = generate_binary_structure(3, 3)
peaks = peak_local_max(img, indices=False, footprint=footprint)
lbl, num_features = label(maxima)
centers = center_of_mass(maxima, lbl, range(1, num_features + 1))
values = img[tuple(np.int0(np.transpose(centers)))]

Categories