translate an image after rotation without using library - python

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")

Related

How to draw repeated slanted lines

I need to draw slanted lines like this programmatically using opencv-python, and it has to be similar in terms of the slant angle and the distance between the lines:
If using OpenCV cv.line() i need to supply the function with the line's start and endpoint.
Following this StackOverflow accepted answer, I think I will be able to know those two points, but first I need to calculate the line equation itself.
So what I have done is first I calculate the slant angle of the line using the measure tool in ai (The actual image was given by the graphic designer as ai (adobe illustrator) file), and I got 67deg and I solve the gradient of the line. But the problem is I don't know how to get the horizontal spacing/distance between the lines. I needed that so i can supply the start.X. I used the illustrator, and try to measure the distance between the lines but how to map it to opencv coordinate?
Overall is my idea feasible? Or is there a better way to achieve this?
Update 1:
I managed to draw this experimental image:
And this is code:
def show_image_scaled(window_name,image,height,width):
cv2.namedWindow(window_name,cv2.WINDOW_NORMAL)
cv2.resizeWindow(window_name,width,height)
cv2.imshow(window_name,image)
cv2.waitKey(0)
cv2.destroyAllWindows()
def slanted_lines_background():
canvas = np.ones((200,300)) * 255
end_x = 0
start_y = 0
m = 2.35
end_x = 0
for x in range(0,canvas.shape[1],10):
start_x = x
end_y = start_y + compute_length(m,start_x,start_y,end_x)
cv2.line(canvas,(start_x,start_y),(end_x,end_y),(0,0,0),2)
show_image_scaled("Slant",canvas,200,300)
def compute_length(m,start_x,start_y,end_x=0):
c = start_y - (m * start_x)
length_square = (end_x - start_x)**2 + ((m *end_x) + c - start_y) ** 2
length = math.sqrt(length_square)
return int(length)
Still working on to fill the left part of the rectangle
This code "shades" every pixel in a given image to produce your hatched pattern. Don't worry about the math. It's mostly correct. I've checked the edge cases for small and wide lines. The sampling isn't exactly correct but nobody's gonna notice anyway because the imperfection amounts to small fractions of a pixel. And I've used numba to make it fast.
import numpy as np
from numba import njit, prange
#njit(parallel=True)
def hatch(im, angle=45, stride=10, dc=None):
stride = float(stride)
if dc is None:
dc = stride * 0.5
assert 0 <= dc <= stride
stride2 = stride / 2
dc2 = dc / 2
angle = angle / 180 * np.pi
c = np.cos(angle)
s = np.sin(angle)
(height, width) = im.shape[:2]
for y in prange(height):
for x in range(width):
# distance to origin along normal
dist_origin = c*x - s*y
# distance to center of nearest line
dist_center = stride2 - abs((dist_origin % stride) - stride2)
# distance to edge of nearest line
dist_edge = dist_center - dc2
# shade pixel, with antialiasing
# use edge-0.5 to edge+0.5 as "gradient" <=> 1-sized pixel straddles edge
# for thick/thin lines, needs hairline handling
# thin line -> gradient hits far edge of line / pixel may span both edges of line
# thick line -> gradient hits edge of adjacent line / pixel may span adjacent line
if dist_edge > 0.5: # background
val = 0
else: # pixel starts covering line
val = 0.5 - dist_edge
if dc < 1: # thin line, clipped to line width
val = min(val, dc)
elif stride - dc < 1: # thick line, little background
val = max(val, 1 - (stride - dc))
im[y,x] = val
canvas = np.zeros((128, 512), 'f4')
hatch(canvas, angle=-23, stride=5, dc=2.5)
# mind the gamma mapping before imshow

Fastest way to create list of (X,Y) incrementing tuples with step value?

I need a fast way to create a list of tuples representing image pixel coordinates (X, Y).
Where X is from 0 to size and Y is from 0 to size.
A step value of 1 results in X and Y values of (0, 1, 2, 3...) which is too many tuples. Using a step value greater than 1 will reduce processing time. For example, if the step value is 2 the values would be (0, 2, 4, 6...). If the step value is 4 the values would be (0, 4, 8, 12...).
In pure python range command might be used. However, NumPy is installed by default in my Linux distribution. In NumPy the arrange command might be used but I'm having a hard time wrapping my mind around NumPy array syntax.
PS: After a list of tuples is created it will be randomly shuffled and then read in the loop.
Edit 1
Using this answer below:
Instead of the image fading in it's doing some kind of weird wipe left to right. Using the code from the answer with a slight modification:
step = 4
size = self.play_rotated_art.size[0] - step
self.xy_list = [
(x, y)
for x in range(0, size - step, step)
for y in range(0, size - step, step)
]
Bug Update
There was an error in my code, it's working fine now:
The updated code is:
self.step = 4
size = self.play_rotated_art.size[0] - self.step
self.xy_list = [
(x, y)
for x in range(0, size - self.step, self.step)
for y in range(0, size - self.step, self.step)
]
shuffle(self.xy_list)
# Convert numpy array into python list & calculate chunk size
self.current_chunk = 0
self.chunk_size = int(len(self.xy_list) / 100)
# Where we stop copying pixels for current 1% chunck
end = self.current_chunk + self.chunk_size
if end > len(self.xy_list) - 1:
end = len(self.xy_list) - 1
while self.current_chunk < end:
x0, y0 = self.xy_list[self.current_chunk]
x1 = x0 + self.step
y1 = y0 + self.step
box = (x0, y0, x1, y1)
region = self.play_rotated_art.crop(box)
self.fade.paste(region, box)
self.current_chunk += 1
self.play_artfade_count += 1
return self.fade
TL;DR
I already have code with step value 1 but this code is overly complex and inefficient to request a modification. The above generic question would help others more and, still help me, if it were answered.
Existing code with step value 1:
def play_artfade2(self):
''' PILLOW VERSION:
Fade in artwork in 100 chunks leaving loop after chunk and
reentering after Tkinter updates screen and pauses.
'''
if self.play_artfade_count == 100:
# We'have completed a full cycle. Force graphical effects exit
self.play_artfade_count = 0 # Reset art fade count
self.play_rotated_value = -361 # Force Spin Art
return None
# Initialize numpy arrays first time through
if self.play_artfade_count == 0:
# Create black image to fade into
self.fade = Image.new('RGBA', self.play_rotated_art.size, \
color='black')
# Generate a randomly shuffled array of the coordinates
im = np.array(self.play_rotated_art)
X,Y = np.where(im[...,0]>=0)
coords = np.column_stack((X,Y))
np.random.shuffle(coords)
# Convert numpy array into python list & calculate chunk size
self.xy_list = list(coords)
self.current_chunk = 0
self.chunk_size = int(len(self.xy_list) / 100)
# Where we stop copying pixels for current 1% chunck
end = self.current_chunk + self.chunk_size
if end > len(self.xy_list) - 1:
end = len(self.xy_list) - 1
while self.current_chunk < end:
x0, y0 = self.xy_list[self.current_chunk]
x1 = x0 + 1
y1 = y0 + 1
box = (x0, y0, x1, y1)
region = self.play_rotated_art.crop(box)
self.fade.paste(region, box)
self.current_chunk += 1
self.play_artfade_count += 1
return self.fade
Using Pillow's Image.crop() and Image.paste() is overkill for a single pixel but the initial working design was future focused to utilize "super pixels" with box size of 2x2, 3x3, 5x5, etc as image is resized from 200x200 to 333x333 to 512x512, etc.
I need fast way to create a list of tuples representing image pixel coordinates (X, Y).
Where X is from 0 to size and Y is from 0 to size
A list comprehension with range will work:
xsize = 10
ysize = 10
coords = [(x, y) for x in range(xsize) for y in range(ysize)]
# this verifies the shape is correct
assert len(coords) == xsize * ysize
If you wanted a step other than 1, this is setting the step argument:
coords = [(x, y) for x in range(0, xsize, 2) for y in range(0, ysize, 2)]
You can use a generator expression:
size = 16
step = 4
coords = (
(x, y)
for x in range(0, size, step)
for y in range(0, size, step)
)
Then you can iterate on that like you would do with a list
for coord in coords:
print(coord)
Using a generator instead of a list or tuple has the advantage of being more memory efficient.

Returning undistorted pixel coordinates rather than the image

I need to undistort the pixel coordinates of an image -- and need the corrected coordinates returned. I do not want an undistorted image returned-- just the corrected coordinates of the pixels. The camera is calibrated, and I have the camera intrinsic parameters, and the distortion matrix. I am using OpenCV in python 3
I have read up as much of the theory as I can find and questions here. Key info is:
https://docs.opencv.org/2.4/doc/tutorials/calib3d/camera_calibration/camera_calibration.html
This pretty clearly describes the radial distortion and tangential distortion that needs to be considered.
radial:
x_{corrected} = x( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6)
y_{corrected} = y( 1 + k_1 r^2 + k_2 r^4 + k_3 r^6)
Tangential:
x_{corrected} = x + [ 2p_1xy + p_2(r^2+2x^2)]
y_{corrected} = y + [ p_1(r^2+ 2y^2)+ 2p_2xy]
I suspect that I can't simply apply these corrections sequentially. Perhaps there is a function to do what I want to do directly, anyway -- and I'd love to hear about that.
I can't simply use the normal undistort procedure on the image, as I am attempting to apply an IR camera's distortion correction to the depth data from the same camera. If you undistort a depth image like this -- you split pixels across coordinates and the answer makes no sense. Hopefully I am on the right track with this.
The code so far:
import numpy as np
import cv2
imgIR = cv2.imread("/20190529-150017-305-1235-depth.png",0)
#you could try this on any image...
h, w = imgIR.shape[:2]
X = np.array([i for i in range(0,w)]*(h))
X = X.reshape(h, w)
Y = np.array([[i]*(w) for i in range(0,h)])
fx = 483.0 #x focal length
fy = 490.2
CentreX = 361.4 #optical centre of the image - x axis
CentreY = 275.6
#Relative to the optical centre, it is possible to determine the `#coordinates of each pixel in the image`
#then do the above operation without loops using a scalar subtraction
Xref = X - CentreX
Yref = Y - CentreY
#"scaling factor" refers to the relation between depth units and meters;
scalingFactor = 18.0/36.0 # 18pixels / 36 mm;
# I'm not sure what should be yet -- whether [pixels at the shelf]/mm
#or mm/[pixels at the shelf]
Z = imgIR / scalingFactor
#using numpy
Xcoord = np.multiply(Xref,Z/fx)
Ycoord = np.multiply(Yref,Z/fy)
#how to correct these coords for the radial and tangential distortion?
#parameters as returned for the distortion matrix using
cv2.calibrateCamera
dstvec = array([[-0.1225, -0.0159, 0.001616, -0.0018924,-0.00120696]])
What I am looking for is a new matrix of undistorted (radial and tangential distortion removed) X coordinates and a matrix of undistored Y coordinates -- with each matrix element representing one of the original pixels.
Thanks for your help!
I think you are looking for OpenCV's undistortPoints (https://amroamroamro.github.io/mexopencv/matlab/cv.undistortPoints.html).
px_distorted = np.zeros((1, 1, 2), dtype=np.float32)
px_distorted[0][0][0] = x_coordinate
px_distorted[0][0][1] = y_coordinate
px_undistorted = cv2.undistortPoints(px_distorted, intrinsics_mat, dist_coefficients)

Plotting image rgb value against function of time

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.

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