Looping program causes index # is out of bounds for axis # - python-3.x

I'm pretty new to python and Opencv, but I have a few pieces from cv2 and random in mind for a simple test program to make sure I understood how these libraries worked.
I'm trying to create a program that effectively generates colored "snow", similar to what an old fashioned television shows when it has no signal.
Basically I generate a random color with random.randint(-1,256) to get a value between 0 and 255. I do it three times and store each in a different variable, randB/G/R. Then I do it twice more for coordinates randX/Y, using img.shape to get variables for width and height for the max number.
I don't think my variables are being interpreted as strings. If I quickly break the loop and print my variables, no errors are shown. If I remove the randX and randY variables and specify fixed coordinates or a range of [X1:Y1, X2:Y2] it doesn't crash.
import cv2
import numpy as np
import random
img = cv2.imread('jake_twitch.png', cv2.IMREAD_COLOR)
height, width, channels = img.shape
while True:
randB = (random.randint(-1,256))
randG = (random.randint(-1,256))
randR = (random.randint(0,256))
randX = (random.randint(0,width))
randY = (random.randint(0,height))
img[randX,randY] = [randB,randG,randR]
cv2.imshow('Snow', img)
if cv2.waitKey(1) & 0xFF == ord('q'):
break
cv2.imwrite('Snow.png', img)
cv2.destroyAllWindows
I would expect my code to run indefinitely coloring pixels random colors within a specified "box" defined by the width and height variables from img.shape.
it seems to start doing that, but If the program runs for more than about a second it crashes and spits out this error
"IndexError: index 702 is out of bounds for axis 1 with size 702"

Your image is width and height pixels wide - but the corresponding indexes run from 0..width-1 and 0..height-1
The randint function returns inclusive limits - so
random.randint(0,width)
might give you width ... which is 1 too big:
random.randint(a, b)
Return a random integer N such that a <= N <= b. Alias for randrange(a, b+1).
Use
randX = (random.randint(0,width-1))
randY = (random.randint(0,height-1))
instead.
Or change it to use random.randrange(0, width) or random.choice(range(width)) - both omit the upper limit value.

Related

Change the mask for few numpy arrays

So, I have on the input few masked arrays. For computation I use slices: top left, top right, bottom left and bottom right:
dy0 = dy__[:-1, :-1]
dy1 = dy__[:-1, 1:]
dy2 = dy__[1:, 1:]
dy3 = dy__[1:, :-1]
The same is done with dx and g values.
To compute sums or differences correctly I need to change the mask to make it the same for all of them. For now I count the sum of converted into int masks of 4 arrays and check if it's more than one. So if there is more than one masked element - I mask it.
import functools
sum = functools.reduce(lambda x1, x2: x1.astype('int') + x2.astype('int'), list_of_masks)
mask = sum > 1 # mask output if more than 1 input is masked
But when I initialize masks like dy0.mask = new_mask they don't change.
Also when I replace 0 elements in one array with 1 using numpy.where() the mask disappears, so I can initialize the new one. But for those arrays which stay the same mask still doesn't change. (I checked the numpy.ma documentation, and it should)
The problem is in some functions there are too many arrays which mask might be changed to the new one, so it's better to find a good way to initialize it in one operation for few arrays and be sure it works.
Is there any way to do it or to find why it doesn`t work as it should?

Need Help in finding 2 seperate contours instead of a combined contour in MICR code

I an running OCR on bank cheques using pyimagesearch tutorial to detect micr code. The code used in the tutorial detects group contours & character contours from a reference image containing symbols.
In the tutorial when finding the contours for symbol below
the code uses an built-in python iterator to iterate over the contours (here 3 seperate contours) and combined to give a character for recognition purposes.
But in the cheque dataset that I use, I have the symbol with low resolution
The actual bottom of the cheque is :
which causes the iterator to consider the contour-2 & contour-3 as a single contour. Due to this the iterator iterates over the character following the above symbol (here '0') and prepares a incorrect template to match with the reference symbols. You can see the code below for better understanding.
I know here noise in the image is a factor, but is it possible to reduce the noise & also find the exact contour to detect the symbol?
I tried using noise reduction techniques like cv2.fastNlMeansDenoising & cv2.GaussianBlur before cv2.findContours step the contours 2&3 are detected as single contour instead of 2 seperate contours.
Also I tried altering the `cv2.findContours' parameters
Below is the working code where the characters are iterated for better understanding of python builtin iterator:
def extract_digits_and_symbols(image, charCnts, minW=5, minH=10):
# grab the internal Python iterator for the list of character
# contours, then initialize the character ROI and location
# lists, respectively
charIter = charCnts.__iter__()
rois = []
locs = []
# keep looping over the character contours until we reach the end
# of the list
while True:
try:
# grab the next character contour from the list, compute
# its bounding box, and initialize the ROI
c = next(charIter)
(cX, cY, cW, cH) = cv2.boundingRect(c)
roi = None
# check to see if the width and height are sufficiently
# large, indicating that we have found a digit
if cW >= minW and cH >= minH:
# extract the ROI
roi = image[cY:cY + cH, cX:cX + cW]
rois.append(roi)
cv2.imshow('roi',roi)
cv2.waitKey(0)
locs.append((cX, cY, cX + cW, cY + cH))
# otherwise, we are examining one of the special symbols
else:
# MICR symbols include three separate parts, so we
# need to grab the next two parts from our iterator,
# followed by initializing the bounding box
# coordinates for the symbol
parts = [c, next(charIter), next(charIter)]
(sXA, sYA, sXB, sYB) = (np.inf, np.inf, -np.inf,
-np.inf)
# loop over the parts
for p in parts:
# compute the bounding box for the part, then
# update our bookkeeping variables
# c = next(charIter)
# (cX, cY, cW, cH) = cv2.boundingRect(c)
# roi = image[cY:cY+cH, cX:cX+cW]
# cv2.imshow('symbol', roi)
# cv2.waitKey(0)
# roi = None
(pX, pY, pW, pH) = cv2.boundingRect(p)
sXA = min(sXA, pX)
sYA = min(sYA, pY)
sXB = max(sXB, pX + pW)
sYB = max(sYB, pY + pH)
# extract the ROI
roi = image[sYA:sYB, sXA:sXB]
cv2.imshow('symbol', roi)
cv2.waitKey(0)
rois.append(roi)
locs.append((sXA, sYA, sXB, sYB))
# we have reached the end of the iterator; gracefully break
# from the loop
except StopIteration:
break
# return a tuple of the ROIs and locations
return (rois, locs)
edit: contour 2 & 3 instead of contours 1 & 2
Try to find the right threshold value, instead of using cv2.THRESH_OTSU. It seems should be possible to find a suitable threshold from the provided example. If you can't find the threshold value that works for all images, you can try morphological closing on the threshold result with structuring element with 1-pixel width.
Edit (steps):
For threshold, you need to find appropriate value by hand, in your image threhsold value 100 seems to work:
i = cv.imread('image.png')
g = cv.cvtColor(i, cv.COLOR_BGR2GRAY)
_, tt = cv.threshold(g, 100, 255, cv.THRESH_BINARY_INV)
as for closing variant:
_, t = cv.threshold(g, 0,255,cv.THRESH_BINARY_INV | cv.THRESH_OTSU)
kernel = np.ones((12,1), np.uint8)
c = cv.morphologyEx(t, cv.MORPH_OPEN, kernel)
Note that I used import cv2 as cv. I also used opening instead of closing since in the example they inverted colors during thresholding

OpenCV get pixels on an circle

I'm new to OpenCV and I'm trying to get the pixels of a circle from an image.
For example, I draw a circle on a random image:
import cv2
raw_img = cv2.imread('sample_picture.png')
x = 50
y = 50
rad = 20
cv2.circle(raw_img,(x,y),rad,(0,255,0),-1)
cv2.imshow('output', raw_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
The output shows an image with a circle.
However, I want to be able to get all the pixel on the circle back in the form of an array. Is there any way to do this? I know I can get the approximate coordinates from the circle formula, but it will involve a lot of decimal calculations, and I'm pretty sure that the function cv2.circle() has already calculated the pixel, so is there a way to get it out from the function itself instead of calculating my self?
Also, if it is possible I would like to get the pixel of an ellipse using cv2.ellipse() back as an array of coordinates. But this time, I want to get the pixel only from a part of an ellipse (from a certain angle to another angle, which I can specify in the parameter of cv2.ellipse()).
Thank you.
You can achieve what you are looking for by using the numpy function:
numpy.where(condition[, x, y])
Detailed explanation of function in link :https://docs.scipy.org/doc/numpy-1.15.1/reference/generated/numpy.where.html
In your case, you would want to it to return the coordinates that has non-zero values. Using this method, you can draw anything on an empty array and it will return all rows and columns corresponding to non-zeros.
It will return the index of the array that satisfies the condition you set. Below is a code showing an example of the usage.
import cv2
import numpy as np
raw_img = cv2.imread('sample_picture.png')
x = 50
y = 50
rad = 20
cv2.circle(raw_img,(x,y),rad,(0,255,0),-1)
# Here is where you can obtain the coordinate you are looking for
combined = raw_img[:,:,0] + raw_img[:,:,1] + raw_img[:,:,2]
rows, cols, channel = np.where(combined > 0)
cv2.imshow('output', raw_img)
cv2.waitKey(0)
cv2.destroyAllWindows()

Use Pillow (PIL fork) for chroma key [duplicate]

I'm writing a script to chroma key (green screen) and composite some videos using Python and PIL (pillow). I can key the 720p images, but there's some left over green spill. Understandable but I'm writing a routine to remove that spill...however I'm struggling with how long it's taking. I can probably get better speeds using numpy tricks, but I'm not that familiar with it. Any ideas?
Here's my despill routine. It takes a PIL image and a sensitivity number but I've been leaving that at 1 so far...it's been working well. I'm coming in at just over 4 seconds for a 720p frame to remove this spill. For comparison, the chroma key routine runs in about 2 seconds per frame.
def despill(img, sensitivity=1):
"""
Blue limits green.
"""
start = time.time()
print '\t[*] Starting despill'
width, height = img.size
num_channels = len(img.getbands())
out = Image.new("RGBA", img.size, color=0)
for j in range(height):
for i in range(width):
#r,g,b,a = data[j,i]
r,g,b,a = img.getpixel((i,j))
if g > (b*sensitivity):
out_g = (b*sensitivity)
else:
out_g = g
# end if
out.putpixel((i,j), (r,out_g,b,a))
# end for
# end for
out.show()
print '\t[+] done.'
print '\t[!] Took: %0.1f seconds' % (time.time()-start)
exit()
return out
# end despill
Instead of putpixel, I tried to write the output pixel values to a numpy array then convert the array to a PIL image, but that was averaging just over 5 seconds...so this was faster somehow. I know putpixel isn't the snappiest option but I'm at a loss...
putpixel is slow, and loops like that are even slower, since they are run by the Python interpreter, which is slow as hell. The usual solution is to convert immediately the image to a numpy array and solve the problem with vectorized operations on it, which run in heavily optimized C code. In your case I would do something like:
arr = np.array(img)
g = arr[:,:,1]
bs = arr[:,:,2]*sensitivity
cond = g>bs
arr[:,:,1] = cond*bs + (~cond)*g
out = Image.fromarray(arr)
(it may not be correct and I'm sure it can be optimized way better, this is just a sketch)

Get pixel colors of tkinter canvas

I'd like to be able to create and interact with a Tkinter Canvas and, at any time, be able to iterate over each of its pixels and get their RGB values.
Setting pixel by pixel is not necessary, just getting. However, methods analogous to Canvas's create_polygon(), create_line(), create_text(), and create_oval() must be available as well for interacting with the image overall.
There are a number of restraints:
Must work with Python 3
Must work with Linux, Mac, and Windows
Must work with libraries that come with Python (no downloads)
The second restraint is mainly the reason I've posted this question when getting the color of pixels on the screen in Python3.x and several other similar questions already exist.
If this is impossible, what is the closest I can get?
Try it. But is slow :/
from util.color import Color
class ImageUtils:
#staticmethod
def get_pixels_of(canvas):
width = int(canvas["width"])
height = int(canvas["height"])
colors = []
for x in range(width):
column = []
for y in range(height):
column.append(ImageUtils.get_pixel_color(canvas, x, y))
colors.append(column)
return colors
#staticmethod
def get_pixel_color(canvas, x, y):
ids = canvas.find_overlapping(x, y, x, y)
if len(ids) > 0:
index = ids[-1]
color = canvas.itemcget(index, "fill")
color = color.upper()
if color != '':
return Color[color.upper()]
return "WHITE"
It's not possible. The canvas doesn't work that way.
If you're not interested in setting, you can use an image rather than a canvas. You can get the value of individual pixels in a PhotoImage.

Resources