Gtk screenshot program weirdness (python) - python

I found some python code online for taking continuous screenshots with Gtk but I have a major problem. When I run my code:
import cv2
import Image
import numpy as np
def getScreenByGtk():
import gtk.gdk
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
if (pb == None):
return False
else:
width,height = pb.get_width(),pb.get_height()
return Image.fromstring("RGB",(width,height),pb.get_pixels() )
cv2.namedWindow('image', cv2.WINDOW_NORMAL)
while True:
screen_img = getScreenByGtk()
screen_img_array = np.asarray(screen_img)
cv2.imshow('image',screen_img_array)
cv2.waitKey(1)
cv2.destroyAllWindows()
It gives me images that are slanted to the left:
I've been trying to find a solution online for this but I can't.
I'm running lubuntu 12.04 in virtual box

Related

Capturing a specific webpage or url (python)

Im new in python and opencv and i'm making a project that captures my whole window , eventually i cope up an idea that i want to capture a specific webpage not my whole window . It is possible to Capture a webpage/url that's run on my computer? (for example i want to capture google url or Stack OverFlow )
(The Code is more like Screen Recorder)
Heres my Code :
import numpy as np
import cv2 as cv
import webbrowser
import pyautogui
from PIL import ImageGrab
from time import time
loop_time = time()
while True :
screenshot = pyautogui.screenshot()
#slicing the frame to make readable in opencv
screenshot = np.array(screenshot)
# Our operations on the frame come here
screenshot = cv.cvtColor(screenshot, cv.COLOR_BGRA2BGR)
#Displaying Text on the screen
# text = "Screen Shot"
# coordinates = (100,100)
# font = cv.FONT_HERSHEY_SIMPLEX
# fontScale = 1
# color = (255,0,255)
# thickness = 2
# screenshot = cv.putText(screenshot,text, coordinates, font, fontScale, color, thickness, cv2.LINE_AA)
cv.imshow('Capture Screen', screenshot)
# debuging fps
print(f'FPS { 1/(time()-loop_time):.0f}')
loop_time = time()
# wait time
keyCode = cv.waitKey(1)
#use esc or q key to exit window
if keyCode == 27 or keyCode == ord('q'):
print('Window is Closed')
cv.destroyAllWindows()
break
You could use PyGetWindow to select a specific window/app and get a screenshot of it like so:
import pyautogui
import pygetwindow as gw
gw.getWindowsWithTitle('Firefox')
active_window.activate()
pyautogui.screenshot().save('test.png')
Note: if you're working on a mac then the above will probably not work though.

Have to reset resolution between each picture with PiCamera

I am using Picamera library in Python in combination ith the new HQ camera and I found that if I want to
use the image port and take pictures in a loop, I must set the resolution again and again in the loop. Is this normal behaviour? It seems stupid to have to set it over and over (I checked the call that setting the resolution makes and it's not a very fast one).
Here is the code I am using:
from picamera.array import PiRGBArray
from picamera.camera import PiCamera
with PiCamera() as camera:
with PiRGBArray(camera) as output:
#camera.resolution = (1280, 720) #THIS DOES NOT WORK
while not self._stop_camera_stream:
camera.resolution = (1280, 720) #BUT THIS DOES
camera.capture(output, 'rgb')
print('Captured %dx%d image' % (output.array.shape[1], output.array.shape[0]))
#The line beginning here are for a preview inside a PyQt5 Window
image = output.array
h, w, ch = image.shape
bytesPerLine = ch*w
convertedToQtFormat = QImage(image, w, h, bytesPerLine, QImage.Format_RGB888)
p = convertedToQtFormat.scaled(640,480,Qt.KeepAspectRatio)
self.changePixmap.emit(p)
output.truncate(0)

[ERROR]libpng warning: image width is zero in ihdr using OpenCV and Python

I'm working on a small code, where a GPIO-trigger will take pictures with 3 webcams on a RPi3 with opencv.
Here is my code :
import numpy as np
import cv2
import datetime
import RPi.GPIO as GPIO
import os
import os.path
from time import gmtime, strftime
import time
#ini GPIO to get the trigger for cooling pause
GPIO_trigger=21
GPIO.setmode(GPIO.BCM)
GPIO.setup(GPIO_trigger, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#Create a list with all pluged-in Cameras
usblist=[0,1,2]
PATH="/home/pi/opencv-3.4.3/python_project/"
def csnip(channel):
#Number of frames to throw away while the camera adjusts to light levels
ramp_frames = 30
for Kn in usblist:
#set name for Video with camera name and Timestamp
Kn=str(Kn)
time_stamp = strftime("%Y-%m-%d_%H:%M:%S", gmtime())
Kameran="KRASP"
name=str(time_stamp) + "KRASP" + Kn
directory = str(PATH + "KRASP" +Kn)
if not os.path.exists(directory):
os.makedirs(directory)
camera_port = Kn
#Number of discarded frames
ramp_frames = 30
camera = cv2.VideoCapture(camera_port)
def make_1080p():
camera.set(3, 1920)
camera.set(4, 1080)
def make_720p():
camera.set(3, 1280)
camera.set(4, 720)
def make_480p():
camera.set(3, 640)
camera.set(4, 480)
def change_res(width, height):
camera.set(3, width)
camera.set(4, height)
make_720p()
#change_res(1280, 720)
# Captures a single image from the camera
def get_image():
retval, im = camera.read()
return im
for i in range(ramp_frames):
temp = get_image()
# Take the actual image we want to keep
camera_capture = get_image()
file = PATH+"KRASP"+Kn+"/"+name+".png"
print("Saving pic...")
cv2.imwrite(file, camera_capture)
del(camera)
time.sleep(2)
#When a falling trigger in detected on the GPIO, uses callback function and sleeps for 2s
GPIO.add_event_detect(GPIO_trigger, GPIO.FALLING, callback=csnip, bouncetime=2000)
try:
while True:
time.sleep(0.01)
except KeyboardInterrupt:
GPIO.cleanup() # clean up GPIO on CTRL+C exit
GPIO.cleanup() # clean up GPIO on normal exit
The code runs fine but i cannot open the picture. i get the following console output:
Saving pic...
libpng warning: Image width is zero in IHDR
libpng warning: Image height is zero in IHDR
libpng error: Invalid IHDR data
What is really wierd is that i have a similar code that is working perfectly, with the same functions ect ..
import numpy as np
import cv2
import datetime
#import RPi.GPIO as GPIO
import os
import os.path
from time import gmtime, strftime
#ini GPIO to get the trigger for cooling pause
#GPIO_trigger=7
#GPIO.setmode(GPIO.BOARD)
#GPIO.setup(GPIO_trigger, GPIO.IN)
#
Camera=int(input("How many Cameras are plugged in RPi?"))
i=0
#Create a list with all pluged-in Cameras
usblist=[]
PATH="/home/pi/opencv/KameraPi/"
def set_name(Kn):
#set name for Video with camera name and Timestamp
Kn=str(Kn)
time_stamp = strftime("%Y-%m-%d_%H:%M:%S", gmtime())
Kameran="KRASP"
name=time_stamp + Kameran + Kn
return name
def img_save(set_name):
# Camera number Kn
camera_port = Kn
#Number of discarded frames
ramp_frames = 30
camera = cv2.VideoCapture(camera_port)
def make_1080p():
camera.set(3, 1920)
camera.set(4, 1080)
def make_720p():
camera.set(3, 1280)
camera.set(4, 720)
def make_480p():
camera.set(3, 640)
camera.set(4, 480)
def change_res(width, height):
camera.set(3, width)
camera.set(4, height)
make_720p()
#change_res(1280, 720)
# Captures a single image from the camera
def get_image():
retval, im = camera.read()
return im
for i in range(ramp_frames):
temp = get_image()
# Take the actual image we want to keep
camera_capture = get_image()
file = str(PATH+usblist[Kn]+"/"+set_name+".png")
print("Saving pic...")
cv2.imwrite(file, camera_capture)
del(camera)
#Add Cameras to the list
while i < Camera:
usbname="KRASP"+str(i)
usblist.append(usbname)
i=i+1
listlength=len(usblist)
#Creat a folder for each Camera in the list if it doesn't exist already
for usb in usblist:
directory = str(PATH + usb)
if not os.path.exists(directory):
os.makedirs(directory)
#Temporary solution: Film and save video for given Camera (max 4 Camera)
while True:
print(usblist)
mchoice = int(input ("Take a picture with what Camera? "))
if mchoice == '':
break
else:
mchoice = int(float(mchoice))
if mchoice==0:
Kn=mchoice
img_save(set_name(Kn))
elif mchoice==1:
Kn=mchoice
img_save(set_name(Kn))
elif mchoice==2:
Kn=mchoice
img_save(set_name(Kn))
elif mchoice==3:
Kn=mchoice
img_save(set_name(Kn))
The last code runs on a user input but it should do the same... and no error on this one.
Does someone have a clue what is wrong with the triggered one?
Thank you

Video player in gtk using opencv gets stuck

I am trying to simply play a video in gtk environment using opencv code in python. In order to achieve it I made a glade file that contains a toplevel window, a file menu, a drawing area and a file chooser dialog. When user select a file, code starts a thread that calls function VideoPlayerDA that starts reading video and after every frame it generates a queue_draw signal to display frame in drawing area. The problem however is that after few frames the whole UI freezes and becomes unresponsive, video gets stuck.
Tools: I am using Gtk version 3.22.11, python 3.5.3, OpenCV version 3.3.0 on debian stretch.
PS: cv2.waitkey also seems to be not working.
import cv2
import time
import threading
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, GObject, Gdk, GdkPixbuf
GObject.threads_init()
mymutex = threading.Lock()
dimg = GdkPixbuf.Pixbuf.new_from_file('test.jpg')
def VideoPlayerDA(filename,drawing_area):
global dimg,dimg_available
cap = cv2.VideoCapture(filename)
while(cap.isOpened()):
mymutex.acquire()
ret, img = cap.read()
if img is not None:
boxAllocation = drawing_area.get_allocation()
img = cv2.resize(img, (boxAllocation.width,\
boxAllocation.height))
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) # opencv by default load BGR colorspace. Gtk supports RGB hance the conversion
dimg = GdkPixbuf.Pixbuf.new_from_data(img.tostring(),
GdkPixbuf.Colorspace.RGB,False,8,
img.shape[1],
img.shape[0],
img.shape[2]*img.shape[1],None,None)
#time.sleep(0.03)
drawing_area.queue_draw()
mymutex.release()
time.sleep(0.03)
#if ((cv2.waitKey(30) & 0xFF) == ord('q')):
# break
else:
mymutex.release()
break
print('end of file')
class video_player_gui:
def on_main_window_destroy(self,object):
Gtk.main_quit()
def on_open_activate(self,widget):
response = self.file_chooser.run()
if response == 0:
self.filename = self.file_chooser.get_filename()
thread = threading.Thread(target = VideoPlayerDA, args=(self.filename, self.drawing_area,))
thread.daemon = True
thread.start()
self.file_chooser.hide()
else:
pass
def on_drawing_area_draw(self,widget,cr):
global dimg
Gdk.cairo_set_source_pixbuf(cr, dimg.copy(), 0, 0)
cr.paint()
def __init__(self):
self.gladefile = '/home/nouman/Development/Glade/P2/OpenCv_integration_test.glade'
self.builder = Gtk.Builder()
self.builder.add_from_file(self.gladefile)
self.builder.connect_signals(self)
self.main_window = self.builder.get_object("main_window")
self.file_chooser = self.builder.get_object("file_chooser")
self.drawing_area = self.builder.get_object("drawing_area")
self.main_window.show()
if __name__ == "__main__":
main = video_player_gui()
Gtk.main()
I found a solution for now for anyone who will be getting same problem. I was using a global variable dimg without thread synchronization. Synchronizing threads before use of variable solved the problem. Edit on_drawing_area_draw as following will solve the issue
def on_drawing_area_draw(self,widget,cr):
global dimg
mymutex.acquire()
Gdk.cairo_set_source_pixbuf(cr, dimg.copy(), 0, 0)
cr.paint()
mymutex.release()

Capture video data from screen in Python

Is there a way with Python (maybe with OpenCV or PIL) to continuously grab frames of all or a portion of the screen, at least at 15 fps or more? I've seen it done in other languages, so in theory it should be possible.
I do not need to save the image data to a file. I actually just want it to output an array containing the raw RGB data (like in a numpy array or something) since I'm going to just take it and send it to a large LED display (probably after re-sizing it).
With all of the above solutions, I was unable to get a usable frame rate until I modified my code in the following way:
import numpy as np
import cv2
from mss import mss
from PIL import Image
bounding_box = {'top': 100, 'left': 0, 'width': 400, 'height': 300}
sct = mss()
while True:
sct_img = sct.grab(bounding_box)
cv2.imshow('screen', np.array(sct_img))
if (cv2.waitKey(1) & 0xFF) == ord('q'):
cv2.destroyAllWindows()
break
With this solution, I easily get 20+ frames/second.
For reference, check this link: OpenCV/Numpy example with mss
There is an other solution with mss which provide much better frame rate. (Tested on a Macbook Pro with MacOS Sierra)
import numpy as np
import cv2
from mss import mss
from PIL import Image
mon = {'left': 160, 'top': 160, 'width': 200, 'height': 200}
with mss() as sct:
while True:
screenShot = sct.grab(mon)
img = Image.frombytes(
'RGB',
(screenShot.width, screenShot.height),
screenShot.rgb,
)
cv2.imshow('test', np.array(img))
if cv2.waitKey(33) & 0xFF in (
ord('q'),
27,
):
break
You will need to use ImageGrab from Pillow (PIL) Library and convert the capture to numpy array. When you have the array you can do what you please with it using opencv. I converted capture to gray color and used imshow() as a demonstration.
Here is a quick code to get you started:
from PIL import ImageGrab
import numpy as np
import cv2
img = ImageGrab.grab(bbox=(100,10,400,780)) #bbox specifies specific region (bbox= x,y,width,height *starts top-left)
img_np = np.array(img) #this is the array obtained from conversion
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
cv2.imshow("test", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
you can plug an array there with the frequency you please to keep capturing frames. After that you just decode the frames. don't forget to add before the loop:
fourcc = cv2.VideoWriter_fourcc(*'XVID')
vid = cv2.VideoWriter('output.avi', fourcc, 6, (640,480))
and inside the loop you can add:
vid.write(frame) #the edited frame or the original img_np as you please
UPDATE
the end result look something like this (If you want to achieve a stream of frames that is. Storing as video just a demonstration of using opencv on the screen captured):
from PIL import ImageGrab
import numpy as np
import cv2
while(True):
img = ImageGrab.grab(bbox=(100,10,400,780)) #bbox specifies specific region (bbox= x,y,width,height)
img_np = np.array(img)
frame = cv2.cvtColor(img_np, cv2.COLOR_BGR2GRAY)
cv2.imshow("test", frame)
cv2.waitKey(0)
cv2.destroyAllWindows()
Hope that helps
based on this post and others posts, i made something like this .
Its taking a screenshot and writing into a video file without saving the img.
import cv2
import numpy as np
import os
import pyautogui
output = "video.avi"
img = pyautogui.screenshot()
img = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
#get info from img
height, width, channels = img.shape
# Define the codec and create VideoWriter object
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
out = cv2.VideoWriter(output, fourcc, 20.0, (width, height))
while(True):
try:
img = pyautogui.screenshot()
image = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
out.write(image)
StopIteration(0.5)
except KeyboardInterrupt:
break
out.release()
cv2.destroyAllWindows()
You can try this code as it is working for me. I've tested it on Linux
import numpy as np
import cv2
from mss import mss
from PIL import Image
sct = mss()
while 1:
w, h = 800, 640
monitor = {'top': 0, 'left': 0, 'width': w, 'height': h}
img = Image.frombytes('RGB', (w,h), sct.grab(monitor).rgb)
cv2.imshow('test', cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR))
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
Make sure that the following packages are installed:
Pillow, opencv-python, numpy, mss
You can try this=>
import mss
import numpy
with mss.mss() as sct:
monitor = {'top': 40, 'left': 0, 'width': 800, 'height': 640}
img = numpy.array(sct.grab(monitor))
print(img)
I tried all of the above but it did not give me the real-time screen update.
You can try this. This code is tested and worked successfully and also give you a good fps output. You can also judge this by each loop time it's needed.
import numpy as np
import cv2
from PIL import ImageGrab as ig
import time
last_time = time.time()
while(True):
screen = ig.grab(bbox=(50,50,800,640))
print('Loop took {} seconds',format(time.time()-last_time))
cv2.imshow("test", np.array(screen))
last_time = time.time()
if cv2.waitKey(25) & 0xFF == ord('q'):
cv2.destroyAllWindows()
break
If anyone looking for a much easier and fastest way to grab screen as frame in python, then try ScreenGear API from my high-performance video-processing vidgear library in just few lines of python code on any machine (Tested on all platforms, including Windows 10, MacOS Serra, Linux Mint) and enjoy threaded screen-casting:
Note: It also supports multiple backends and screens out-of-the box.
# import required libraries
from vidgear.gears import ScreenGear
import cv2
# define dimensions of screen w.r.t to given monitor to be captured
options = {'top': 40, 'left': 0, 'width': 100, 'height': 100}
# open video stream with defined parameters
stream = ScreenGear(logging=True, **options).start()
# loop over
while True:
# read frames from stream
frame = stream.read()
# check for frame if Nonetype
if frame is None:
break
# {do something with the frame here}
# Show output window
cv2.imshow("Output Frame", frame)
# check for 'q' key if pressed
key = cv2.waitKey(1) & 0xFF
if key == ord("q"):
break
# close output window
cv2.destroyAllWindows()
# safely close video stream
stream.stop()
VidGear library Docs: https://abhitronix.github.io/vidgear
ScreenGear API: https://abhitronix.github.io/vidgear/latest/gears/screengear/overview/
More examples: https://abhitronix.github.io/vidgear/latest/gears/screengear/usage/
I've tried ImageGrab from PIL and it gave me 20fps which is ok but using win32 libraries gave me +40fps which is amazing!
I used this code by Frannecklp but it didn't work just fine so I needed to modify it:
-Firstly pip install pywin32 in case using the libraries
-import the libraries like this instead:
import cv2
import numpy as np
from win32 import win32gui
from pythonwin import win32ui
from win32.lib import win32con
from win32 import win32api
for geting a simple image screen do:
from grab_screen import grab_screen
import cv2
img = grab_screen()
cv2.imshow('frame',img)
and for getting frames:
while(True):
#frame = grab_screen((0,0,100,100))
frame = grab_screen()
cv2.imshow('frame',frame)
if cv2.waitKey(1) & 0xFF == ord('q') or x>150:
break
This task is very simple with opencv, we are just capturing screenshots in loop, and converting it into frames. I created timer for screenrecording, in start you have to enter how many seconds you want to record:) Here is the code.
import cv2
import numpy as np
import pyautogui
from win32api import GetSystemMetrics
import time
#Take resolution from system automatically
w = GetSystemMetrics(0)
h = GetSystemMetrics(1)
SCREEN_SIZE = (w,h)
fourcc = cv2.VideoWriter_fourcc(*"XVID")
out = cv2.VideoWriter("recording.mp4", fourcc, 20.0, (SCREEN_SIZE))
tim = time.time()
tp = int(input('How many times you want to record screen?->(Define value in Seconds): '))
tp = tp+tp
f = tim+tp
while True:
img = pyautogui.screenshot()
frame = np.array(img)
frame = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
out.write(frame)
tu = time.time()
if tu>f:
break
cv2.destroyAllWindows()
out.release()
So that's how you can use time in screen recording, you don't need to use imshow() because it shows infinitely our screen recording on-screen so output video looks weird.

Categories

Resources