Drawing spiral square with python opencv - python

I'm trying to draw a spiral square in python using OpenCV and Numpy.
I know that I can do it via turtle and there are so many examples on the internet but I need to do it as I described in the title.
So I drew chessboard chessboard via python OpenCV
This is code for it
import cv2
import numpy as np
mySize = 256
myOffset = 16
mySquare = 32
myNumberY = mySize // mySquare
myNumberX = mySize // mySquare
myColor = 255
img = np.ones((mySize + 2 * myOffset, mySize + 2 * myOffset), dtype = np.uint8) * 127
for y in range(myNumberY) :
for x in range(myNumberX) :
myColor = 0 if (x + y) % 2 == 0 else 255
print(y, x, myColor)
for ix in range(mySquare) :
for iy in range(mySquare) :
img[myOffset + y * mySquare + iy][myOffset + x * mySquare + ix] = myColor
cv2.imshow('my image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
Next, I have some kind of squares - one inside the other.
And this is code for it.
import cv2, numpy
mySize, myOffset, mySquare, myColor = 256, 16, 16, 0
img = numpy.ones((mySize + 2 * myOffset, mySize + 2 * myOffset), dtype = numpy.uint8) * 127
for item in range(mySize // mySquare // 2) :
myTempOffsetStart = myOffset + item * mySquare
myTempOffsetFinish = myOffset + mySize - item * mySquare
myColor = 0 if myColor == 255 else 255
img[myTempOffsetStart : myTempOffsetFinish, myTempOffsetStart : myTempOffsetFinish] = myColor
cv2.imshow('my image', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
You can change the size of drawing pictures (lines) by changing mySquare value.
So for me important is to draw square spirals like this in the following picture.

If you look at the picture you see that the pattern is actually pretty simple. You have to go right, up, left, down and repeat a few times. Also the distance you have to go starts with 3 and goes up by two every other step.
After having down that I calculate how much space I need to draw that and shift the points so the coordinates are not negative. Here is my code:
import cv2
import matplotlib.pyplot as plt
import itertools
import numpy as np
repeates = 10
directions = [(1,0),(0,1),(-1,0),(0,-1)]
moves = [(3+2*(i//2))*np.array(d)
for i,d in enumerate(itertools.chain(*itertools.repeat(directions, repeates)))]
points = (np.array([0,0]),*itertools.accumulate(moves))
coordinates = np.array(points).reshape(-1)
r1,r2 = coordinates.min(), coordinates.max()
n = r2-r1+1
img = np.zeros((n,n))
for p,q in zip(points[0:-1],points[1:]):
cv2.line(img, tuple(p-r1), tuple(q-r1), (1,1,1))
plt.imshow(img, cmap='gray')
And it looks like this

Related

how can i find a specific sized rectangle in a 2d array?

I'm trying to find x and y coordinates of 67x45 sized rectangle (one-pixel wide) in the background image using Numpy.
The background is variable and the color of rectangle may differ.
The rectangle's height sometimes will only have the half size. (67x22) but without bottom line of rectangle (check rectangle in template2).
I want to use Numpy for my intention but I'm having a hard time doing it.
Template:
Rectangle in template:
Rectangle in template2:
Background image:
I tried cv2.matchtemplate but it didn't quite work well. Maybe because color of rectangles border is variable? Not sure.
Below code is by #claudio. This code actually works but it is slow for my intention. And it also doesn't detect half cut rectangle.
import cv2
import numpy as np
img_filename = "background.png"
ibgr = cv2.imread(img_filename, cv2.IMREAD_UNCHANGED)
assert ibgr.shape[2] == 3
sizeY, sizeX, _ = ibgr.shape
# Convert uint8 BGR tuples to uint32 value:
iint32 = np.dstack((ibgr, np.zeros(ibgr.shape[:2], "uint8"))).view("uint32").squeeze(-1)
img_filename = "template.png"
grey_t = cv2.imread(img_filename, cv2.IMREAD_GRAYSCALE)
t_sizeY, t_sizeX = grey_t.shape
rectAt = []
for x in range(sizeX - t_sizeX):
for y in range(sizeY - t_sizeY):
if (
(iint32[y, x] == iint32[y + 1 : y + t_sizeY, x]).all()
and (iint32[y, x] == iint32[y, x + 1 : x + t_sizeX]).all()
and (iint32[y, x] == iint32[y + 1 : y + t_sizeY, x + t_sizeX - 1]).all()
and (iint32[y, x] == iint32[y + t_sizeY - 1, x + 1 : x + t_sizeX]).all()
and (iint32[y, x] != iint32[y + 1, x + 1])
and True
):
rectAt.append((x, y))
print(rectAt)
for x, y in rectAt:
cv2.rectangle(ibgr, (x - 1, y - 1), (x + t_sizeX, y + t_sizeY), (0, 255, 255), 1)
cv2.imshow("ibgr", ibgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

Why is my code only working on part of my image?

I created code to equalize the luminosity values of pixels in an image so that when the image is further edited I do not have dark or light spots in my final image. However, the code seems to stop short and only equalize part of my image. Any ideas as to why the code is stopping early?
Here is my code:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
img = mpimg.imread('EXP_0159-2_8b.tif')
imgOut = img.copy()
for i in range(0, len(img[0, :])):
imgLine1 = (img[:, i] < 165) * img[:, i]
p = imgLine1.nonzero()
if len(p[0]) < 1:
imgOut[:, i] == 0
else:
imgLine2 = imgLine1[p[0]]
def curvefitting(lineFunction):
x = np.arange(0, len(lineFunction))
y = lineFunction
curve = np.polyfit(x, y, deg = 2)
a = curve[0]
b = curve[1]
c = curve[2]
curveEquation = (a*(x**2)) + (b*(x**1)) + (c)
curveCorrected = lineFunction - curveEquation + 200
return curveCorrected
imgLine1[p[0]] = curvefitting(imgLine2)
imgOut[:, i] = imgLine1
plt.imshow(imgOut, cmap = 'gray')
The for loop takes the individual columns of pixels in my image and restricts the endpoints of that column to (0, 165), so that pixels outside of that range are turned into zero and ignored by the nonzero() function. The if condition just finalizes the conversion of values outside (0, 165) to zero. Additionally, I converted the image to gray so I would not have to deal with colors and could focus only on luminosity.
This is my corrected image. The program works to average the luminosity values across the entire surface. However, you can see that it stops before reaching the end. The initial image was darker on the sides and lighter in the middle, but the file is too large to upload.
Any help is greatly appreciated.
If you are not interested in color you can convert input image to grayscale. That would simplified the matrix multiplications. The simplified version would be
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
def rgb2gray(rgb):
return np.dot(rgb[...,:3], [0.2989, 0.5870, 0.1140])
def curvefitting(lineFunction):
x = np.arange(0, len(lineFunction))
y = lineFunction
curve = np.polyfit(x, y, deg = 2)
a = curve[0]
b = curve[1]
c = curve[2]
curveEquation = [(a*(x_**2)) + (b*(x_**1)) + (c) for x_ in x]
curveCorrected = lineFunction - curveEquation + 200
return curveCorrected
img = mpimg.imread('EXP_0159-2_8b.tif')
img = rgb2gray(img)
imgOut = img.copy()
for i in range(0, len(img[0, :])):
imgLine1 = (img[:, i] < 165) * img[:, i]
p = imgLine1.nonzero()
if len(p) < 1:
imgOut[:, i] == 0
else:
imgLine2 = imgLine1[p]
imgLine1[p] = curvefitting(imgLine2)
imgOut[:, i] = imgLine1
plt.imshow(imgOut, cmap = 'gray')
plt.show()

Manipulate RGB values in image

I would like to apply a simple algebraic operation to the RBG values of an image, that I have loaded via PIL. My current version works, but is slow:
from PIL import Image
import numpy as np
file_name = '1'
im = Image.open('data/' + file_name + '.jpg').convert('RGB')
pixels = np.array(im)
s = pixels.shape
p = pixels.reshape((s[0] * s[1], s[2]))
def update(ratio=0.5):
p2 = np.array([[min(rgb[0] + rgb[0] * ratio, 1), max(rgb[1] - rgb[1] * ratio, 0), rgb[2]] for rgb in p])
img = Image.fromarray(np.uint8(p2.reshape(s)))
img.save('result/' + file_name + '_test.png')
return 0
update(0.5)
Has someone a more efficient idea?
Make use of NumPy's vectorized operations to get rid of the loop.
I modified your original approach to compare performance between the following, different solutions. Also, I added a PIL only approach using ImageMath, if you want to get rid of NumPy completely.
Furthermore, I assume, there is/was a bug:
p2 = np.array([[min(rgb[0] + rgb[0] * ratio, 1), max(rgb[1] - rgb[1] * ratio, 0), rgb[2]] for rgb in p])
You actually do NOT convert to float, so it should be 255 instead of 1 in the min call.
Here's, what I've done:
import numpy as np
from PIL import Image, ImageMath
import time
# Modified, original implementation; fixed most likely wrong compare value in min (255 instead of 1)
def update_1(ratio=0.5):
pixels = np.array(im)
s = pixels.shape
p = pixels.reshape((s[0] * s[1], s[2]))
p2 = np.array([[min(rgb[0] + rgb[0] * ratio, 255), max(rgb[1] - rgb[1] * ratio, 0), rgb[2]] for rgb in p])
img = Image.fromarray(np.uint8(p2.reshape(s)))
img.save('result_update_1.png')
return 0
# More efficient vectorized approach using NumPy
def update_2(ratio=0.5):
pixels = np.array(im)
pixels[:, :, 0] = np.minimum(pixels[:, :, 0] * (1 + ratio), 255)
pixels[:, :, 1] = np.maximum(pixels[:, :, 1] * (1 - ratio), 0)
img = Image.fromarray(pixels)
img.save('result_update_2.png')
return 0
# More efficient approach only using PIL
def update_3(ratio=0.5):
(r, g, b) = im.split()
r = ImageMath.eval('min(float(r) / 255 * (1 + ratio), 1) * 255', r=r, ratio=ratio).convert('L')
g = ImageMath.eval('max(float(g) / 255 * (1 - ratio), 0) * 255', g=g, ratio=ratio).convert('L')
Image.merge('RGB', (r, g, b)).save('result_update_3.png')
return 0
im = Image.open('path/to/your/image.png')
t1 = time.perf_counter()
update_1(0.5)
print(time.perf_counter() - t1)
t1 = time.perf_counter()
update_2(0.5)
print(time.perf_counter() - t1)
t1 = time.perf_counter()
update_3(0.5)
print(time.perf_counter() - t1)
The performance on a [400, 400] RGB image on my machine:
1.723889293 s # your approach
0.055316339 s # vectorized NumPy approach
0.062502050 s # PIL only approach
Hope that helps!

Getting a incorrect picture output when running my self-made rotation algorithm

In order to better understand how image manipulation works, I've decided to create my own image rotation algorithm rather than using cv2.rotate() However, I'm encountering a weird picture cropping and pixel misplacement issue.
I think it may have something to do with my padding, but there may be other errors
import cv2
import math
import numpy as np
# Load & Show original image
img = cv2.imread('Lena.png', 0)
cv2.imshow('Original', img)
# Variable declarations
h = img.shape[0] # Also known as rows
w = img.shape[1] # Also known as columns
cX = h / 2 #Image Center X
cY = w / 2 #Image Center Y
theta = math.radians(100) #Change to adjust rotation angle
imgArray = np.array((img))
imgArray = np.pad(imgArray,pad_width=((100,100),(100,100)),mode='constant',constant_values=0)
#Add padding in an attempt to prevent image cropping
# loop pixel by pixel in image
for x in range(h + 1):
for y in range(w + 1):
try:
TX = int((x-cX)*math.cos(theta)+(y-cY)*math.sin(theta)+cX) #Rotation formula
TY = int(-(x-cX)*math.sin(theta)+(y-cY)*math.cos(theta)+cY) #Rotation formula
imgArray[x,y] = img[TX,TY]
except IndexError as error:
print(error)
cv2.imshow('Rotated', imgArray)
cv2.waitKey(0)
Edit:
I think the misplaced image position may have something to do with lack of proper origin point, however I cannot seem to find a functioning solution to that problem.
Though I didn't dive in the math part of the domain, but based on the given information I think the matrix rotating formula should work like this:
UPDATE:
As I promised I dived a bit into the domain and got to the solution you can see as follows. The main trick that I've swapped the source and destination indices in the looping too, so the rounding doesn't mean any problem ever:
import cv2
import math
import numpy as np
# Load & Show original image
img = cv2.imread('/home/george/Downloads/lena.png', 0)
cv2.imshow('Original', img)
# Variable declarations
h = img.shape[0] # Also known as rows
w = img.shape[1] # Also known as columns
p = 120
h += 2 * p
w += 2 * p
cX = h / 2 #Image Center X
cY = h / 2 #Image Center Y
theta = math.radians(45) #Change to adjust rotation angle
imgArray = np.zeros_like((img))
#Add padding in an attempt to prevent image cropping
imgArray = np.pad(imgArray, pad_width=p, mode='constant', constant_values=0)
img = np.pad(img, pad_width=p, mode='constant', constant_values=0)
# loop pixel by pixel in image
for TX in range(h + 1):
for TY in range(w + 1):
try:
x = int( +(TX - cX) * math.cos(theta) + (TY - cY) * math.sin(theta) + cX) #Rotation formula
y = int( -(TX - cX) * math.sin(theta) + (TY - cY) * math.cos(theta) + cY) #Rotation formula
imgArray[TX, TY] = img[x, y]
except IndexError as error:
pass
# print(error)
cv2.imshow('Rotated', imgArray)
cv2.waitKey(0)
exit()
Note: See usr2564301 comment too, if you want to dive deeper in the domain.

merge two images with alpha channel

I have two images, one with and other without alpha channel. Thus, image A and B has a shape of (x,y,4) and (x,y,3) respectively.
I want to merge both images in a single tensor using python, where B is the background and A is the upper image. The final image must have a shape of (x, y, 3). I tried if scikit-image or cv2 is capable of doing this, but I couldn't found any solution.
here is alpha blending in python
import numpy as np
import cv2
alpha = 0.4
img1 = cv2.imread('Desert.jpg')
img2 = cv2.imread('Penguins.jpg')
#r,c,z = img1.shape
out_img = np.zeros(img1.shape,dtype=img1.dtype)
out_img[:,:,:] = (alpha * img1[:,:,:]) + ((1-alpha) * img2[:,:,:])
'''
# if want to loop over the whole image
for y in range(r):
for x in range(c):
out_img[y,x,0] = (alpha * img1[y,x,0]) + ((1-alpha) * img2[y,x,0])
out_img[y,x,1] = (alpha * img1[y,x,1]) + ((1-alpha) * img2[y,x,1])
out_img[y,x,2] = (alpha * img1[y,x,2]) + ((1-alpha) * img2[y,x,2])
'''
cv2.imshow('Output',out_img)
cv2.waitKey(0)
The above solution works, however I have a more efficient one:
alpha = A[:,:,3]
A1 = A[:,:,:3]
C = np.multiply(A1, alpha.reshape(x,y,1)) + np.multiply(B, 1-alpha.reshape(x,y,1))

Categories