Python Opencv img.item() performance too slow - python

i wrote this little code to compare the pixels greyscale values of two 100x100 jpeg images.
However, the performance is very disappointing (1.5 Seconds for 10.000 comparisons). Is there a way to achieve a better performance?
Here is the code:
import cv2
import numpy as np
import math
import datetime
img1 = cv2.imread('Testbild 2014-08-23 17:27:25.141362.jpeg', cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread('Testbild 2014-08-23 17:27:25.061802.jpeg', cv2.IMREAD_GRAYSCALE)
height, width =img1.shape
cnt = 0
threshold = 10
print("start:" + str(datetime.datetime.now()))
for y in range(0 , height):
for x in range(0 , width):
val1 = img1.item(y,x)
val2 = img2.item(y,x)
diff_abs = math.fabs(int(val1)-int(val2))
if diff_abs > threshold:
cnt += 1
if x == height and y == width:
break
if x == height:
x=0
if y == width:
y=0
print("end:" + str(datetime.datetime.now()))
print("Result: " + str(cnt))
Many thanks for your answers!

The double loop:
for y in range(0 , height):
for x in range(0 , width):
val1 = img1.item(y,x)
val2 = img2.item(y,x)
diff_abs = math.fabs(int(val1)-int(val2))
if diff_abs > threshold:
cnt += 1
if x == height and y == width:
break
if x == height:
x=0
if y == width:
y=0
can be replaced by:
diff_abs = np.abs(img1-img2)
cnt = (diff_abs > threshold).sum()
This takes advantage of NumPy array's ability to do fast element-wise arithmetic.
The condition
x == height and y == width
is never true. If height < width, then y will never equal width (since y is in range(0, height)). If height > width, then x will never equal height. and if height == width, then neither x nor y will ever equal height.
The condition
if x == height:
x=0
does nothing useful, since even if x == height, the assignment to x is lost on the next iteration of the loop.
The same goes for
if y == width:
y=0

Related

Make box-counting code faster using Python

The code below is one of the features on the audio file. Since I segmented one audio file to 4096 samples so I must call boxcounting function 4096 times to get the output for each segmented file. This code I wrote for one segmented file and called from the main python file. It takes around 10 seconds/segmented file if full audio is short and takes 30 seconds/segmented file if full audio is around 3-4 minutes. My problem is it takes a very long time to run one audio file.
get an array of from audio and separate them to 2 mono array (Left channel and Right channel)
normalize values and multiply array with 20 for scaling up
round numbers to one decimal place
pair them (L, R) by using zip()
remove the duplicate value
count coordinate pair in each small box
count boxes that have value (Final output)
This is my example
import numpy as np
from pydub import AudioSegment
from collections import OrderedDict
def difference(a, b):
if (a > 0) and (b > 0):
return (abs(a - b))
elif (a > 0) and (b < 0):
return abs(a + abs(b))
elif (a < 0) and (b < 0):
return (abs(a) - abs(b))
def boxcounting(left_channel, right_channel, scale):
ratioX = difference(max(left_channel), min(left_channel))/scale
ratioY = difference(max(right_channel), min(right_channel))/scale
startX = min(left_channel)
count_per_scale = []
countbox = 0
pair = list(OrderedDict.fromkeys(list(zip(left_channel, right_channel))))
for x in range(scale):
print('startX',startX)
startY = min(right_channel)
endX = startX + ratioX
if x == (scale-1):
endX = max(left_channel)
print('endX',endX)
for y in range(scale):
print('-----------------------')
print('startY',startY)
endY = startY + ratioY
if y == (scale-1):
endY = max(right_channel)
print('endY',endY)
count = 0 # reset
for l,r in pair:
if (startX < l <= endX):
if (startY < r <= endY):
count+=1
print('0',l,r)
print('count',count)
elif (min(right_channel) == r and r == startY):
count+=1
print('1',l,r)
print('count',count)
elif (min(left_channel) == l and l == startX):
if (startY < r <= endY):
count+=1
print('2',l,r)
print('count',count)
elif (min(right_channel) == r and r == startY):
count+=1
print('3',l,r)
print('count',count)
count_per_scale.append(count)
if count != 0:
countbox += 1
startY = endY
startX = endX
print('===============================')
print(count_per_scale)
countbox = 0
for i in count_per_scale:
if(i > 0):
countbox += 1
countbox = np.count_nonzero(count_per_scale)
print('No. of box that has value =', countbox)
return countbox
sound = AudioSegment.from_file('Alpharock - Pump This Party.mp3')
split_sound = sound.split_to_mono()
left_channel = np.array(split_sound[0].get_array_of_samples())
right_channel = np.array(split_sound[1].get_array_of_samples())
scale = 10 #norm and scale up
scaleupL = np.round((left_channel/np.abs(left_channel).max())* scale,1)
scaleupR = np.round((right_channel/np.abs(right_channel).max())* scale,1)
Can anyone help me to make it faster? Thank you very much.

Script freezes after completing a 'while' loop in a 'while' loop (oops)

How can get the RGB values of every pixel in an image and after it gets all the values of the first row?
Script:
image = input("image:")
im = Image.open(image)
pix = im.load()
width, height = im.size
x = 0
y = 0
# For each pixel in the Y
while (y < height):
# For each pixel in the X
while (x < width):
print pix[x,y]
x = x + 1
y = y + 1
The way you initialize your x and y values is the problem. X should be initialized back to zero immediately before the second while loop, so that the count starts again for the width of the next row.
Something like:
x = 0
y = 0
#for each pixel in the Y
while (y < height):
# for each pixel in the X
x = 0 #start counting again for the next row
while (x < width):
print pix[x,y]
x = x + 1
y = y + 1
Your loop freezes, because at the end of the first row, x=width and you forget to reset it back to zero for the second iteration of the first while loop.

python - iterating through a code and implementing it into shapes on tkinter

def draw_pattern(a_canvas, size, pattern_str_list):
x0 = size
x1 = x0 + size
y0 = size
y1 = y0 + size
for word in pattern_str_list:
for char in word:
if char == "A":
a_canvas.create_oval(x0,y0,x1,y1,fill = "gray")
x0 = x0 + size
x1 = x0 + size
if char == "B":
a_canvas.create_oval(x0,y0,x1,y1,fill = "red")
x0 = x0 + size
x1 = x0 + size
if char == "C":
a_canvas.create_oval(x0,y0,x1,y1,fill = "blue")
x0 = x0 + size
x1 = x0 + size
if char == "D":
a_canvas.create_oval(x0,y0,x1,y1,fill = "green")
x0 = x0 + size
x1 = x0 + size
if char == "_":
a_canvas.create_oval(x0,y0,x1,y1,fill = "black")
x0 = x0 + size
x1 = x0 + size
y0 += size
Hi there, I am trying to build a program that takes a list of strings, read and takes the data from it and displays shapes in rows next to each other through the tkinter widget depending on what is in the list. Let's say the a_canvas is a large square grid, size = 50 and pattern_str_list = (ABC, D_A, BB__A_D). How can I iterate through the list and display the correct colored circle and then move onto the row below for the next item in the list?
Basically so it looks like:
gray red blue
green black gray
red red black gray green
in colored circles on tkinter?
Try this on for size:
def draw_pattern(a_canvas, size, pattern_str_list):
colours = {"A":"gray", "B":"red", "C":"blue", "D":"green"}
x = size
y = size
for word in pattern_str_list:
for char in word:
a_canvas.create_oval(x, y , x + size, y + size, fill=colours.get(char, "black"))
x += size
y += size

Python 3.4.3 Diamond-Square Algorithm is producing odd results

I am currently stumped by an artefact in my code. It appears to produce very sharp points in a grid pattern that have a noticeable difference in value to their neighbours.
I am following the blog post at http://www.bluh.org/code-the-diamond-square-algorithm/ and converting from whichever language they are using (assuming either C# or Java), and have double-checked that what I am doing should match.
Is there any chance that someone could have a browse over this, and see what I'm doing wrong? I've stepped through it at smaller levels, and stopped it on specific iterations of the algorithm (by unrolling the top loop, and explicitly calling the algorithm a set number of times) and everything seems to work until we get to the very last set of points/pixels.
I use a class (called Matrix) to access the list, and wrap any out of bounds values.
The code for the algorithm is as follows:
class World :
def genWorld (self, numcells, cellsize, seed):
random.seed(seed)
self.dims = numcells*cellsize
self.seed = seed
self.cells = Matrix(self.dims, self.dims)
# set the cells at cellsize intervals
half = cellsize/2
for y in range(0, self.dims, cellsize):
for x in range(0, self.dims, cellsize):
self.cells[x,y] = random.random()
scale = 1.0
samplesize = cellsize
while samplesize > 1:
self._diamondSquare(samplesize, scale)
scale *= 0.8
samplesize = int(samplesize/2)
# I need to sort out the problem with the diamond-square algo that causes it to make the weird gridding pattern
def _sampleSquare(self, x, y, size, value):
half = size/2
a = self.cells[x-half, y-half]
b = self.cells[x+half, y-half]
c = self.cells[x-half, y+half]
d = self.cells[x+half, y+half]
res = min(((a+b+c+d+value)/5.0), 1.0)
self.cells[x, y] = res
def _sampleDiamond(self, x, y, size, value):
half = size/2
a = self.cells[x+half, y]
b = self.cells[x-half, y]
c = self.cells[x, y+half]
d = self.cells[x, y-half]
res = min(((a+b+c+d+value)/5.0), 1.0)
self.cells[x, y] = res
def _diamondSquare(self, stepsize, scale):
half = int(stepsize/2)
for y in range(half, self.dims+half, stepsize):
for x in range(half, self.dims+half, stepsize):
self._sampleSquare(x, y, stepsize, random.random()*scale)
for y in range(0, self.dims, stepsize):
for x in range(0, self.dims, stepsize):
self._sampleDiamond(x+half, y, stepsize, random.random()*scale)
self._sampleDiamond(x, y+half, stepsize, random.random()*scale)
and is called with:
w = World()
w.genWorld(16, 16, 1) # a 256x256 square world, since the numcells is multiplied by the cellsize to give us the length of ONE side of the resulting grid
then I save to file to check the result:
file = io.open("sample.raw",'wb')
arr = [int(i * 255) for i in w.cells.cells] # w.cells.cells should not have a value >= 1.0, so what's going on?
ind = 0
for a in arr:
if a > 255:
print ("arr["+str(ind)+"] ::= "+str(a))
ind += 1
file.write(bytearray(arr))
file.close()
which gives the result:
EDIT: Okay, so it appears that I managed to get it working. I swapped from using functions for working out the diamond and square steps to doing it all in the _diamondSquare() function, but this wasn't the only thing. I also found out that random.random() provides values in the range [0.0 ->1.0), when I was expecting values in the range [-1.0 -> 1.0). After I corrected this, everything started working properly, which was a relief.
Thanks for the advice everyone, here's the working code in case anyone else is struggling with something similar:
Random Function
# since random.random() gives a value in the range [0.0 -> 1.0), I need to change it to [-1.0 -> 1.0)
def rand():
mag = random.random()
sign = random.random()
if sign >=0.5:
return mag
return mag * -1.0
Matrix class
class Matrix:
def __init__(self, width, height):
self.cells = [0 for i in range(width*height)]
self.width = width
self.height = height
self.max_elems = width*height
def _getsingleindex(self, ind):
if ind < 0:
ind *= -1
while ind >= self.max_elems:
ind -= self.max_elems
return ind
def _getmultiindex(self, xind, yind):
if xind < 0:
xind *= -1
if yind < 0:
yind *= -1
while xind >= self.width:
xind -= self.width
while yind >= self.height:
yind -= self.height
return xind + (yind*self.height)
def __getitem__(self, inds):
# test that index is an integer, or two integers, and throw an indexException if not
if hasattr(inds, "__len__"):
if len(inds) > 1:
return self.cells[self._getmultiindex(int(inds[0]), int(inds[1]))]
return self.cells[self._getsingleindex(int(inds))]
def __setitem__(self, inds, object):
# test that index is an integer, or two integers, and throw an indexException if not
if hasattr(inds, "__len__"):
if len(inds) > 1:
self.cells[self._getmultiindex(int(inds[0]),int(inds[1]))] = object
return self.cells[self._getmultiindex(int(inds[0]),int(inds[1]))]
self.cells[self._getsingleindex(int(inds))] = object
return self.cells[self._getsingleindex(int(inds))]
def __len__(self):
return len(self.cells)
The Actual Diamond-Square Generation
# performs the actual 2D generation
class World:
def genWorld (self, numcells, cellsize, seed, scale = 1.0):
random.seed(seed)
self.dims = numcells*cellsize
self.seed = seed
self.cells = Matrix(self.dims, self.dims)
mountains = Matrix(self.dims, self.dims)
# set the cells at cellsize intervals
for y in range(0, self.dims, cellsize):
for x in range(0, self.dims, cellsize):
# this is the default, sets the heights randomly
self.cells[x,y] = random.random()
while cellsize > 1:
self._diamondSquare(cellsize, scale)
scale *= 0.5
cellsize = int(cellsize/2)
for i in range(len(mountains)):
self.cells[i] = self.cells[i]*0.4 + (mountains[i]*mountains[i])*0.6
def _diamondSquare(self, stepsize, scale):
half = int(stepsize/2)
# diamond part
for y in range(half, self.dims+half, stepsize):
for x in range(half, self.dims+half, stepsize):
self.cells[x, y] = ((self.cells[x-half, y-half] + self.cells[x+half, y-half] + self.cells[x-half, y+half] + self.cells[x+half, y+half])/4.0) + (rand()*scale)
# square part
for y in range(0, self.dims, stepsize):
for x in range(0, self.dims, stepsize):
self.cells[x+half,y] = ((self.cells[x+half+half, y] + self.cells[x+half-half, y] + self.cells[x+half, y+half] + self.cells[x+half, y-half])/4.0)+(rand()*scale)
self.cells[x,y+half] = ((self.cells[x+half, y+half] + self.cells[x-half, y+half] + self.cells[x, y+half+half] + self.cells[x, y+half-half])/4.0)+(rand()*scale)
Main Function (added for completeness)
# a simple main function that uses World to create a 2D array of diamond-square values, then writes it to a file
def main():
w = World()
w.genWorld(20, 16, 1)
mi = min(w.cells.cells)
ma = max(w.cells.cells) - mi
# save the resulting matrix to an image file
file = io.open("sample.raw",'wb')
maxed = [(i-mi)/ma for i in w.cells.cells]
arr = [int(i * 255) for i in maxed]
file.write(bytearray(arr))
file.close()

Python average color over square region

I'm using four inputs for my function: a picture object, x coordinate, y coordinate, and the height/width of the square. I want to separately average all the red, green, and blue values of the picture. I'm having trouble with the accumulator variables of the RGB pixels and checking the bounds of the square. Can anyone help me out?
from imageTools import *
p1 = makePicture("flower.jpg")
def averageColor(pic, xCord, yCord, width):
rAcc = 0
gAcc = 0
bAcc = 0
for x in range(xCord, xCord + width):
for y in range(yCord, yCord + width):
picWidth = getWidth(pic)
picHeight = getHeight(pic)
if x <= picWidth and y <= picHeight:
pixel = getPixel(pic, xCord, yCord)
r = getRed(pixel)
g = getGreen(pixel)
b = getBlue(pixel)
rAcc = rAcc + 1
gAcc = gAcc + 1
bAcc = bAcc + 1
avgRed = r / rAcc
avgGreen = g / gAcc
avgBlue = b / bAcc
newColor = makeColor(avgRed, avgGreen, avgBlue)
return newColor
col1 = averageColor(p1, 0, 150, 100)
print(col1)
You want to use < and not <= when comparing indexes because indexes are zero based. For instance if the picture is 10 pixels wide 9 is the index to the last pixel
Also you are overwriting your accumulation variables r, g and b. You need to do
r = r + getRed(...)
oh. Also it looks like you are trying to use xCoord and yCoord to get the pixel rather than your x and y variables

Categories

Resources