使用GTK截取屏幕子区域的截图

2 投票
1 回答
1095 浏览
提问于 2025-04-17 08:24

我正在尝试在Ubuntu 10.04上使用Python只截取屏幕的一部分。

这是我的代码(假设IMAGE_GRAB是False):

def screenshot_roi(regions):
    if IMAGE_GRAB:
        return map(ImageGrab.grab, regions)
    else:
        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])
        src = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
        results = []
        for roi in regions:
            if not roi:
                results.append(None)
                continue
            x,y,width,height = roi
            dst = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,width,height)
            src.copy_area(x,y,width,height,dst,0,0)
            im = PIL.Image.fromstring("RGB", (width, height), dst.get_pixels())
            results.append(im)
        return results

这里没有什么特别的。它会把整个可绘制区域捕捉到一个像素缓冲区中,然后再对每个像素缓冲区进行裁剪,最后把它转换成需要的PIL对象。

这是主要的代码部分:

def main():
    regions = [(845, 219, 248, 82), (1101, 243, 109, 59), 
               (1213, 245, 66, 57), (1281, 245, 74, 58)]    
    images = screenshot_roi(regions)
    for i,roi in enumerate(images):
        if roi:
            roi.save('%d.png' % i)

if __name__ == '__main__':
    main()

不过,生成的图像(除了第一张)都有一些步幅问题:

1: 这里插入图片描述 2: 这里插入图片描述 3: 这里插入图片描述 4: 这里插入图片描述

现在,如果我在PIL中进行裁剪,一切都能正常工作:

def screenshot_roi(regions):
    if IMAGE_GRAB:
        return map(ImageGrab.grab, regions)
    else:
        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])
        src = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
        entire_im = PIL.Image.fromstring("RGB", sz, src.get_pixels())
        results = []
        for roi in regions:
            if not roi:
                results.append(None)
                continue
            x,y,width,height = roi
            crop = entire_im.crop((x,y,x+width,y+height))
            crop.load()
            results.append(crop)
        return results

不过,我不想这样做,因为从GTK转换到PIL的过程相当耗费资源。这样做很浪费,因为我把整个图像都转换了,只是为了获取一些小的子区域。

有没有人能建议一下为什么GTK版本会出现步幅错误?

编辑

有效的源代码:

x,y,w,h = region
win = gtk.gdk.get_default_root_window()
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,w,h)
src = pb.get_from_drawable(win,win.get_colormap(),x,y,0,0,w,h)
im = PIL.Image.frombuffer('RGB', (w,h), src.get_pixels(), 'raw', 
                'RGB', src.get_rowstride(), 1)
return im

1 个回答

4

我觉得这是因为get_pixels这个函数返回的数据是按照它在内存中存储的方式来的。你需要注意的是,一行数据的末尾可能会有一些额外的空间,这主要是为了提高性能(因为内存对齐的要求)。

可以看看GdkPixbuf结构:这里面有个很重要的信息叫做rowstride,它表示一行的开始和下一行的开始之间的字节数。这个值等于一行中的数据字节数加上额外的填充字节数。

要注意的是最后一行是没有填充的

撰写回答