Plotting image rgb value against function of time - python

Is there a way to compute to only one number in order to represent the rgb value of a pixel in an image? I was trying to visualize my ROI color changes over time.x as my function of time and y as my rgb value. Initially, i average the pixel rgb value that I got. For example [84 90 135] = 103 and plot it as my first point, but I realised this might be wrong representation?[135 90 84] gave the same average value as well but they actually represent different colour? This mean I will get wrong graph.
EDIT : Sorry for the late update was trying to fix my graph. I do not know why but i could not draw the line graph for my data, only works with point marker or round marker
Was trying to track the color data of images when it approaches to white colour like
I was expecting that the value will keep on increasing when it approaches white as decimal code for white is 255 255 255, so the trend should be inclined upwards? But i got the result otherwise, this is the result i got when i plotted b,g,r value of images and it doesnt really show me much info.
. Code is shown below:
import cv2
import numpy as np
import matplotlib.pyplot as plt
path = 'R:\\xx\\'
path1 = 'R:\\xx\\'
def BlueComponent(im_file):
im = cv2.imread(im_file) #return blue value
im1 = im[788, 526]
b = im1[0]
return b
def GreenComponent(im_file):
im = cv2.imread(im_file) #return green value
im1 = im[788, 526]
g = im1[1]
return g
def RedComponent(im_file): #return red value
im = cv2.imread(im_file)
im1 = im[788, 526]
r = im1[2]
return r
myBlueList = []
myGreenList = []
myRedList = []
myList = []
num_images = 99 # number of images
dotPos = 0
for i in range(1770, 1869): # loop to auto-generate image names and run prior function
image_name = path + 'Cropped_Aligned_IMG_' + str(i) + '.png' # for loop runs from image number 1770 to 1868
myBlueList.append(BlueComponent(image_name))
myGreenList.append(GreenComponent(image_name))
myRedList.append(RedComponent(image_name))
myList.append(dotPos)
dotPos = dotPos + 0.5
print(myList)
print(myBlueList)
print(myGreenList)
print(myRedList)
for k in range(1770,1869):
a = 'Cropped_Aligned_IMG_' + str(k)
image_name = path + a + '.png'
img_file = cv2.imread(image_name)
y = [myGreenList]
x = [myList]
y1 = [myBlueList]
y2 = [myRedList]
plt.xticks(np.arange(0.0 ,50.0, 0.5), rotation='vertical' )
plt.plot(x, y, 'g.-')
plt.plot(x, y1, 'b.-')
plt.plot(x, y2, 'r.-')
plt.title('Color Decimal Code Against Time')
plt.xlabel('Time(Hours)', labelpad=10)
plt.ylabel('Colour Code')
plt.show()

If you are only interested in color you can convert your RGB touples to Hue values. If saturation and intensity also matter this is of course not sufficient.
This will of course fail for neutral values.
Please search the web for details.
MIN = min(r,g,b)
MAX = max(r,g,b)
Hue =
0 if MIN == MAX
60° ⋅ (g - b)/(MAX - MIN) if MAX == r
60° ⋅ (2 + (b - r)/(MAX - MIN)) if MAX == g
60° ⋅ (4 + (r - g)/(MAX - MIN)) if MAX == b
If you are only interested in change, but not to which colour you could for example use the distance between RGB touples.
Another option that has already been suggested in the comments is to compose a 3 byte value.
You just cannot fully visualize a 3d change in 1d in an intuitive way.

Related

translate an image after rotation without using library

I try to rotate an image clockwise 45 degree and translate the image -50,-50.
Rotation process works fine:(I refer to this page:How do I rotate an image manually without using cv2.getRotationMatrix2D)
import numpy as np
import math
from scipy import ndimage
from PIL import Image
# inputs
img = ndimage.imread("A.png")
rotation_amount_degree = 45
# convert rotation amount to radian
rotation_amount_rad = rotation_amount_degree * np.pi / 180.0
# get dimension info
height, width, num_channels = img.shape
# create output image, for worst case size (45 degree)
max_len = int(math.sqrt(height*height + width*width))
rotated_image = np.zeros((max_len, max_len, num_channels))
#rotated_image = np.zeros((img.shape))
rotated_height, rotated_width, _ = rotated_image.shape
mid_row = int( (rotated_height+1)/2 )
mid_col = int( (rotated_width+1)/2 )
# for each pixel in output image, find which pixel
#it corresponds to in the input image
for r in range(rotated_height):
for c in range(rotated_width):
# apply rotation matrix, the other way
y = (r-mid_col)*math.cos(rotation_amount_rad) + (c-mid_row)*math.sin(rotation_amount_rad)
x = -(r-mid_col)*math.sin(rotation_amount_rad) + (c-mid_row)*math.cos(rotation_amount_rad)
# add offset
y += mid_col
x += mid_row
# get nearest index
#a better way is linear interpolation
x = round(x)
y = round(y)
#print(r, " ", c, " corresponds to-> " , y, " ", x)
# check if x/y corresponds to a valid pixel in input image
if (x >= 0 and y >= 0 and x < width and y < height):
rotated_image[r][c][:] = img[y][x][:]
# save output image
output_image = Image.fromarray(rotated_image.astype("uint8"))
output_image.save("rotated_image.png")
However, when I try to translate the image. I edited the above code to this:
if (x >= 0 and y >= 0 and x < width and y < height):
rotated_image[r-50][c-50][:] = img[y][x][:]
But I got something like this:
It seems the right and the bottom did not show the right pixel. How could I solve it?
Any suggestions would be highly appreciated.
The translation needs to be handled as a wholly separate step. Trying to translate the value from the source image doesn't account for newly created 0,0,0 (if RGB) valued pixels by the rotation.
Further, simply subtracting 50 from the rotated array index values, without validating them at that stage for positivity, is allowing for a negative valued index, which is fully supported by Python. That is why you are getting a "wrap" effect instead of a translation
You said your script rotated the image as intended, so while perhaps not the most efficient, the most intuitive is to simply shift the values of the image assembled after you rotate. You could test that the values for the new image remain positive after subtracting 50 and only saving the ones >= 0 or being cognizant of the fact that you are shifting the values downward by 50, any number less than 50 will be discarded and you get:
<what you in the block you said was functional then:>
translated_image = np.zeros((max_len, max_len, num_channels))
for i in range(0, rotated_height-50): # range(start, stop[, step])
for j in range(0, rotated_width-50):
translated_image[i+50][j+50][:] = rotated[i][j][:]
# save output image
output_image = Image.fromarray(translated_image.astype("uint8"))
output_image.save("rotated_translated_image.png")

I don't know why the same code works for Julia but not work for Mandelbrot?

I have the following code that generates a Mandelbrot image. The white spaces around the image, which has to be gotten rid.
import numpy as np
import matplotlib.pyplot as plt
from pylab import *
from numpy import NaN
def mandelbrot(C):
z = 0
for n in range(1, 10):
z = z**2 + C
if abs(z) > 2:
return n
return NaN
def plot():
X = np.arange(-2.0, 1.0, 0.05)
Y = np.arange(-1.5, 1.5, 0.05)
pixel = np.zeros((len(Y), len(X)))
for x_iter, x in enumerate(X):
for y_iter, y in enumerate(Y):
pixel[y_iter, x_iter] = mandelbrot(x + 1j * y)
imshow(pixel, cmap = 'gray', extent = (X.min(), X.max(), Y.min(), Y.max()))
return pixel
pixel = mandelbrot(-0.7 + 0.27015j)
plt.axis('off')
plot()
plt.show()
from PIL import Image
min_value = np.nanmin(pixel)
max_value = np.nanmax(pixel)
pixel_int = (255*(pixel-min_value)/(max_value-min_value)).astype(np.uint8)
# sample LUT from matplotlib
lut = (plt.cm.viridis(np.arange(256)) * 255).astype(np.uint8) # CHOOSE COLORMAP HERE viridis, jet, rainbow
pixel_rgb = lut[pixel_int]
# changing NaNs to a chosen color
nan_color = [0,0,0,0] # Transparent NaNs
for i,c in enumerate(nan_color):
pixel_rgb[:,:,i] = np.where(np.isnan(pixel),c,pixel_rgb[:,:,i])
# apply LUT and display
img = Image.fromarray(pixel_rgb, 'RGBA')
print(pixel)
But it turns out IndexError: too many indices for array for the line
pixel_rgb[:,:,i] = np.where(np.isnan(pixel),c,pixel_rgb[:,:,i])
Please, how to fix it?
Actually, in order to get rid of the white spaces around the image the same code (same line) had worked for Julia instead of Mandelbrot a few weeks ago. The following code that generates the Julia image is getting rid of the white spaces around the image.
import numpy as np
import matplotlib.pyplot as plt
def julia(C):
X = np.arange(-1.5, 1.5, 0.05)
Y = np.arange(-1.5, 1.5, 0.05)
pixel = np.zeros((len(Y), len(X)))
for x_iter, x in enumerate(X):
for y_iter, y in enumerate(Y):
z = x + 1j * y
intensity = np.nan
r = np.empty((100, 100)) # Unused at the moment
for n in range(1, 1024):
if abs(z) > 2:
intensity = n
break
z = z**2 + C
pixel[y_iter, x_iter] = intensity
r.fill(intensity) # Unused at the moment
# We return pixel matrix
return pixel
# Compute Julia set image
pixel = julia(-0.7 + 0.27015j)
# Plotting
print(pixel)
plt.show()
from PIL import Image
min_value = np.nanmin(pixel)
max_value = np.nanmax(pixel)
#want to set all the 255 pixels to removed
pixel_int = (255*(pixel-min_value)/(max_value-min_value)).astype(np.uint8)
# sample LUT from matplotlib,If lut is not None it must be an integer giving the number of entries desired in the lookup table
lut = (plt.cm.viridis(np.arange(256)) * 255).astype(np.uint8) # CHOOSE COLORMAP HERE viridis, jet, rainbow
pixel_rgb = lut[pixel_int]
# changing NaNs to a chosen color
nan_color = [0,0,0,0] # Transparent NaNs
for i,c in enumerate(nan_color):
pixel_rgb[:,:,i] = np.where(np.isnan(pixel),c,pixel_rgb[:,:,i])
# apply LUT and display
img = Image.fromarray(pixel_rgb, 'RGBA')
img.save('julia.tiff')
Image.open('julia.tiff').show()
print(min_value, max_value)
Now, I just don't know why this code of getting rid of the white space around the image doesn't work for the Mandelbrot?! Please help me to figure out the problem!
Your direct problem is that in the Julia case, pixel_rgb is a three dimensional array, where in the Mandelbrot case, pixel_rgb is a one dimensional array. So you're trying to apply a three dimensional transform to each of them, and this blows up for the Mandelbrot case, because what you're operating on has only a single dimension, not three.
I don't have more time to completely understand and play with your code, but in the Mandelbrot case, it seems that the mandelbrot() function only returns a single value, where the julia() function returns a 2D array. It is the plot() function that returns a 2D array in the Mandelbrot case. So my quick guess at the change you want to make is to change this:
pixel = mandelbrot(-0.7 + 0.27015j)
plt.axis('off')
plot()
to this:
# pixel = mandelbrot(-0.7 + 0.27015j)
plt.axis('off')
pixel = plot()
This allows the Mandelbrot code to run without crashing. I don't know if it's doing exactly what you want though.

Matplotlib draw vertical lines up to a curve

I am currently using Rectangle in an attempt to fill an area under a curve with a single colour per rectangle. However the Rectanges are > 1 pixel wide. I want to draw lines 1 pixel wide so that they dont overlap. Currently the vertical rectangles under the curve overlap horizontally by 1 or two pixels.
def rect(x,y,w,h,c):
ax = plt.gca()
polygon = plt.Rectangle((x,y),w,h,color=c, antialiased=False)
ax.add_patch(polygon)
def mask_fill(X,Y, fa, cmap='Set1'):
plt.plot(X,Y,lw=0)
plt.xlim([X[0], X[-1]])
plt.ylim([0, MAX])
dx = X[1]-X[0]
for n, (x,y, f) in enumerate(zip(X,Y, fa)):
color = cmap(f)
rect(x,0,dx,y,color)
If I use the code below to draw lines, the overlap is reduced but there is still an overlap
def vlines(x_pos, y1, y2, c):
plt.vlines(x_pos, ymin=y1, ymax=y2, color=c)
def draw_lines(X, Y, trend_len, cmap='Blues_r']):
plt.plot(X, Y, lw=0)
plt.xlim([X[0], X[-1]])
plt.ylim([0, MAX])
dx = X[1] - X[0]
ydeltas = y_trend(Y, trend_len)
for n, (x, y, yd) in enumerate(zip(X, Y, ydeltas)):
color = cmap(y / MAX)
vlines(x, y1=0, y2=y, c=color)
Printing the first 3 iterations of values of parameters into vlines we can see that x_pos is incrementing by 1 - yet the red line clearly overlaps the first blue line as per image below (NB first (LHS) blue line is 1 pixel wide):
x_pos: 0, y1: 0, y2: 143.51, c: (0.7816378316032295, 0.8622683583237216, 0.9389773164167627, 1.0)
x_pos: 1, y1: 0, y2: 112.79092811646952, c: (0.9872049211841599, 0.5313341022683583, 0.405843906189927, 1.0)
x_pos: 2, y1: 0, y2: 123.53185623293905, c: (0.9882352941176471, 0.6059669357939254, 0.4853671664744329, 1.0)
Sample data:
47.8668447889, 1
78.5668447889, 1
65.9768447889, 1
139.658525932, 2
123.749454049, 2
116.660382165, 3
127.771310282, 3
114.792238398, 3
The first column above corresponds the the y value of the series (x values just number of values, counting from 0)
The second column corresponds to the class.
I am generating two images:
One with unique values per class (0-6), each a different colour (7 unique colours), with colour filled up the to the y value this will be used as a mask over data image below.
Second image (example shown) uses different colour maps for different class values (eg 0=Blues_r, 1=Reds_r etc) and the intensity of the colour is given by the value of y.
The code for calculating the colours is fine, but I just cant get matplotlib to plot vertical lines a sigle pixel wide.
Since your goal is not to create an interactive figure, and you are trying to manipulate columns of pixels, you can use numpy instead of matplotlib to generate the result.
Here is a function that will take in y and category arrays, and create an image that's as wide as y is long, with the specified height. Color scaling is done similarly to your solution, where y is divided by the max.
from matplotlib import pyplot as plt
import numpy as np
def draw_lines(y, category, filename, cmap='Set1', max=None, height=None):
y = np.asanyarray(y).ravel()
category = np.asanyarray(category).ravel()
assert y.size == category.size
if max is None:
max = y.max()
if height is None:
height = int(np.ceil(max))
if isinstance(cmap, str):
cmap = plt.get_cmap(cmap)
colors = cmap(category)
colors[:, 3] = y / max
colors = (255 * colors).astype(np.uint8)
output = np.repeat(colors[None, ...], height, axis=0)
heights = np.round(height * (y / max))
mask = np.arange(height)[:, None] >= heights
mask = np.broadcast_to(mask[::-1, :, None], output.shape)
output[mask] = 0
plt.imsave(filename, output)
return output
The first part just sets up the input values. The second part gets the color values. Calling a colormap with an array of n values returns an (n, 4) array of colors in the range [0, 1.0]. colors[:, 3] = y / max sets the alpha channel proportional to the height. The colors are then smeared vertically to the desired height. The last part creates a mask to set the top part of each column to zero, according to the method proposed here.
This version uses transparency to turn off the colors, and to trim the shape. You can do the same thing with a white background, if you are willing to scale the colors instead of adjusting the transparency:
def draw_lines_b(y, category, filename, cmap='Set1', max=None, height=None):
y = np.asanyarray(y).ravel()
category = np.asanyarray(category).ravel()
assert y.size == category.size
if max is None:
max = y.max()
if height is None:
height = int(np.ceil(max))
if isinstance(cmap, str):
cmap = plt.get_cmap(cmap)
colors = cmap(category)
colors[..., :3] *= (y / max)[..., None]
colors = (255 * colors).astype(np.uint8)
output = np.repeat(colors[None, ...], height, axis=0)
heights = np.round(height * (y / max))
mask = np.arange(height)[:, None] >= heights
mask = np.broadcast_to(mask[::-1, :, None], output.shape)
output[mask] = 255
plt.imsave(filename, output)
return output
In both cases, as you can imagine, matplotlib is not strictly necessary. You can define your own list of colors, and use a more appropriate library, such as PIL, to save the images.

Python - Finding the linear equation of a line in a image

I am using Python v2.7 for this work.
As an input i have a relatively white image with a clear black line on it. The line is always linear, no polynomial of second or above order. The line can be anyway on the image
I am trying to define the equation of this line in the form of y = ax +b
Currently my approach would be to find which pixel belongs to the line then do a linear regression to get the equation. But i am trying to find out which function in python i need to use to achieve this and this is where I would need some help
Or maybe you have an even simpler way of doing it.
adding one image as example
Okay so i found the way i wanted to do quite simply in the end
def estimate_coef(x, y):
# number of observations/points
n = np.size(x)
# mean of x and y vector
m_x, m_y = np.mean(x), np.mean(y)
# calculating cross-deviation and deviation about x
SS_xy = np.sum(y*x) - n*m_y*m_x
SS_xx = np.sum(x*x) - n*m_x*m_x
# calculating regression coefficients
a = SS_xy / SS_xx
b = m_y - a*m_x
return(a, b)
# MAIN CODE
# 1. Read image
# 2. find where the pixel belonging to the line are located
# 3. perform linear regression to get coeff
image = [] # contain the image read
# for all images to analyze
for x in range(len(dut.images)):
print "\n\nimage ",x, dut.images[x]
# read image (convert to greyscale)
image = imread(dut.images[x], mode="L")
height = image.shape[0] - 1
threshold = (np.min(image) + np.max(image)) / 2
line = np.where(image < threshold) #get coordinate of the pixel belonging to the line
x = line[1] # store the x position
y = height - line[0] # store the y position. Need to invert because of image origine being on top left corner instead of bottom left
#position = np.array([x,y])
a, b = estimate_coef(x, y)
print("Estimated coefficients:\n \
a = %.6f \n \
b = %.6f" % (a, b))

How to find the maximum value of a numpy array, with location restrictions?

I have a numpy array in python 2.7, which I am using the imshow() function to visualise. The code generating the array looks like:
from pylab import *
r0 = 3.0
S0 = 10.0
x = zeros((101,101))
noiseimg = zeros((101,101))
for i in range(101):
for j in range(101):
noiseimg[i,j] = noiseimg[i,j] + normal(3,1)
mean_i = randint(0,101)
mean_j = randint(0,101)
for i in range(101):
for j in range(101):
r = ((i-mean_i)**2 + (j-mean_j)**2)**0.5
x[i,j] = S0*(1+(r/r0)**2)**-1.5
x[i,j] = x[i,j] + noiseimg[i,j]
if (((i-50)**2 + (j-50)**2)**0.5 >= 40) and (((i-50)**2 + (j-50)**2)**0.5 <= 41):
x[i,j]=0
imshow(x)
show()
What this does is produce an image with a level of background noise, and one circularly symmetric source. There is a circle centred on the image, with a radius of 40 pixels.
What I need to know is how to find the location of the highest value pixel within that circle. I know how to find the maximum value in the circle, but not the [i,j] location of it.
Thank you!
My question has been flagged by stackoverflow as a potential duplicate, but this doesn't contain the location restrictions that I need.
One solution is to "zero" out all the elements surrounding the circle and then simply take the max of the entire array. It appears your radius is 41, centered at (50,50).
Then you could do
import numpy as np
xc, yc = 50, 50
length = 101
radius = 41
y_grid, x_grid = np.ogrid[-xc:length-xc, -yc:length-yc]
mask = x_grid ** 2 + y_grid ** 2 > radius ** 2
And now create your image. Then find the minimum value and set that to every value out side your boundary. If there is a pixel outside the circle that is bigger than the max inside the circle, it is now set to a much smaller value.
x_min = np.min(x)
x[mask] = x_min
So your image will look like
And now just take the max
print np.max(x)
6.4648628255130571
This solution is nice because it avoids loops, which pretty much defeats the purpose of using numpy in the first place.
EDIT:
Sorry you said you wanted the indices of the max. The above solution is the same just unravel the index.
>>> i, j = np.unravel_index(x.argmax(), x.shape)
>>> print "{} {}".format(i, j)
23 32
>>> np.max(x) == x[i,j]
True
circleList = []
indeces = []
for i in len(x[0]):
for j in len(x[1]):
if x[i,j] in circle: #However you check if pixel is inside circle
circleList.append(x[i,j])
indeces.append = ((i,j))
print np.max(circleList) #Here is your max
print indeces(np.argmax(circleList)) #Here are the indeces of the max
should do it.

Categories

Resources