How can I search an area for pixel change? - python

This is the code I am using to detect if a pixel (in this case pixel 510,510) turns to a certain color.
import PIL.ImageGrab
import mouse
while True:
rgb = PIL.ImageGrab.grab(bbox = None)
rgb2=(253, 146, 134)
print (rgb.getpixel((510, 510)))
if (rgb.getpixel((510, 510))) == rgb2:
mouse.click()
I want to be able to search an area of my screen for any pixel that changes to a specified color, not just an individual pixel. How might I do that? I want to keep this running as fast as possible. I know most areas searched on an image or video would be a rectangle, but could it be a triangle to cut down on pixels searched? If not, the next sentences are irrelevant. How so? Would it work if I give the coords of each point in the triangle?

Make a black rectangular image just big enough to contain the shape you want to detect. Use np.zeros((h,w,3), np.uint8) to create it. It will be zero everywhere.
Draw the shape you want to detect in the black rectangle with colour=[1,1,1]. You now have an image that is 1 where you are interested in the pixels and 0 elsewhere. Do these first 2 steps outside your main loop.
Inside your loop, grab an area of screen the same size as your mask from steps 1 and 2. Multiply your image by the mask and all pixels you are not interested in will become zero. Test if your colour exists using np.where() or cv2.countNonZero(np.all(im==soughtColour, axis=-1))
As an alternative to drawing with colour=[1,1,1] at the second step, draw with colour=[255,255,255] and then in the third step use cv2.bitwise_and() instead of multiplying.

Related

Stacking images together opencv python

So I am trying to make a single top down view from these 4 bird eye view images taken from 4 different cameras and after doing perspective transform, I just need to stack the 4 trapezoids together (without the green parts which are the walls).
the four images
example of what I want to achieve
first make your 4 images the same size by padding them with 0s while maintaining their position.
lets assume the top & bottom images are 150x50 and the left & right images are 50x100. So your final output image size will be 150x50+100+50=150x200. now do the math to figure out where to pad each image to keep their position.
now we need to remove the walls in each image and only keep the floor, you have two options from here:
Option 1:
Create a new black "mask" image for each image (same size 150x200). Now you can either manually get the location of the floor pixels or use color, and set the floor pixels in the mask to 255.
Now that you have the floor pixels mask for each image, you will need to copy those floor pixels over to your final output image. so create a new black image, for each mask, get the location of the non-zero pixels and copy the value of the pixels from the corresponding image over to your output image
Option 2:
Find the wall pixels in your images and set them to 0 then just add the images together.

Grab the required part from an Image in Python and find the defective pixels in that part

I am working on a project, where I have to find defective pixels of a screen based on the Image of that screen. Now, I have used opencv and applied mask for the contours and got the screen area and also the defective pixels. But I am stuck at the point of finding the positions of the defective pixels. To find defective pixels I have used, red is the dominant color:
Now, I need to find the positions of the red pixels which are not appended in 'data_red' and within contour. Looking forward for a solution! Thanking you in advance.
red=[i for i in Image_data]
for i in red:
if i>175:
data_red.append(i)
The image before applying the mask, after applying the mask, only the red part remains red and others become black
Now I need the red pixel positions to be displayed, which do not satisfy the condition, >175.
Use Numpy.
import numpy as np
# get red from somewhere
red_np = np.array(red)
data_red = red_np[red_np > 175]
# and for the ones not appended
not_appended = np.argwhere(data_red <= 175)
This will return a numpy array with positions of all elements in the array data_red whose values were below 175.

How to get border pixels of an image in python?

I have an image, using steganography I want to save the data in border pixels only.
In other words, I want to save data only in the least significant bits(LSB) of border pixels of an image.
Is there any way to get border pixels to store data( max 15 characters text) in the border pixels?
Plz, help me out...
OBTAINING BORDER PIXELS:
Masking operations are one of many ways to obtain the border pixels of an image. The code would be as follows:
a= cv2.imread('cal1.jpg')
bw = 20 //width of border required
mask = np.ones(a.shape[:2], dtype = "uint8")
cv2.rectangle(mask, (bw,bw),(a.shape[1]-bw,a.shape[0]-bw), 0, -1)
output = cv2.bitwise_and(a, a, mask = mask)
cv2.imshow('out', output)
cv2.waitKey(5000)
After I get an array of ones with the same dimension as the input image, I use cv2.rectangle function to draw a rectangle of zeros. The first argument is the image you want to draw on, second argument is start (x,y) point and the third argument is the end (x,y) point. Fourth argument is the color and '-1' represents the thickness of rectangle drawn (-1 fills the rectangle). You can find the documentation for the function here.
Now that we have our mask, you can use 'cv2.bitwise_and' (documentation) function to perform AND operation on the pixels. Basically what happens is, the pixels that are AND with '1' pixels in the mask, retain their pixel values. Pixels that are AND with '0' pixels in the mask are made 0. This way you will have the output as follows:
.
The input image was :
You have the border pixels now!
Using LSB planes to store your info is not a good idea. It makes sense when you think about it. A simple lossy compression would affect most of your hidden data. Saving your image as JPEG would result in loss of info or severe affected info. If you want to still try LSB, look into bit-plane slicing. Through bit-plane slicing, you basically obtain bit planes (from MSB to LSB) of the image. (image from researchgate.net)
I have done it in Matlab and not quite sure about doing it in python. In Matlab,
the function, 'bitget(image, 1)', returns the LSB of the image. I found a question on bit-plane slicing using python here. Though unanswered, you might want to look into the posted code.
To access border pixel and enter data into it.
A shape of an image is accessed by t= img.shape. It returns a tuple of the number of rows, columns, and channels.A component is RGB which 1,2,3 respectively.int(r[0]) is variable in which a value is stored.
import cv2
img = cv2.imread('xyz.png')
t = img.shape
print(t)
component = 2
img.itemset((0,0,component),int(r[0]))
img.itemset((0,t[1]-1,component),int(r[1]))
img.itemset((t[0]-1,0,component),int(r[2]))
img.itemset((t[0]-1,t[1]-1,component),int(r[3]))
print(img.item(0,0,component))
print(img.item(0,t[1]-1,component))
print(img.item(t[0]-1,0,component))
print(img.item(t[0]-1,t[1]-1,component))
cv2.imwrite('output.png',img)

numpy and detecting .mean() in a radius

I'm pretty new to numpy.
I have been looking around how to do this but I can't find anything easy enough.
This is the problem.
I'm identifying particles in red (it's ok and done) so I have an array with locations.
I make a new image with these locations with grey dilation and scipy.ndimage, having the dilated positions with a value and the rest 0.
Then I multiply this image with another image (green color), so that the new color only has signals where you have particles in red. What I want to do is to detect the mean of intensities in this other color per given point, in a given radius or square for example.
How can I do this? Do I make scipy.ndimage.measurements.label in the initial color and then use the same array indexes to have the means? Or I can just have x,y coordinates and do the mean() over a given radius?

OpenCV concave and convex corner points of polygons

Problem
I am working on a project where I need to get the bounding boxes of dumbell like shapes. However, I need the fewest points possible, and the boxes need to fit the shapes at all corners. Here's an Image I made to test: Blurry, cracked, dumbell shape
I don't care about the gaps going into the shape, I just want to clean it up, and straighten the edges so that I can get the contours of a shape like this: Cleaned up
I have been attempting to threshold() it out, getting the contours of it using findContours() and then using approxPolyDP() to simplify the crazy amount of points the contours end up being. So, after fiddling with this for about three days now, how can I simply get either:
Two boxes specifying the ends of the dumbell and a rectangle in the middle, or
One contour with the twelve points for all the corners
The second option would be preferred since that really is my ultimate goal: getting the points that are at those corners.
A few things to note:
I am using OpenCV for Python
There will generally be many of these shapes of all sizes all over the input image
They will have only horizontal or vertical positioning. No strange 27 degree angles...
What I need:
I really don't need someone to write the code for me, I just need some method or algorithm in order to get this done, preferably with some simple examples.
My Code
Here is my overly clean code with functions I don't even use but figure I would use them eventually:
import cv2
import numpy as np
class traceImage():
def __init__(self, imageLocation):
self.threshNum = 127
self.im = cv2.imread(imageLocation)
self.imOrig = self.im
self.imGray = cv2.cvtColor(self.im, cv2.COLOR_BGR2GRAY)
self.ret, self.imThresh = cv2.threshold(self.imGray, self.threshNum, 255, 0)
self.contours, self.hierarchy = cv2.findContours(self.imThresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
def createGray(self):
self.imGray = cv2.cvtColor(self.im, cv2.COLOR_BGR2GRAY)
def adjustThresh(self, threshNum):
self.ret, self.imThresh = cv2.threshold(self.imGray, threshNum, 255, 0)
def getContours(self):
self.contours, self.hierarchy = cv2.findContours(self.imThresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
def approximatePoly(self, percent):
i=0
for shape in self.contours:
shape = cv2.approxPolyDP(shape, percent*cv2.arcLength(shape, True), True)
self.contours[i] = shape
i+=1
def drawContours(self, blobWidth, color=(255,255,255)):
cv2.drawContours(self.im, self.contours, -1, color, blobWidth)
def newWindow(self, name):
cv2.namedWindow(name)
def showImage(self, window):
cv2.imshow(window, self.im)
def display(self):
while True:
cv2.waitKey()
def displayUntil(self, key):
while True:
pressed = cv2.waitKey()
if pressed == key:
break
if __name__ == "__main__":
blobWidth = 30
ti = traceImage("dumbell.png")
ti.approximatePoly(0.01)
for thresh in range(127,256):
ti.adjustThresh(thresh)
ti.getContours()
ti.drawContours(blobWidth)
ti.showImage("Image")
ti.displayUntil(10)
ti.createGray()
ti.adjustThresh(127)
ti.getContours()
ti.approximatePoly(0.0099)
ti.drawContours(2, (0,255,0))
ti.showImage("Image")
ti.display()
Code Explanation
I know I might not be doing some things right here, but hey, I'm proud of it :)
So, the idea is that there are very often holes and gaps in these dumbells and so I figured that if I iterated through all the threshold values from 127 to 255 and drew the contours onto the image with large enough thickness, the thickness of drawing the contours would fill in any small enough holes, and I could use the new, blobby image to get the edges and then scale the sides back down to size. That was my thinking. There's got to be another, beter way though...
Summary
I want to end up with 12 points; one for each corner of the shape.
EDIT:
After trying out some erosion and dilation, it seems that the best option would be to slice the contours at certain points and then use bounding boxes around the sliced shapes to get the right boxy corners, and then doing some calculations to rejoin the boxes into one shape. A rather interesting challenge...
EDIT 2:
I discovered something that works well! I made my own line detection system, that only detects horizontal or vertical lines, and then on a detected line/contour edge, the program draws a black line that extends across the whole image, thus effectively slicing the image at the straight lines of the contours. Once it does that, it gets new contours of the sliced up boxes, draws bounding boxes around the pieces and then uses dilation to close the gaps. So far, it works well on large shapes, but when the shapes are small, it tends to lose a bit of the shape.
So, after fiddling with erosion, dilation, closing, opening, and looking at straight contours, I have figured out a solution that works. Thank you #Ante and #a.alsram! Your two ideas combined helped me get to my solution. So here's how it works.
Method
The program iterates over each contour, and over every pair of points in the contour, looking for point pairs that lie on the same axis and calculating the distance between them. If the distance is greater than an adjustable threshold, the program decides that those points are considered an edge on the shape. Then the program uses that edge, and draws a black line along the whole contour, thus cutting the contour at that edge. Then the program redetermines contours and since the shape was cut. These pieces that were cut off are know their own contours, which then are bounded by bounding boxes. and finally, all shapes are dilated and eroded (close) to rejoin the boxes that were cut off.
This method can be done several times, but each time there is a little bit of accuracy loss. But it works for what I need and certainly was a fun challenge! Thanks for your help guys!
natebot13
Maybe simple solution can help. If there is a threshold length to close a gaps,
it is possible to split image in a grid with cell lengths >= threshold, and use
cells that have something inside. With that there will be only horizontal and
vertical lines, and by taking a care about grid to follow original horizontal
and vertical lines it will cover main line features.
Update
Take a look on mathematical morphology. I think closing operation with structuring element (2*k+1)x(2*k+1) pixels can do what you are looking for.
Algorithm should take threshold parameter k, and performs dilation and than erosion. That means change image so that for each white pixel set all neighbours on distance k ((2*k+1)x(2*k+1) box) to the white, and than change image so that for each black pixel set neighbours on distance k to the black.
It is enough to do operations on boundary pixels.

Categories