Screenshot specific window in background without saving it as file - python

I would like to screenshot window in the background (not focused) without saving it as a file so it would be only as a variable. I have code that can screenshot the window but it will save it as .bmp. I can open it with pillow than but it would be better if it was saved in a variable instead of a file.
This is the code I use for the screenshot. I than open it with Image.open("imagename")
def background_screenshot(hwnd, width, height):
wDC = win32gui.GetWindowDC(hwnd)
dcObj=win32ui.CreateDCFromHandle(wDC)
cDC=dcObj.CreateCompatibleDC()
dataBitMap = win32ui.CreateBitmap()
dataBitMap.CreateCompatibleBitmap(dcObj, width, height)
cDC.SelectObject(dataBitMap)
cDC.BitBlt((0,0), (width, height), dcObj, (0,0), win32con.SRCCOPY)
dataBitMap.SaveBitmapFile(cDC, "images\\screenshot.bmp")
dcObj.DeleteDC()
cDC.DeleteDC()
win32gui.ReleaseDC(hwnd, wDC)
win32gui.DeleteObject(dataBitMap.GetHandle())

You can try this, and replace the 'take screenshot' function with your.
import mss import mss.tools
with mss.mss() as sct: # Use the 1st monitor
monitor = sct.monitors[1]
# Grab the picture
im = sct.grab(monitor)
# From now, you have access to different attributes like `rgb`
# See https://python-mss.readthedocs.io/api.html
# mss.tools.ScreenShot.rgb
# `im.rgb` contains bytes of the screen shot in RGB _but_ you will have to
# build the complete image because it does not set needed headers/structures
# for PNG, JPEG or any picture format.
# You can find the `to_png()` function that does this work for you,
# you can create your own, just take inspiration here:
# https://github.com/BoboTiG/python-mss/blob/master/mss/tools.py#L11
# If you would use that function, it is dead simple:
# args are (raw_data: bytes, (width, height): tuple, output: str)
Raw_bytes = mss.tools.to_png(im.rgb, im)
Or you can save the file and write a function that automatically deltes it after use.
os.remove(filepath)

Related

CreateCompatibleDC() or DeleteDC() fail in continues loop in Python - possible memory leak?

I am feeding an opencv window in a loop with this specific window screen capture routine below.
PROBLEM: after hundreds of cycles in the loop, it suddenly fail at either one of the two FAIL POINTS marked below in the code.
I am suspecting possible memory leak, but if I am not mistaken, I do delete and release what's required as well as I (re)select object before I delete it.
(The reason I am using this method, because it is important for me to be able to capture the specific window even if it is inactive and in the background and I did not found any other module/method actually works.)
What am I overlooking?
import win32gui
import win32ui
from PIL import Image
import numpy as np
import cv2
while True:
target_window = win32gui.FindWindow(None, ("Analytics dashboard - Google Chrome"))
hwndDC = win32gui.GetWindowDC(target_window)
mfcDC = win32ui.CreateDCFromHandle(hwndDC)
saveDC = mfcDC.CreateCompatibleDC() #### <-- RANDOM FAIL POINT 1: win32ui.error: CreateCompatibleDC failed
saveBitMap = win32ui.CreateBitmap()
saveBitMap.CreateCompatibleBitmap(mfcDC, screen_width, screen_height)
saveDC.SelectObject(saveBitMap)
result = windll.user32.PrintWindow(target_window, saveDC.GetSafeHdc(), 3)
bmpinfo = saveBitMap.GetInfo()
bmpstr = saveBitMap.GetBitmapBits(True)
screen_image = Image.frombuffer('RGB', (bmpinfo['bmWidth'], bmpinfo['bmHeight']), bmpstr, 'raw', 'BGRX', 0, 1)
mfcDC.DeleteDC() #### <-- RANDOM FAIL POINT 2: win32ui.error: DeleteDC failed
saveDC.DeleteDC()
win32gui.DeleteObject(saveBitMap.GetHandle())
win32gui.ReleaseDC(target_window, hwndDC)
image = cv2.cvtColor(np.array(screen_image), cv2.IMREAD_ANYCOLOR)
I tried the above code with ctypes.windll.user32.PrintWindow and there were no GDI leaks. PrintWindow's third argument should be PW_CLIENTONLY (1), or there is the undocumented PW_RENDERFULLCONTENT (2) option. Undocumented code is not reliable. I don't know what the constant (3) refers to.
If Chrome is the top window you should just take screen shot of desktop. This would be compliant.
It might help if you remove some of the code outside the loop, it will be more efficient at least.
import ctypes
import win32gui
import win32ui
import win32con
from PIL import Image
hdesktop = win32gui.GetDesktopWindow()
(l, r, width, height) = win32gui.GetClientRect(hdesktop)
hdc = win32gui.GetWindowDC(hdesktop)
dc = win32ui.CreateDCFromHandle(hdc)
memdc = dc.CreateCompatibleDC()
bitmap = win32ui.CreateBitmap()
bitmap.CreateCompatibleBitmap(dc, width, height)
memdc.SelectObject(bitmap)
while True:
hwnd = win32gui.FindWindow("Chrome_WidgetWin_1", None)
if hwnd == 0:
break
result = ctypes.windll.user32.PrintWindow(hwnd, memdc.GetSafeHdc(), 2)
if result == 1:
bytes = bitmap.GetBitmapBits(True)
img = Image.frombuffer('RGB', (width, height), bytes, 'raw', 'BGRX', 0, 1)
img.save("file.bmp")
#break
dc.DeleteDC()
memdc.DeleteDC()
win32gui.DeleteObject(bitmap.GetHandle())
win32gui.ReleaseDC(hwnd, hdc)
You can also add ctypes.windll.shcore.SetProcessDpiAwareness(2) on top

How to save an ammended image filename while maintaining an original file extention

I have a task of watermarking the image using python pillow. The function is supposed to take in two parameters: 1. Name of an image file and 2.Message - This is text that should be watermarked onto the image
The function should save the watermarked image with the name of the original file with a “_zz” appended to the of the name before the period.
I used the 'classic" python pillow approach:
def watermark (name, message):
from PIL import Image, ImageDraw, ImageFont
im = Image.open(name)
width, height = im.size
draw = ImageDraw.Draw(im)
text = message
font = ImageFont.truetype('arial.ttf', 36)
textwidth, textheight = draw.textsize(text, font)
margin = 10
x = width - textwidth - margin
y = height - textheight - margin
draw.text((x, y), text, font=font)
im.show()
im.save(name+'_zz') # incorrect of course
I can't figure out how to save the file with '_zz" amended to file name only and not to the extension.
I am new to Python, please be patient with me...
Cool! I am answering my own question... Very simple:
`
e=name.split(".")[-1]
n=name.split(".")[0]
wm_name=(n+'_zz'+'.'+e)
im.save(zz_name)
`

My Screen Recorder built using OpenCV and PyAutoGUI records only one frame

I am building a Screen Recorder in Python using numpy, OpenCV and PyAutoGUI. I have used tkinter for GUI Purposes. The problem with my screen recorder is that it records only one frame when I click on the Record Screen Button and then the screen gets jammed and I can't do anything. Here is my code so far:
from tkinter import *
import cv2
import numpy as np
import pyautogui
resolution = (1366,768)
Specify video codec:
codec = cv2.VideoWriter_fourcc(*"XVID")
Specify name of Output file:
filename = "Recordings.avi"
Specify frames rate (we can choose any value and experiment with it):
fps = 30.0
Creating a VideoWriter object:
out = cv2.VideoWriter(filename, codec, fps, resolution)
def startRecording():
window.iconify()
while True:
img = pyautogui.screenshot()
# Convert the screenshot to a numpy array
frame = np.array(img)
# Convert it from BGR(Blue, Green, Red) to
# RGB(Red, Green, Blue)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Write it to the output file
out.write(frame)
def stopRecording():
cv2.destroyAllWindows()
out.release()
window.destroy()
window = Tk()
window.title("Screen Recorder")
window.geometry("400x150")
window.config(bg='pink')
recordButton = Button(window,text="Record(F9)",font=("Bell MT",20),width=20,command=startRecording)
recordButton.pack(pady=(10,0))
stopButton = Button(window,text="Stop(F10)",font=("Bell MT",20),width=20,command=stopRecording)
stopButton.pack(pady=(10,0))
mainloop()
You cannot do a blocking call in a button callback.
As you wrote it startRecording will never end and will hence block tkinter mainloop. The recording probably works but your UI becomes unresponsive.
Your best shot would be to schedule the recording (look for the after method): record one frame every x millisecond.
Here is a simplified example based on your original code (you need to complete it)
continueRecording = True # must be declared before stopRecording
window = Tk() # must be declared before recordOneFrame
def stopRecording():
global continueRecording
continueRecording = False
def recordOneFrame():
global continueRecording
img = pyautogui.screenshot()
# Convert the screenshot to a numpy array
frame = np.array(img)
# Convert it from BGR(Blue, Green, Red) to
# RGB(Red, Green, Blue)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Write it to the output file
out.write(frame)
if continueRecording:
window.after(round(1/25. * 1000),recordOneFrame)
def startRecording():
recordOneFrame()

PyGame Saving Webcam, the image is split

I am using pygame to capture an image, and I can't seem to get it right. Attached is the image, and as you can see the image is split down the middle.
Here is the sourcecode:
def getImg(fname):
pygame.camera.init()
cm = pygame.camera.list_cameras()
cam = pygame.camera.Camera(cm[0])
cam.start()
img = cam.get_image()
pygame.image.save(img,fname)
cam.stop()
Ok, so I found a work around that I am posting, since it may be useful in the future:
def saveImage(fname):
pygame.camera.init()
cam_list = pygame.camera.list_cameras()
cam = pygame.camera.Camera(cam_list[0])
cam.start()
## the duplicate get_image():
## the first one gets the camera working,
## the second captures a clean image
cam.get_image()
cam.get_image()
img = cam.get_image()
pygame.image.save(img,fname)
cam.stop()

How to convert a string to an image?

I started to learn python a week ago and want to write a small program that converts a email to a image (.png) so that it can be shared on forums without risking to get lots of spam mails.
It seems like the python standard library doesn't contain a module that can do that but I've found out that there's a PIL module for it (PIL.ImageDraw).
My problem is that I can't seem to get it working.
So basically my questions are:
How to draw a text onto a image.
How to create a blank (white) image
Is there a way to do this without actually creating a file so that I can show it in a GUI before saving it?
Current Code:
import Image
import ImageDraw
import ImageFont
def getSize(txt, font):
testImg = Image.new('RGB', (1, 1))
testDraw = ImageDraw.Draw(testImg)
return testDraw.textsize(txt, font)
if __name__ == '__main__':
fontname = "Arial.ttf"
fontsize = 11
text = "example#gmail.com"
colorText = "black"
colorOutline = "red"
colorBackground = "white"
font = ImageFont.truetype(fontname, fontsize)
width, height = getSize(text, font)
img = Image.new('RGB', (width+4, height+4), colorBackground)
d = ImageDraw.Draw(img)
d.text((2, height/2), text, fill=colorText, font=font)
d.rectangle((0, 0, width+3, height+3), outline=colorOutline)
img.save("D:/image.png")
use ImageDraw.text - but it doesn't do any formating, it just prints string at the given location
img = Image.new('RGB', (200, 100))
d = ImageDraw.Draw(img)
d.text((20, 20), 'Hello', fill=(255, 0, 0))
to find out the text size:
text_width, text_height = d.textsize('Hello')
When creating image, add an aditional argument with the required color (white):
img = Image.new('RGB', (200, 100), (255, 255, 255))
until you save the image with Image.save method, there would be no file. Then it's only a matter of a proper transformation to put it into your GUI's format for display. This can be done by encoding the image into an in-memory image file:
import cStringIO
s = cStringIO.StringIO()
img.save(s, 'png')
in_memory_file = s.getvalue()
or if you use python3:
import io
s = io.BytesIO()
img.save(s, 'png')
in_memory_file = s.getvalue()
this can be then send to GUI. Or you can send direct raw bitmap data:
raw_img_data = img.tostring()
The first 3 lines are not complete, when I'm not wrong. The correct code would be:
from PIL import Image
from PIL import ImageDraw
from PIL import ImageFont

Categories