pygame.transform.scale not working on a font surface - python-3.x

I am trying to make a function in a pygame program for python 3. The function basically makes the multiple-line process of blitting words onto the screen simpler by making it one function, MakeWord(). It has some parameters to adjust size, font, position, etc. Instead of making the font based on normal font sizes, I wanted it based on pixel sizes so I did pygame.transfom.flip() on a font surface and it did not work. Can someone find the problem please?
def MakeWord(Phrase, Font, Color, Pos, Size):
FontType = pygame.font.SysFont(Font, 400) #400 because the size will be changed anyways
FontSurf = FontType.render(Phrase, True, Color)
pygame.transform.scale(FontSurf, Size) #Does not work
FontRect = FontSurf.get_rect()
FontRect.topleft = Pos
Display.blit(FontSurf, FontRect)
return FontRect #For FontRect.collidepoint(pygame.mouse.get_pos) used later

pygame.transform.scale returns a new Surface object. Try to assign it to FontSurf:
FontSurf = pygame.transform.scale(FontSurf, Size)

Related

How to detect an object in an image rather than screen with pyautogui?

I am using pyautogui.locateOnScreen() function to locate elements in chrome and get their x,y coordinates and click them. But at some point I need to take a screenshot of a part of the screen and search for the object I want in this screenshot. Then I get coordinates of it. Is it possible to do it with pyautogui?
My example code:
coord_one = pyautogui.locateOnScreen("first_image.png",confidence=0.95)
scshoot = pyautogui.screenshot(region=coord_one)
coord_two = # search second image in scshoot and if it can be detected get coordinates of it.
If it is not possible with pyautogui, can you advice the easiest-smartest way?
Thanks in advance.
I don't believe there is a built-in direct way to do what you need but the python-opencv library does the job.
The following code sample assumes you have an screen capture you just took "capture.png" and you want to find "logo.png" in that capture, which you know is an subsection of "capture.png".
Minimal example
"""Get bounding box of cropped image from original image."""
import cv2 as cv
import numpy as np
img_rgb = cv.imread(r'res/original.png')
# the cropped image, expected to be smaller
target_img = cv.imread(r'res/crop.png')
_, w, h = target_img.shape[::-1]
res = cv.matchTemplate(img_rgb,target_img,cv.TM_CCOEFF_NORMED)
# with the method used, the date in res are top left pixel coords
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)
top_left = max_loc
# if we add to it the width and height of the target, then we get the bbox.
bottom_right = (top_left[0] + w, top_left[1] + h)
cv.rectangle(img_rgb,top_left, bottom_right, 255, 2)
cv.imshow('', img_rgb)
MatchTemplate
From the docs, MatchTemplate "simply slides the template image over the input image (as in 2D convolution) and compares the template and patch of input image under the template image." Under the hood, this offers methods such as square difference to compare the images represented as arrays.
See more
For a more in-depth explanation, check the opencv docs as the code is entirely based off their example.

Change width of image in Opencv using Numpy

I'm making a Python file that will make a filter to have color on the Canny filter in OpenCV. I do this change from grayscale to color using the code provided below. My problem is when I apply the concatenate method (to add the color back as Canny filter is converted to grayscale), it cuts the width of the screen in 3 as I show in the 2 screenshots of before the color is added and after. The code snippet shown is only the transformation from grayscale to colored images.
What I've tried:
Tried using NumPy.tile: this wasn't the wisest attempt as it just repeated the same 1/3 of the screen twice more and didn't expand it to take up the whole screen as I had hoped.
Tried changing the image to only be from the index of 1/3 of the screen to cover the entire screen.
Tried setting the column index that is blank to equal None.
Image without the color added
Image with the color added
My code:
def convert_pixels(image, color):
rows, cols = image.shape
concat = np.zeros(image.shape)
image = np.concatenate((image, concat), axis=1)
image = np.concatenate((image, concat), axis=1)
image = image.reshape(rows, cols, 3)
index = image.nonzero()
#TODO: turn color into constantly changing color wheel or shifting colors
for i in zip(index[0], index[1], index[2]):
color.next_color()
image[i[0]][i[1]] = color.color
#TODO: fix this issue below:
#image[:, int(cols/3):cols] = None # turns right side (gliched) into None type
return image, color
In short, you're using concatenate on the wrong axis. axis=1 is the "columns" axis, so you're just putting two copies of zeros next to each other in the x direction. Since you want a three-channel image I would just initialize color_image with three channels and leave the original grayscale image alone:
def convert_pixels(image,color):
rows, cols = image.shape
color_image = np.zeros((rows,cols,3),dtype=np.uint8)
idx = image.nonzero()
for i in zip(*idx):
color_image[i] = color.color
return color_image,color
I've changed the indexing to match. I can't check this exactly since I don't know what your color object is, but I can confirm this works in terms of correctly shaping and indexing the new image.

Why does this code slow down? Graphics.py?

I have some code that reads a small BMP (128x96) file and puts the RGB values into a list.
I then run a nested loop and read the RGB values in reverse from the list and draw them on the screen.
It starts quite quickly and draws the first 20 lines in a second, but progressively slows down to such an extent I've never seen it finish. It only a small 128x96 image.
I feel it's the calls to the graphics.py library, buy why, or is it something else?
I'm running this on a raspberry pi, if that's of use. Python 3.4.2
If your interested in trying you can find the supporting files here https://drive.google.com/open?id=1yM9Vn1Nugnu79l1UNShamEAGd2VWF3T4
(It's the graphics.py library I'm using and the tiny bmp file, also the actual file in question called SlowDownWhy.py)
import math
import sys
from graphics import *
from PIL import Image
# Initialise Vars for Image width n height
iw=0
ih=0
img=Image.open("ani1.bmp","r") # Open Image
iw, ih = img.size # Set image width n height
ch = int(1000/ih) # Cube height set
cw = ch # Cube width set
win = GraphWin("My Window", iw*cw, ih*ch)
win.setBackground(color_rgb(128,128,128))
#Transfer Bitmap RGB vales to csv list - 'RGBlist'
pix_val = list(img.getdata())
RGBlist = [x for sets in pix_val for x in sets]
noe = (iw * ih * 3)-3
x = iw
y = ih
for vy in list(range(ih)):
y = y-1
x = iw
for vx in list(range(iw)):
x = x-1
r=RGBlist[noe]
g=RGBlist[noe+1]
b=RGBlist[noe+2]
noe=noe-3
cx=x*cw
cy=y*ch
aPoint = Rectangle(Point(cx,cy), Point(cx+cw,cy+ch))
aPoint.setFill(color_rgb(r,g,b))
aPoint.draw(win)
It should create a window no bigger than 1000 pixels in height and start drawing the picture from the bottom right to the top left, line by line. but slows down progressively.
Ignoring the invalid syntax, this is simply because of the way graphics.py is programmed: It is not designed to handle this many objects put onto the screen. (It uses tkinter in the back-end, which will slow down with 128*96=12,288 objects). For rendering images, you should either directly integrate them or use another library, as example pygame.
To integrate it into the graphics.py program, there is the Image-class, which you overwrote with the PIL.Image-library (this is the reason why you never do import *). Look here: Importing custom images into graphics.py

Change background and pixel color of image

For an experiment I want to show the participants drawings from a database which includes black drawn lines on a white background. Eventually I only want to shown what is the 'drawn part' per image in a certain color. So I want the white parts of the image to be made gray, so it is indistinguishable from the gray background. And I want to show the black parts of the image (the actual drawing) in other colors, for example red.
I am quite new to programming and so far I couldn't find an answer. I have tried several things, including the 2 options below.
Could anyone maybe show me an example of how to change the colors of the image I have attached to this message?
It would be very much appreciated!
[enter image description here][1]
####### OPTION 1, not working
#picture = Image.open(fname)
fname = exp.get_file('PICTURE_1.png')
picture = Image.open(fname)
# Get the size of the image
width, height = picture.size
# Process every pixel
for x in range(width):
for y in range(height):
current_color = picture.getpixel( (x,y) )
if current_color == (255,255,255):
new_color = (255,0,0)
picture.putpixel( (x,y), new_color)
elif current_color == (0,0,0):
new_color2 = (115,115,115)
picture.putpixel( (x,y), new_color2)
picture.show()
#picture.show()
win.flip()
clock.sleep(1000)
Implemented changes as you suggested gives: TypeError: 'int' object has no attribute 'getitem'
for x in range(width):
for y in range(height):
current_color = picture.getpixel( (x,y) )
if (current_color[0]<200) and (current_color[1]<200) and (current_color[2]<200):
new_color = (255,0,0)
picture.putpixel( (x,y), new_color)
elif (current_color[0]>200) and (current_color[1]>200) and (current_color[2]>200):
new_color2 = (115,115,115)
picture.putpixel( (x,y), new_color2)
picture.show()
Your approach in option one is basically correct, but here are a few tips to help you get it working properly:
Instead of saying if current_color == (255,255,255):, you should instead put
if (current_color[0]>200) and (current_color[1]>200) and (current_color[2]>200):
as even though the white parts of the image look white the pixels may not be exactly (255,255,255).
I thought you wanted to turn the white parts grey and the black parts red? In your code for option one, the lines
if current_color == (255,255,255):
new_color = (255,0,0)
will turn white pixels red. To turn black pixels red, it should be if current_color == (0,0,0).
If your code is still not working when these changes are made, you could try creating a new image with the same dimensions as the original one, and adding pixels to the new image rather than editing the pixels in the original one.
Also, it would help if you could tell us what actually happens when you run your code. Is there an error message, or is an image shown but the image is not correct? Could you please attach an example output?
Update:
I fiddled around with your code, and got it to do what you want it to do. Here is the code I ended up with:
import PIL
from PIL import Image
picture = Image.open('image_one.png')
# Get the size of the image
width, height = picture.size
for x in range(width):
for y in range(height):
current_color = picture.getpixel( (x,y) )
if (current_color[0]<200) and (current_color[1]<200) and (current_color[2]<200):
new_color = (255,0,0)
picture.putpixel( (x,y), new_color)
elif (current_color[0]>200) and (current_color[1]>200) and (current_color[2]>200):
new_color2 = (115,115,115)
picture.putpixel( (x,y), new_color2)
picture.show()
If you copy and paste this code into a script and run it in the same folder as your image, it should work.
There are much more efficient ways to do this than looping through each pixel and changing its value.
Since it looks like you're using PsychoPy, you can save your images as greyscale with a transparent background. By using the greyscale image format you allow PsychoPy to change the color of the lines to anything you want simply by altering the stimulus color setting. By using a transparent background, whatever you see behind your lines will show through, so you can choose to have a white square, a different square or no square at all. By this method, all the calculations for the colors are being done on the graphics card and can be changed every frame with no problems.
If for some reason you need to alter the image in ways that PsychoPy doesn't inherently allow (and if speed of processing matters) then you should try to change all the pixels in a single operation (using the numpy arrays) rather than one pixel at a time in a for-loop.

Detect Color of particular area of Image Nodejs OpenCV

I'm trying to write code to detect the color of a particular area of an image.
So far I have come across is using OpenCV, we can do this, But still haven't found any particular tutorial to help with this.
I want to do this with javascript, but I can also use python OpenCV to get the results.
can anyone please help me with sharing any useful link or can explain how can I achieve detecting the color of the particular area in the image.
For eg.
The box in red will show a different color. I need to figure out which color it is showing.
What I have tried:
I have tried OpenCV canny images, though I am successful to get area separated with canny images, how to detect the color of that particular canny area is still a challenge.
Also, I tried it with inRange method from OpenCV which works perfect
# find the colors within the specified boundaries and apply
# the mask
mask = cv2.inRange(image, lower, upper)
output = cv2.bitwise_and(image, image, mask = mask)
# show the images
cv2.imshow("images", np.hstack([image, output]))
It works well and extracts the color area from the image But is there any callback which responds if the image has particular color so that it can be all done automatically?
So I am assuming here that, you already know the location of the rect which is going to be dynamically changed and need to find out the single most dominant color in the desired ROI. There are a lot of ways to do the same, one is by getting the average, of all the pixels in the ROI, other is to count all the distinct pixel values in the given ROI, with some tolerance difference.
Method 1:
import cv2
import numpy as np
img = cv2.imread("path/to/img.jpg")
region_of_interest = (356, 88, 495, 227) # left, top, bottom, right
cropped_img = img[region_of_interest[1]:region_of_interest[3], region_of_interest[0]:region_of_interest[2]]
print cv2.mean(cropped_img)
>>> (53.430516018839604, 41.05708814243569, 244.54991977640907, 0.0)
Method 2:
To find out the various dominant clusters in the given image you can use cv2.kmeans() as:
import cv2
import numpy as np
img = cv2.imread("path/to/img.jpg")
region_of_interest = (356, 88, 495, 227)
cropped_img = img[region_of_interest[1]:region_of_interest[3], region_of_interest[0]:region_of_interest[2]]
Z = cropped_img.reshape((-1, 3))
Z = np.float32(Z)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 4
ret, label, center = cv2.kmeans(Z, K, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)
# Sort all the colors, as per their frequencies, as:
print center[sorted(range(K), key=lambda x: np.count_nonzero(label == [x]), reverse=True)[0]]
>>> [ 52.96525192 40.93861389 245.02325439]
#Prateek... nice to have the question narrowed down to the core. The code you provided does not address this issue at hand and remains just a question. I'll hint you towards a direction but you have to code it yourself.
steps that guide you towards a scripting result:
1) In your script add two (past & current) pixellists to store values (pixeltype + occurance).
2) Introduce a while-loop with an action true/stop statement (link to "3") for looping purpose because then it becomes a dynamic process.
3) Write a GUI with a flashy warning banner.
4) compare the pixellist with current_pixellist for serious state change (threshhold).
5) If the delta state change at "4" meets threshold throw the alert ("3").
When you've got written the code and enjoyed the trouble of tracking the tracebacks... then edit your question, update it with the code and reshape your question (i can help wiht that if you want). Then we can pick it up from there. Does that sound like a plan?
I am not sure why you need callback in this situation, but maybe this is what you mean?
def test_color(image, lower, upper):
mask = cv2.inRange(image, lower, upper)
return np.any(mask == 255)
Explanations:
cv2.inRange() will return 255 when pixel is in range (lower, upper), 0 otherwise (see docs)
Use np.any() to check if any element in the mask is actually 255

Resources