Chụp ảnh màn hình qua tập lệnh Python trên Linux


81

Tôi muốn chụp ảnh màn hình thông qua tập lệnh python và lưu nó một cách kín đáo.

Tôi chỉ quan tâm đến giải pháp Linux và sẽ hỗ trợ bất kỳ môi trường dựa trên X.


Bất kỳ lý do gì bạn không thể sử dụng scrot ?
Đánh dấu

Tôi tò mò muốn kiểm tra hiệu suất của các phương pháp được đề xuất bên dưới.
JDong


@ Mark -: - / Đáng tiếc, Scrot không đi kèm với OS X (Tôi biết, đây là một câu hỏi Linux Chỉ cần bình thường bất cứ điều gì áp dụng cho Linux cũng có thể áp dụng cho OS X đúng nguyên văn..)
ArtOfWarfare

Ahh đúng, đó là ảnh chụp màn hình trên OS X.
Mark

Câu trả lời:


65

Điều này hoạt động mà không cần phải sử dụng scrot hoặc 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."

Mượn từ http://ubuntuforums.org/showpost.php?p=2681009&postcount=5


Điều này không hoạt động trong ứng dụng dựa trên GUI sử dụng glade và nhanh chóng, bạn có thể cải thiện mã này không.
Subodh Ghulaxe

Khi tôi thực thi mã này (sử dụng linux mint 16 trong virtualbox), hình ảnh kết quả hoàn toàn là màu đen. Bạn có bất kỳ ý tưởng tại sao?
bab

Đôi khi mã hóa màu bị tắt. Nó khá khó chịu. Xem liệu github.com/JDong820/neobot/blob/master/Linux/Robot/screen.py có trợ giúp gì cho bạn không; thông báo cuộc gọi đến get_rowstride.
JDong

48

Biên dịch tất cả các câu trả lời trong một lớp. Xuất hình ảnh 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()

Tôi không biết liệu đã có sự thay đổi đối với wxWidgets kể từ bài đăng này hay chưa, nhưng getScreenByWxphương pháp này không thành công với wx._core.PyNoAppError: The wx.App object must be created first!. Thật thú vị, mã hoạt động tốt nếu bạn nhập từng dòng một trong trình bao python nhưng trong một tập lệnh, nó không thành công.
CadentOrange

Bạn nên kiểm tra mã của bạn! Hoặc, không phải của bạn, nếu bạn đang xuất bản nó ... getScreenByWxBạn nên a) thay thế myBitmapbằng bmpvà b) lưu wx.App()vào một biến. Thay getScreenByGtkthế (pb != None)thành pb is None. Và đừng sử dụng Qt vì vậy - bạn không thể tạo hai lần QApplication- ứng dụng của bạn sẽ gặp sự cố khi cố tạo lần thứ hai.
Ban giám khảo

41

Chỉ để hoàn thiện: Xlib - Nhưng hơi chậm khi chụp toàn màn hình:

from Xlib import display, X
import Image #PIL

W,H = 200,200
dsp = display.Display()
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()

Người ta có thể cố gắng loại bỏ một số loại trong các tệp nút cổ chai trong PyXlib, và sau đó biên dịch nó bằng Cython. Điều đó có thể tăng tốc độ một chút.


Chỉnh sửa: Chúng ta có thể viết cốt lõi của hàm trong C, sau đó sử dụng nó trong python từ ctypes, đây là thứ mà tôi đã hack cùng nhau:

#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);
}

Và sau đó là tệp 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()

3
Đây là giá trị vàng, nếu không ít nhất là nhiều phiếu bầu hơn các câu trả lời khác. Công việc vững chắc và một bản địa quá! Chúc mừng!
Torxed

1
dành cho những ai đang tìm kiếm một cách nhanh chóng: phương pháp này mất ~ 25ms ở trung bình cho một pic của kích thước 1000 x 1000.
DiCaprio

1
@JHolta, bạn có biết cách thay đổi chất lượng của ảnh đã chụp không? (theo thứ tự để tăng tốc độ thậm chí nhiều hơn)
DiCaprio

1
Không. Hiện tại, nó chỉ là sao chép hình ảnh trên máy tính để bàn, bất kỳ sự chuyển đổi nào của hình ảnh sẽ mang lại chi phí. Vì vậy, nếu không làm giảm chất lượng thực tế của máy tính để bàn của bạn, bạn đã gặp may với ý tưởng đó. Dù sao, chi phí hiện tại có lẽ là ở phần cuối của Python, nơi chúng tôi cấp phát trước một bộ đệm (có thể được thực hiện trong c), và phần chậm khác nơi PIL đọc trong bộ đệm này, cả hai đều có thể được tối ưu hóa và có thể được đặt ở cuối C của sự vật.
JHolta

6
Điều này hoạt động tốt, nhưng tôi phải #include <X11/Xutil.h>thay thế #include <X11/Xlib.h>. Còn cho biên soạn, tôi đã phải di chuyển -lX11đến cuối như thế này: gcc -shared -O3 -Wall -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c -lX11.
Josh

18

Cái này hoạt động trên X11 và có lẽ cũng trên Windows (ai đó, vui lòng kiểm tra). Cần PyQt4 :

import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')

2
Xin lưu ý về việc cấp phép của PyQt, hạn chế hơn so với Python và Qt. riverbankcomputing.co.uk/software/pyqt/license
user120242

Đó là giải pháp duy nhất chạy trên các bản cài đặt Linux "ngoài hộp" của tôi. Tôi không biết tại sao, nhưng tôi có PyQt4 ở khắp mọi nơi, trong khi thiếu PyWX, PyGtk, ImageGrab. - Cảm ơn :).
Grzegorz Wierzowiecki

Mã vừa hoạt động (trên Windows 7 x64 - Python 2.7.5; phân phối Pythonxy). Jpeg cũng có sẵn (ví dụ như ... lệnh .save ( 'd: /test.jpg', 'jpeg'))
Mohamad Fakih

15

Tôi có một dự án trình bao bọc ( pyscreenshot ) cho scrot, imagemagick, pyqt, wx và pygtk. Nếu bạn có một trong số chúng, bạn có thể sử dụng nó. Tất cả các giải pháp được bao gồm từ cuộc thảo luận này.

Tải về:

easy_install pyscreenshot

Thí dụ:

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: không thể nhập tên gtkpixbuf
tommy.carstensen

nó cho tôi lỗi này:pyscreenshot.tempexport.RunProgError: No protocol specified giblib error: Can't open X display. It *is* running, yeah?" timeout_happened=False>
Jasar Orion

9

Giải pháp đa nền tảng sử dụng 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)

Tham chiếu, với nhận xét, giải thích và ngữ cảnh trong mã python. blog.pythonlibrary.org/2010/04/16/… hoặc blog.pythonlibrary.org/2010/04/16/…
Dân trí


5

Bạn có thể sử dụng cái này

import os
os.system("import -window root screen_shot.png")

Đây là một cách tiếp cận tốt khi bạn có thể lấy hình ảnh từ một chương trình nền, nhưng bạn nên biết rằng nếu chương trình được lấy nét, nó sẽ trả về một ngoại lệ.
Lucas Araújo

3

hơi muộn nhưng không bao giờ dễ dàng một là

import autopy
import time
time.sleep(2)
b = autopy.bitmap.capture_screen()
b.save("C:/Users/mak/Desktop/m.png")

3

Tôi không thể chụp ảnh màn hình trong Linux bằng pyscreenshot hoặc scrot vì đầu ra của pyscreenshot chỉ là một tệp hình ảnh png màn hình đen.

nhưng cảm ơn chúa, có một cách khác rất dễ dàng để chụp ảnh màn hình trong Linux mà không cần cài đặt bất kỳ thứ gì. chỉ cần đặt mã bên dưới vào thư mục của bạn và chạy vớipython demo.py

import os
os.system("gnome-screenshot --file=this_directory.png")

cũng có nhiều tùy chọn có sẵn cho 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

2

Có một gói python cho tính năng Autopy này

Mô-đun bitmap có thể lấy màn hình (bitmap.capture_screen) Nó là đa dạng (Windows, Linux, Osx).



1

đối với ubuntu, công việc này đối với tôi, bạn có thể chụp ảnh màn hình của cửa sổ chọn với cái này:

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()) 

chuyển pixbuf thành mảng

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)

0

Đó là một câu hỏi cũ. Tôi muốn trả lời nó bằng cách sử dụng các công cụ mới.

Hoạt động với python 3 (nên hoạt động với python 2, nhưng tôi chưa thử nghiệm nó) và PyQt5.

Ví dụ làm việc tối thiểu. Sao chép nó vào trình bao python và nhận kết quả.

from PyQt5.QtWidgets import QApplication
app = QApplication([])
screen = app.primaryScreen()
screenshot = screen.grabWindow(QApplication.desktop().winId())
screenshot.save('/tmp/screenshot.png')

bạn có thời gian trung bình để hoàn thành chức năng này không? chỉ quan tâm nếu giá trị nó
DiCaprio

1
@Mrlenny 300 ms (đối với mã đầy đủ), 165 ms (ba dòng mã cuối cùng).
rominf

-3

Thử nó:

#!/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."

3
Cái crap này là gì? Một nửa số lần nhập là vô dụng, có một whilevòng lặp không bao giờ thoát (và sử dụng 1thay vì True), if (pb != None):thay vì chỉ if pb:, có một số chuỗi thô vô nghĩa.
ArtOfWarfare
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.