Я хочу сделать снимок экрана с помощью скрипта python и ненавязчиво его сохранить.
Меня интересует только решение для Linux, и я должен поддерживать любую среду на основе X.
Мне любопытно проверить производительность предложенных ниже методов.
Новая ссылка: manpages.ubuntu.com/manpages/karmic/man1/scrot.1.html (@ArtOfWarfare)
@Mark -: - / К сожалению, Scrot не поставляется с OS X (я знаю, это был вопрос Linux. Обычно все, что применимо к Linux, также может применяться к OS X дословно).
Ах да, это снимок экрана на OS X.






import ImageGrab
img = ImageGrab.grab()
img.save('test.jpg','JPEG')
для этого требуется библиотека изображений Python
Работает только в Windows: pythonware.com/library/pil/handbook/imagegrab.htm
Этот работает на X11, а возможно, и на Windows (кто-нибудь, пожалуйста, проверьте). Требуется PyQt4:
import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')
Обратите внимание на лицензирование PyQt, которое является более строгим, чем Python и Qt. riverbankcomputing.co.uk/software/pyqt/license
Это единственное решение, которое работает на моих установках Linux "из коробки". Не знаю почему, но у меня везде PyQt4, а PyWX, PyGtk, ImageGrab не хватает. - Спасибо :).
Код просто заработал (в Windows 7 x64 - Python 2.7.5; дистрибутив Pythonxy). Также доступен Jpeg (например, ... .save ('d: /test.jpg', 'jpeg'))
Это работает без использования scrot или ImageMagick.
import gtk.gdk
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
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):
pb.save("screenshot.png","png")
print "Screenshot saved to screenshot.png."
else:
print "Unable to get the screenshot."
Заимствовано у http://ubuntuforums.org/showpost.php?p=2681009&postcount=5
Это не работает в приложении на основе графического интерфейса пользователя, использующем glade, и можете ли вы быстро улучшить этот код.
Когда я выполняю этот код (используя linux mint 16 в виртуальном боксе), результирующее изображение становится полностью черным. Вы знаете почему?
Иногда кодировка цветов отключена. Это очень раздражает. Посмотрите, поможет ли вам github.com/JDong820/neobot/blob/master/Linux/Robot/screen.py; обратите внимание на вызов get_rowstride.
Кросс-платформенное решение с использованием wxPython:
import wx
wx.App() # Need to create an App instance before doing anything
screen = wx.ScreenDC()
size = screen.GetSize()
bmp = wx.EmptyBitmap(size[0], size[1])
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
del mem # Release bitmap
bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
Ссылки с комментариями, пояснениями и контекстом в коде Python. blog.pythonlibrary.org/2010/04/16/… или blog.pythonlibrary.org/2010/04/16/…
Соберите все ответы в одном классе. Выводит изображение PIL.
#!/usr/bin/env python
# encoding: utf-8
"""
screengrab.py
Created by Alex Snet on 2011-10-10.
Copyright (c) 2011 CodeTeam. All rights reserved.
"""
import sys
import os
import Image
class screengrab:
def __init__(self):
try:
import gtk
except ImportError:
pass
else:
self.screen = self.getScreenByGtk
try:
import PyQt4
except ImportError:
pass
else:
self.screen = self.getScreenByQt
try:
import wx
except ImportError:
pass
else:
self.screen = self.getScreenByWx
try:
import ImageGrab
except ImportError:
pass
else:
self.screen = self.getScreenByPIL
def getScreenByGtk(self):
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 is None:
return False
else:
width,height = pb.get_width(),pb.get_height()
return Image.fromstring("RGB",(width,height),pb.get_pixels() )
def getScreenByQt(self):
from PyQt4.QtGui import QPixmap, QApplication
from PyQt4.Qt import QBuffer, QIODevice
import StringIO
app = QApplication(sys.argv)
buffer = QBuffer()
buffer.open(QIODevice.ReadWrite)
QPixmap.grabWindow(QApplication.desktop().winId()).save(buffer, 'png')
strio = StringIO.StringIO()
strio.write(buffer.data())
buffer.close()
del app
strio.seek(0)
return Image.open(strio)
def getScreenByPIL(self):
import ImageGrab
img = ImageGrab.grab()
return img
def getScreenByWx(self):
import wx
wx.App() # Need to create an App instance before doing anything
screen = wx.ScreenDC()
size = screen.GetSize()
bmp = wx.EmptyBitmap(size[0], size[1])
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
del mem # Release bitmap
#bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
myWxImage = wx.ImageFromBitmap( myBitmap )
PilImage = Image.new( 'RGB', (myWxImage.GetWidth(), myWxImage.GetHeight()) )
PilImage.fromstring( myWxImage.GetData() )
return PilImage
if __name__ == '__main__':
s = screengrab()
screen = s.screen()
screen.show()
Я не знаю, были ли изменения в wxWidgets после этой публикации, но метод getScreenByWx не работает с wx._core.PyNoAppError: The wx.App object must be created first!. Как ни странно, код работает нормально, если вы вводите его построчно в оболочке python, но в сценарии он не работает.
Вы должны протестировать свой код! Или не ваш, если вы его публикуете ... В getScreenByWx вам следует: а) заменить myBitmap на bmp и б) сохранить wx.App() в переменной. В getScreenByGtk замените (pb != None) на pb is None. И не используйте Qt, поэтому - вы не можете создать QApplication дважды - ваше приложение выйдет из строя при попытке создать его второй раз.
У меня есть проект оболочки (снимок экрана) для scrot, imagemagick, pyqt, wx и pygtk. Если у вас есть один из них, вы можете его использовать. Все решения включены в это обсуждение.
Установить:
easy_install pyscreenshot
Пример:
import pyscreenshot as ImageGrab
# fullscreen
im=ImageGrab.grab()
im.show()
# part of the screen
im=ImageGrab.grab(bbox=(10,10,500,500))
im.show()
# to file
ImageGrab.grab_to_file('im.png')
ImportError: невозможно импортировать имя gtkpixbuf
это дает мне эту ошибку: pyscreenshot.tempexport.RunProgError: No protocol specified giblib error: Can't open X display. It *is* running, yeah?" timeout_happened=False>
Просто для полноты: Xlib - Но при захвате всего экрана он несколько медленный:
from Xlib import display, X
import Image #PIL
W,H = 200,200
dsp = display.Display()
try:
root = dsp.screen().root
raw = root.get_image(0, 0, W,H, X.ZPixmap, 0xffffffff)
image = Image.fromstring("RGB", (W, H), raw.data, "raw", "BGRX")
image.show()
finally:
dsp.close()
Можно попробовать добавить некоторые типы в файлы-узкие места в PyXlib, а затем скомпилировать их с помощью Cython. Это может немного увеличить скорость.
Редактировать: Мы можем написать ядро функции на C, а затем использовать его в python из ctypes, вот что я вместе взломал:
#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
//Compile hint: gcc -shared -O3 -lX11 -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c
void getScreen(const int, const int, const int, const int, unsigned char *);
void getScreen(const int xx,const int yy,const int W, const int H, /*out*/ unsigned char * data)
{
Display *display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
XImage *image = XGetImage(display,root, xx,yy, W,H, AllPlanes, ZPixmap);
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
int x, y;
int ii = 0;
for (y = 0; y < H; y++) {
for (x = 0; x < W; x++) {
unsigned long pixel = XGetPixel(image,x,y);
unsigned char blue = (pixel & blue_mask);
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
data[ii + 2] = blue;
data[ii + 1] = green;
data[ii + 0] = red;
ii += 3;
}
}
XDestroyImage(image);
XDestroyWindow(display, root);
XCloseDisplay(display);
}
И затем python-файл:
import ctypes
import os
from PIL import Image
LibName = 'prtscn.so'
AbsLibPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + LibName
grab = ctypes.CDLL(AbsLibPath)
def grab_screen(x1,y1,x2,y2):
w, h = x2-x1, y2-y1
size = w * h
objlength = size * 3
grab.getScreen.argtypes = []
result = (ctypes.c_ubyte*objlength)()
grab.getScreen(x1,y1, w, h, result)
return Image.frombuffer('RGB', (w, h), result, 'raw', 'RGB', 0, 1)
if __name__ == '__main__':
im = grab_screen(0,0,1440,900)
im.show()
Это стоит золота, если не по крайней мере больше голосов, чем другие ответы. Солидная работа и родная тоже! Ваше здоровье!
для тех, кто ищет быстрый способ: этот подход в среднем занимает ~ 25 мсек для картинки размером 1000 x 1000.
@JHolta, знаете ли вы, как изменить качество захваченного изображения? (чтобы еще больше ускориться)
Неа. В настоящее время он просто копирует изображение рабочего стола как есть, любое преобразование изображения приведет к накладным расходам. Так что без снижения фактического качества рабочего стола вам не повезло с этой идеей. В любом случае, текущие накладные расходы, предположительно, относятся к стороне Python, где мы предварительно выделяем буфер (возможно, это можно сделать в c), и другой медленной части, где PIL читает в этом буфере, и те, и другие могут быть оптимизированы и может быть размещен на конце C.
Это отлично работает, но мне пришлось использовать #include <X11/Xutil.h> вместо #include <X11/Xlib.h>. Также для компиляции мне пришлось переместить -lX11 в конец вот так: gcc -shared -O3 -Wall -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c -lX11.
При дальнейшем стресс-тестировании это приводит к утечке ресурсов различными способами при последовательном захвате множества кадров. Вы должны освободить дисплей X11, который, как я считаю, по умолчанию ограничен 255 клиентами. Даже если вы увеличите этот предел, не отпуская изображение / окно, в конечном итоге у вас закончится нехватка памяти. Я отправил правку выше, которая решает эти проблемы.
Это наиболее фундаментальный ответ и наиболее эффективный.
@Mrlenny Меня также интересуют ускорения. Я думаю, что один из вариантов - снизить битовая глубина всей вашей среды X, чтобы XGetImage (который является основной рабочей лошадкой) фактически возвращал изображение более низкого качества.
0xffffffff, откуда вы узнали, что это 0xffffffff?
Попытайся:
#!/usr/bin/python
import gtk.gdk
import time
import random
import socket
import fcntl
import struct
import getpass
import os
import paramiko
while 1:
# generate a random time between 120 and 300 sec
random_time = random.randrange(20,25)
# wait between 120 and 300 seconds (or between 2 and 5 minutes)
print "Next picture in: %.2f minutes" % (float(random_time) / 60)
time.sleep(random_time)
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
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])
ts = time.asctime( time.localtime(time.time()) )
date = time.strftime("%d-%m-%Y")
timer = time.strftime("%I:%M:%S%p")
filename = timer
filename += ".png"
if (pb != None):
username = getpass.getuser() #Get username
newpath = r'screenshots/'+username+'/'+date #screenshot save path
if not os.path.exists(newpath): os.makedirs(newpath)
saveas = os.path.join(newpath,filename)
print saveas
pb.save(saveas,"png")
else:
print "Unable to get the screenshot."
Что это за хрень? Половина импорта бесполезна, есть цикл while, который никогда не завершается (и использует 1 вместо True), имеет if (pb != None): вместо if pb:, имеет бессмысленные необработанные строки.
Для этого Autopy есть пакет python
Модуль растрового изображения может захватывать экран (bitmap.capture_screen) Он многоплатформенный (Windows, Linux, Osx).
Это старый вопрос. Я хотел бы ответить на него с помощью новых инструментов.
Работает с python 3 (должен работать с python 2, но я его не тестировал) и PyQt5.
Минимальный рабочий пример. Скопируйте его в оболочку Python и получите результат.
from PyQt5.QtWidgets import QApplication
app = QApplication([])
screen = app.primaryScreen()
screenshot = screen.grabWindow(QApplication.desktop().winId())
screenshot.save('/tmp/screenshot.png')
у вас есть среднее время выполнения этой функции? просто интерес, если оно того стоит
@Mrlenny 300 мс (для полного кода), 165 мс (последние три строки кода).
От эта ветка:
import os
os.system("import -window root temp.png")
немного поздно, но неважно, что это просто
import autopy
import time
time.sleep(2)
b = autopy.bitmap.capture_screen()
b.save("C:/Users/mak/Desktop/m.png")
Я не мог сделать снимок экрана в Linux с помощью pyscreenshot или scrot, потому что вывод pyscreenshot был просто файлом изображения png с черным экраном.
но, слава богу, был еще один очень простой способ сделать снимок экрана в Linux, ничего не устанавливая. просто поместите код ниже в свой каталог и запустите с python demo.py
import os
os.system("gnome-screenshot --file=this_directory.png")
также есть много доступных опций для gnome-screenshot --help
Application Options:
-c, --clipboard Send the grab directly to the clipboard
-w, --window Grab a window instead of the entire screen
-a, --area Grab an area of the screen instead of the entire screen
-b, --include-border Include the window border with the screenshot
-B, --remove-border Remove the window border from the screenshot
-p, --include-pointer Include the pointer with the screenshot
-d, --delay=seconds Take screenshot after specified delay [in seconds]
-e, --border-effect=effect Effect to add to the border (shadow, border, vintage or none)
-i, --interactive Interactively set options
-f, --file=filename Save screenshot directly to this file
--version Print version information and exit
--display=DISPLAY X display to use
Вы можете использовать это
import os
os.system("import -window root screen_shot.png")
Это хороший подход, если вы можете получить изображение из фоновой программы, но хорошо знать, что если программа сфокусирована, она вернет исключение.
для ubuntu это работает для меня, вы можете сделать снимок экрана окна выбора с помощью этого:
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk
from gi.repository import GdkPixbuf
import numpy as np
from Xlib.display import Display
#define the window name
window_name = 'Spotify'
#define xid of your select 'window'
def locate_window(stack,window):
disp = Display()
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
WM_NAME = disp.intern_atom('WM_NAME')
name= []
for i, w in enumerate(stack):
win_id =w.get_xid()
window_obj = disp.create_resource_object('window', win_id)
for atom in (NET_WM_NAME, WM_NAME):
window_name=window_obj.get_full_property(atom, 0)
name.append(window_name.value)
for l in range(len(stack)):
if (name[2*l]==window):
return stack[l]
window = Gdk.get_default_root_window()
screen = window.get_screen()
stack = screen.get_window_stack()
myselectwindow = locate_window(stack,window_name)
img_pixbuf = Gdk.pixbuf_get_from_window(myselectwindow,*myselectwindow.get_geometry())
преобразовать pixbuf в массив
def pixbuf_to_array(p):
w,h,c,r=(p.get_width(), p.get_height(), p.get_n_channels(), p.get_rowstride())
assert p.get_colorspace() == GdkPixbuf.Colorspace.RGB
assert p.get_bits_per_sample() == 8
if p.get_has_alpha():
assert c == 4
else:
assert c == 3
assert r >= w * c
a=np.frombuffer(p.get_pixels(),dtype=np.uint8)
if a.shape[0] == w*c*h:
return a.reshape( (h, w, c) )
else:
b=np.zeros((h,w*c),'uint8')
for j in range(h):
b[j,:]=a[r*j:r*j+w*c]
return b.reshape( (h, w, c) )
beauty_print = pixbuf_to_array(img_pixbuf)
По какой причине вы не можете использовать мошонка?