Okay, so i have these annotation functions
def rotate_bound(image, angle):
# grab the dimensions of the image and then determine the
# center
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# grab the rotation matrix (applying the negative of the
# angle to rotate clockwise), then grab the sine and cosine
# (i.e., the rotation components of the matrix)
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the new bounding dimensions of the image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
return cv2.warpAffine(image, M, (nW, nH), borderValue=(255, 255, 255))
def blur_image(image, radius):
pil_img = Image.fromarray(image)
pil_img = pil_img.filter(ImageFilter.GaussianBlur(radius=radius))
opencv_image = np.array(pil_img)
return opencv_image
def enhance_brightness(image, value):
for x in range(image.shape[0]):
for y in range(image.shape[1]):
if image[x][y][0] < 245 and image[x][y][1] < 245 and image[x][y][2] < 245:
if image[x][y][0] + value <= 255 and image[x][y][0] + value >= 0:
image[x][y][0] += value
if image[x][y][1] + value <= 255 and image[x][y][0] + value >= 0:
image[x][y][1] += value
if image[x][y][2] + value <= 255 and image[x][y][0] + value >= 0:
image[x][y][2] += value
return image
When i try to use them, i get errors i cant fix.
Both functions rotate_bound and enhance_brightness give the same error:
'JpegImageFile' object has no attribute 'shape'.
And the blur function returns another error:
a bytes-like object is required, not 'JpegImageFile'
I would be very glad if someone gives me a hand.
You're importing the image using Pillow. Pillow has no attribute 'shape'. Instead, try
img = cv2.imread("test.jpg")
This should fix all the issues.
Related
I implemented blur image function without using any libraries in Python. Input image is RGB image, output image is blurred RGB image. I tried in some images and found black pixels in the border of blurred images. I couldn'f fix this problem. How could I solve this problem on my code ?
My code was here:
def blur(image, ksize): # blur image function
height, width, channel = image.shape[:] # get height, width, channel in shape of image
array_channel = []
array_height = []
array_width = []
for i in range(channel):
array_channel.append(np.float64(0))
for j in range(width):
array_width.append(array_channel)
for k in range(height):
array_height.append(array_width)
new_image = np.array(array_height)
for i in range(height - ksize + 1): # iterate vertical pixel in height
for j in range(width - ksize + 1): # iterate horizontal pixel in width
for k in range(channel): # iterate channel
for l in range(ksize): # iterate ksize
temp = (float(image[i + l, j + l][k]) * float(image[i - l, j - l][k]) * \
float(image[i, j + l][k]) * float(image[i, j - l][k]) * \
float(image[i + l, j][k]) * float(image[i - l, j][k]) * \
float(image[i + l, j - l][k]) * float(image[i - l, j + l][k])) ** (1/8)
# calculate temp value
new_image[i, j][k] += temp / ksize # calculate pixels in range of ksize
return new_image # return new image
Trying to convert Kitti label format to Yolo. But after converting the bbox is misplaced.
this is kitti bounding box
This is conversion code:
def convertToYoloBBox(bbox, size):
# Yolo uses bounding bbox coordinates and size relative to the image size.
# This is taken from https://pjreddie.com/media/files/voc_label.py .
dw = 1. / size[0]
dh = 1. / size[1]
x = (bbox[0] + bbox[1]) / 2.0
y = (bbox[2] + bbox[3]) / 2.0
w = bbox[1] - bbox[0]
h = bbox[3] - bbox[2]
x = x * dw
w = w * dw
y = y * dh
h = h * dh
return (x, y, w, h)
convert =convertToYoloBBox([kitti_bbox[0],kitti_bbox[1],kitti_bbox[2],kitti_bbox[3]],image.shape[:2])
The function does some normalization which is essential for yolo and outputs following:
(0.14763590391908976,
0.3397063758389261,
0.20452591656131477,
0.01810402684563757)
but when i try to check if the normalization is being done correctly with this code:
x = int(convert[0] * image.shape[0])
y = int(convert[1] * image.shape[1])
width = x+int(convert[2] * image.shape[0])
height = y+ int(convert[3] * image.shape[1])
cv.rectangle(image, (int(x), int(y)), (int(width), int(height)), (255,0,0), 2 )
the bounding box is misplaced:
Any suggestions ? Is conversion fucntion correct? or the problem is in the checking code ?
You got the centroid calculation wrong.
Kitti labels are given in the order of left, top, right, and bottom.
to get the centroid you have to do (left + right)/ 2 and (top + bottom)/2
so your code will become
x = (bbox[0] + bbox[2]) / 2.0
y = (bbox[1] + bbox[3]) / 2.0
w = bbox[2] - bbox[0]
h = bbox[3] - bbox[1]
OpenCV / Python related:
Given a photo of a round object, how can you output that object flattened, while adjusting for surface area? Here is an example image of an input:
Soccer ball
It is similar to adjusting for camera distortion (turning a round object into flat one), but in this case the distortion comes from the object itself and not the camera.
Distorted image:
Undistorted image:
Any suggestions would help. Thank you!
Edit: The package squircle is just what I needed, thank you fmw42!
Here is a solution in Python/OpenCV. It creates transformation maps that define the equations from output back to input and applies them using cv2.remap(). The equations come from https://arxiv.org/pdf/1509.06344.pdf for the Elliptical Grid Mapping approach.
Input:
import numpy as np
import cv2
import math
# References:
# https://arxiv.org/pdf/1509.06344.pdf
# http://squircular.blogspot.com/2015/09/mapping-circle-to-square.html
# Evaluate:
# u = x*sqrt(1-y**2/2)
# v = y*sqrt(1-x**2/2)
# u,v are input circle coordinates and x,y are output square coordinates
# read input
img = cv2.imread("rings.png")
# get dimensions and center
h, w = img.shape[:2]
xcent = w / 2
ycent = h / 2
# set up the maps as float32 from output square (x,y) to input circle (u,v)
map_u = np.zeros((h, w), np.float32)
map_v = np.zeros((h, w), np.float32)
# create u and v maps where x,y is measured from the center and scaled from -1 to 1
for y in range(h):
Y = (y - ycent)/ycent
for x in range(w):
X = (x - xcent)/xcent
map_u[y, x] = xcent * X * math.sqrt(1 - 0.5*Y**2) + xcent
map_v[y, x] = ycent * Y * math.sqrt(1 - 0.5*X**2) + ycent
# do the remap
result = cv2.remap(img, map_u, map_v, cv2.INTER_LINEAR, borderMode = cv2.BORDER_REFLECT_101, borderValue=(0,0,0))
# save results
cv2.imwrite("rings_circle2square.png", result)
# display images
cv2.imshow('img', img)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Result:
Here is another example:
Input:
Result:
And here is a 3rd example:
Input:
Result:
ADDITION
Here is an alternate approach based upon the Simple Stretch equations in the reference above:
import numpy as np
import cv2
import math
# References:
# https://arxiv.org/pdf/1509.06344.pdf
# Simple stretch equations
# read input
img = cv2.imread("rings.png")
#img = cv2.imread("ICM.png")
#img = cv2.imread("soccerball_small.jpg")
# get dimensions and center
h, w = img.shape[:2]
xcent = w / 2
ycent = h / 2
# set up the maps as float32 from output square (x,y) to input circle (u,v)
map_u = np.zeros((h, w), np.float32)
map_v = np.zeros((h, w), np.float32)
# create u and v maps where x,y is measured from the center and scaled from -1 to 1
# note: copysign(1,x) is signum(x) and returns 1 ,0, or -1 depending upon sign of x
for y in range(h):
Y = (y - ycent)/ycent
for x in range(w):
X = (x - xcent)/xcent
X2 = X*X
Y2 = Y*Y
XY = X*Y
R = math.sqrt(X2+Y2)
if R == 0:
map_u[y, x] = xcent
map_v[y, x] = ycent
elif X2 >= Y2:
map_u[y, x] = xcent * math.copysign(1, X) * X2/R + xcent
map_v[y, x] = ycent * math.copysign(1, X) * XY/R + ycent
else:
map_u[y, x] = xcent * math.copysign(1, Y) * XY/R + xcent
map_v[y, x] = ycent * math.copysign(1, Y) * Y2/R + ycent
# do the remap
result = cv2.remap(img, map_u, map_v, cv2.INTER_LINEAR, borderMode = cv2.BORDER_REFLECT_101, borderValue=(0,0,0))
# save results
cv2.imwrite("rings_circle2square2.png", result)
#cv2.imwrite("ICM_circle2square2.png", result)
#cv2.imwrite("soccerball_small_circle2square2.png", result)
# display images
cv2.imshow('img', img)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()
Input:
Result:
Input:
Result:
Input:
Result:
A lot of questions about removing radial (or barrel) distortion, but how would I add it?
Visually, I want to take my input, which is presumed to be image (a), and distort it, to be like image (b):
And ideally I'd like a tunable parameter of some "radius" to control "how much distortion" I get. Based on what I want to do, it looks like I'd just need one parameter to control the 'radius of distortion' or whatever it would be called (correct me if I'm wrong).
How can I achieve this with OpenCV? I figure it must be possible because a lot of people try going the other way for things like this. I'm just not as familiar with the proper math operations and library calls to do it.
Any help much appreciated, cheers.
The program below creates barrel distortion
from wand.image import Image
import numpy as np
import cv2
with Image(filename='Path/to/Img/') as img:
print(img.size)
img.virtual_pixel = 'transparent'
img.distort('barrel', (0.1, 0.0, 0.0, 1.0)) # play around these values to create distortion
img.save(filename='filname.png')
# convert to opencv/numpy array format
img_opencv = np.array(img)
# display result with opencv
cv2.imshow("BARREL", img_opencv)
cv2.waitKey(0)
cv2.destroyAllWindows()
Here is one way to produce barrel or pincushion distortion in Python/OpenCV by creating the X and Y distortion maps and then using cv.remap() to do the warping.
Input:
import numpy as np
import cv2 as cv
import math
img = cv.imread('lena.jpg')
# grab the dimensions of the image
(h, w, _) = img.shape
# set up the x and y maps as float32
map_x = np.zeros((h, w), np.float32)
map_y = np.zeros((h, w), np.float32)
scale_x = 1
scale_y = 1
center_x = w/2
center_y = h/2
radius = w/2
#amount = -0.75 # negative values produce pincushion
amount = 0.75 # positive values produce barrel
# create map with the barrel pincushion distortion formula
for y in range(h):
delta_y = scale_y * (y - center_y)
for x in range(w):
# determine if pixel is within an ellipse
delta_x = scale_x * (x - center_x)
distance = delta_x * delta_x + delta_y * delta_y
if distance >= (radius * radius):
map_x[y, x] = x
map_y[y, x] = y
else:
factor = 1.0
if distance > 0.0:
factor = math.pow(math.sin(math.pi * math.sqrt(distance) / radius / 2), amount)
map_x[y, x] = factor * delta_x / scale_x + center_x
map_y[y, x] = factor * delta_y / scale_y + center_y
# do the remap
dst = cv.remap(img, map_x, map_y, cv.INTER_LINEAR)
# save the result
#cv.imwrite('lena_pincushion.jpg',dst)
cv.imwrite('lena_barrel.jpg',dst)
# show the result
cv.imshow('src', img)
cv.imshow('dst', dst)
cv.waitKey(0)
cv.destroyAllWindows()
Barrel (positive amount):
Pincushion (negative amount):
I am pretty new to Python and want to do the following: I want to divide the following image into 8 pie segments:
I want it to look something like this (I made this in PowerPoint):
The background should be black and the edge of the figure should have an unique color as well as each pie segment.
EDIT: I have written a code that divides the whole image in 8 segments:
from PIL import Image, ImageDraw
im=Image.open('C:/Users/20191881/Documents/OGO Beeldanalyse/Python/asymmetrie/rotation.png')
fill = 255
draw = ImageDraw.Draw(im)
draw.line((0,0) + im.size, fill)
draw.line((0, im.size[1], im.size[0], 0), fill)
draw.line((0.5*im.size[0],0, 0.5*im.size[0], im.size[1]), fill)
draw.line((0, 0.5*im.size[1], im.size[0], 0.5*im.size[1]), fill)
del draw
im.show()
The output gives:
The only thing that is left to do is to find a way to make each black segment inside the border an unique color and also give all the white edge segments an unique color.
Your code divides the image in eight parts, that's correct, but with respect to the image center, you don't get eight "angular equally" pie segments like you show in your sketch.
Here would be my solution, only using Pillow and the math module:
import math
from PIL import Image, ImageDraw
def segment_color(i_color, n_colors):
r = int((192 - 64) / (n_colors - 1) * i_color + 64)
g = int((224 - 128) / (n_colors - 1) * i_color + 128)
b = 255
return (r, g, b)
# Load image; generate ImageDraw
im = Image.open('path_to/vgdrD.png').convert('RGB')
draw = ImageDraw.Draw(im)
# Number of pie segments (must be an even number)
n = 8
# Replace (all-white) edge with defined edge color
edge_color = (255, 128, 0)
pixels = im.load()
for y in range(im.height):
for x in range(im.width):
if pixels[x, y] == (255, 255, 255):
pixels[x, y] = edge_color
# Draw lines with defined line color
line_color = (0, 255, 0)
d = min(im.width, im.height) - 10
center = (int(im.width/2), int(im.height)/2)
for i in range(int(n/2)):
angle = 360 / n * i
x1 = math.cos(angle/180*math.pi) * d/2 + center[0]
y1 = math.sin(angle/180*math.pi) * d/2 + center[1]
x2 = math.cos((180+angle)/180*math.pi) * d/2 + center[0]
y2 = math.sin((180+angle)/180*math.pi) * d/2 + center[1]
draw.line([(x1, y1), (x2, y2)], line_color)
# Fill pie segments with defined segment colors
for i in range(n):
angle = 360 / n * i + 360 / n / 2
x = math.cos(angle/180*math.pi) * 20 + center[0]
y = math.sin(angle/180*math.pi) * 20 + center[1]
ImageDraw.floodfill(im, (x, y), segment_color(i, n))
im.save(str(n) + '_pie.png')
For n = 8 pie segments, the following result is produced:
The first step is to replace all white pixels in the original image with the desired edge color. Of course, the assumption here is, that there are no other (white) pixels in the image. Also, this might be better done using NumPy and vectorized code, but I wanted to keep the solution Pillow-only.
Next step is to draw the (green) lines. Here, I calculate the proper coordinates of the lines' start and end using sin and cos.
The last step is to flood fill the pie segments' area, cf. ImageDraw.floodfill. Therefore, I calculate the seed points the same way as before, but add an angular shift to hit a point exactly within the pie segment.
As you can see, n is variable in my solution (n must be even):
Of course, there are limitations regarding the angular resolution, most due to the small image.
Hope that helps!
EDIT: Here's a modified version to also allow for individually colored edges.
import math
from PIL import Image, ImageDraw
def segment_color(i_color, n_colors):
r = int((192 - 64) / (n_colors - 1) * i_color + 64)
g = int((224 - 128) / (n_colors - 1) * i_color + 128)
b = 255
return (r, g, b)
def edge_color(i_color, n_colors):
r = 255
g = 255 - int((224 - 32) / (n_colors - 1) * i_color + 32)
b = 255 - int((192 - 16) / (n_colors - 1) * i_color + 16)
return (r, g, b)
# Load image; generate ImageDraw
im = Image.open('images/vgdrD.png').convert('RGB')
draw = ImageDraw.Draw(im)
center = (int(im.width/2), int(im.height)/2)
# Number of pie segments (must be an even number)
n = 8
# Replace (all-white) edge with defined edge color
max_len = im.width + im.height
im_pix = im.load()
for i in range(n):
mask = Image.new('L', im.size, 0)
mask_draw = ImageDraw.Draw(mask)
angle = 360 / n * i
x1 = math.cos(angle/180*math.pi) * max_len + center[0]
y1 = math.sin(angle/180*math.pi) * max_len + center[1]
angle = 360 / n * (i+1)
x2 = math.cos(angle/180*math.pi) * max_len + center[0]
y2 = math.sin(angle/180*math.pi) * max_len + center[1]
mask_draw.polygon([center, (x1, y1), (x2, y2)], 255)
mask_pix = mask.load()
for y in range(im.height):
for x in range(im.width):
if (im_pix[x, y] == (255, 255, 255)) & (mask_pix[x, y] == 255):
im_pix[x, y] = edge_color(i, n)
# Draw lines with defined line color
line_color = (0, 255, 0)
d = min(im.width, im.height) - 10
for i in range(int(n/2)):
angle = 360 / n * i
x1 = math.cos(angle/180*math.pi) * d/2 + center[0]
y1 = math.sin(angle/180*math.pi) * d/2 + center[1]
x2 = math.cos((180+angle)/180*math.pi) * d/2 + center[0]
y2 = math.sin((180+angle)/180*math.pi) * d/2 + center[1]
draw.line([(x1, y1), (x2, y2)], line_color)
# Fill pie segments with defined segment colors
for i in range(n):
angle = 360 / n * i + 360 / n / 2
x = math.cos(angle/180*math.pi) * 20 + center[0]
y = math.sin(angle/180*math.pi) * 20 + center[1]
ImageDraw.floodfill(im, (x, y), segment_color(i, n))
im.save(str(n) + '_pie.png')
Binary masks for each pie segment are created, and all white pixels only within that binary mask are replaced with a defined edge color.
Using NumPy still seems favorable, but I was curious to do that in Pillow only.