Examples

Basics

One screen shot per monitor

for filename in sct.save():
    print(filename)

Screen shot of the monitor 1

filename = sct.shot()
print(filename)

A screen shot to grab them all

filename = sct.shot(mon=-1, output='fullscreen.png')
print(filename)

Callback

Screen shot of the monitor 1 with a callback:

import os
import os.path

import mss


def on_exists(fname):
    # type: (str) -> None
    """
    Callback example when we try to overwrite an existing screenshot.
    """

    if os.path.isfile(fname):
        newfile = fname + ".old"
        print("{} -> {}".format(fname, newfile))
        os.rename(fname, newfile)


with mss.mss() as sct:
    filename = sct.shot(output="mon-{mon}.png", callback=on_exists)
    print(filename)

Part of the screen

You can capture only a part of the screen:

import mss
import mss.tools


with mss.mss() as sct:
    # The screen part to capture
    monitor = {"top": 160, "left": 160, "width": 160, "height": 135}
    output = "sct-{top}x{left}_{width}x{height}.png".format(**monitor)

    # Grab the data
    sct_img = sct.grab(monitor)

    # Save to the picture file
    mss.tools.to_png(sct_img.rgb, sct_img.size, output=output)
    print(output)

New in version 3.0.0.

Part of the screen of the 2nd monitor

This is an example of capturing some part of the screen of the monitor 2:

import mss
import mss.tools


with mss.mss() as sct:
    # Get information of monitor 2
    monitor_number = 2
    mon = sct.monitors[monitor_number]

    # The screen part to capture
    monitor = {
        "top": mon["top"] + 100,  # 100px from the top
        "left": mon["left"] + 100,  # 100px from the left
        "width": 160,
        "height": 135,
        "mon": monitor_number,
    }
    output = "sct-mon{mon}_{top}x{left}_{width}x{height}.png".format(**monitor)

    # Grab the data
    sct_img = sct.grab(monitor)

    # Save to the picture file
    mss.tools.to_png(sct_img.rgb, sct_img.size, output=output)
    print(output)

New in version 3.0.0.

Use PIL bbox style and percent values

You can use the same value as you would do with PIL.ImageGrab(bbox=tuple(...)). This is an example that uses it, but also using percentage values:

import mss
import mss.tools


with mss.mss() as sct:
    # Use the 1st monitor
    monitor = sct.monitors[1]

    # Capture a bbox using percent values
    left = monitor["left"] + monitor["width"] * 5 // 100  # 5% from the left
    top = monitor["top"] + monitor["height"] * 5 // 100  # 5% from the top
    right = left + 400  # 400px width
    lower = top + 400  # 400px height
    bbox = (left, top, right, lower)

    # Grab the picture
    # Using PIL would be something like:
    # im = ImageGrab(bbox=bbox)
    im = sct.grab(bbox)  # type: ignore

    # Save it!
    mss.tools.to_png(im.rgb, im.size, output="screenshot.png")

New in version 3.1.0.

PNG Compression

You can tweak the PNG compression level (see zlib.compress() for details):

sct.compression_level = 2

New in version 3.2.0.

Advanced

You can handle data using a custom class:

import mss


class SimpleScreenShot:
    """
    Define your own custom method to deal with screen shot raw data.
    Of course, you can inherit from the ScreenShot class and change
    or add new methods.
    """

    def __init__(self, data, monitor, **kwargs):
        self.data = data
        self.monitor = monitor


with mss.mss() as sct:
    sct.cls_image = SimpleScreenShot  # type: ignore
    image = sct.grab(sct.monitors[1])
    # ...

New in version 3.1.0.

PIL

You can use the Python Image Library (aka Pillow) to do whatever you want with raw pixels. This is an example using frombytes():

import mss
from PIL import Image


with mss.mss() as sct:
    # Get rid of the first, as it represents the "All in One" monitor:
    for num, monitor in enumerate(sct.monitors[1:], 1):
        # Get raw pixels from the screen
        sct_img = sct.grab(monitor)

        # Create the Image
        img = Image.frombytes("RGB", sct_img.size, sct_img.bgra, "raw", "BGRX")
        # The same, but less efficient:
        # img = Image.frombytes('RGB', sct_img.size, sct_img.rgb)

        # And save it!
        output = "monitor-{}.png".format(num)
        img.save(output)
        print(output)

New in version 3.0.0.

Playing with pixels

This is an example using putdata():

import mss
from PIL import Image


with mss.mss() as sct:
    # Get a screenshot of the 1st monitor
    sct_img = sct.grab(sct.monitors[1])

    # Create an Image
    img = Image.new("RGB", sct_img.size)

    # Best solution: create a list(tuple(R, G, B), ...) for putdata()
    pixels = zip(sct_img.raw[2::4], sct_img.raw[1::4], sct_img.raw[0::4])
    img.putdata(list(pixels))

    # But you can set individual pixels too (slower)
    """
    pixels = img.load()
    for x in range(sct_img.width):
        for y in range(sct_img.height):
            pixels[x, y] = sct_img.pixel(x, y)
    """

    # Show it!
    img.show()

New in version 3.0.0.

OpenCV/Numpy

See how fast you can record the screen. You can easily view a HD movie with VLC and see it too in the OpenCV window. And with __no__ lag please.

import time

import cv2
import mss
import numpy


with mss.mss() as sct:
    # Part of the screen to capture
    monitor = {"top": 40, "left": 0, "width": 800, "height": 640}

    while "Screen capturing":
        last_time = time.time()

        # Get raw pixels from the screen, save it to a Numpy array
        img = numpy.array(sct.grab(monitor))

        # Display the picture
        cv2.imshow("OpenCV/Numpy normal", img)

        # Display the picture in grayscale
        # cv2.imshow('OpenCV/Numpy grayscale',
        #            cv2.cvtColor(img, cv2.COLOR_BGRA2GRAY))

        print("fps: {}".format(1 / (time.time() - last_time)))

        # Press "q" to quit
        if cv2.waitKey(25) & 0xFF == ord("q"):
            cv2.destroyAllWindows()
            break

New in version 3.0.0.

FPS

Benchmark

Simple naive benchmark to compare with Reading game frames in Python with OpenCV - Python Plays GTA V:

import time

import cv2
import mss
import numpy


def screen_record():
    try:
        from PIL import ImageGrab
    except ImportError:
        return 0

    # 800x600 windowed mode
    mon = (0, 40, 800, 640)

    title = "[PIL.ImageGrab] FPS benchmark"
    fps = 0
    last_time = time.time()

    while time.time() - last_time < 1:
        img = numpy.asarray(ImageGrab.grab(bbox=mon))
        fps += 1

        cv2.imshow(title, cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
        if cv2.waitKey(25) & 0xFF == ord("q"):
            cv2.destroyAllWindows()
            break

    return fps


def screen_record_efficient():
    # 800x600 windowed mode
    mon = {"top": 40, "left": 0, "width": 800, "height": 640}

    title = "[MSS] FPS benchmark"
    fps = 0
    sct = mss.mss()
    last_time = time.time()

    while time.time() - last_time < 1:
        img = numpy.asarray(sct.grab(mon))
        fps += 1

        cv2.imshow(title, img)
        if cv2.waitKey(25) & 0xFF == ord("q"):
            cv2.destroyAllWindows()
            break

    sct.close()
    return fps


print("PIL:", screen_record())
print("MSS:", screen_record_efficient())

New in version 3.0.0.

Multiprocessing

Performances can be improved by delegating the PNG file creation to a specific worker. This is a simple example using the multiprocessing inspired by the TensorFlow Object Detection Introduction project:

from multiprocessing import Process, Queue

import mss
import mss.tools


def grab(queue):
    # type: (Queue) -> None

    rect = {"top": 0, "left": 0, "width": 600, "height": 800}

    with mss.mss() as sct:
        for _ in range(1_000):
            queue.put(sct.grab(rect))

    # Tell the other worker to stop
    queue.put(None)


def save(queue):
    # type: (Queue) -> None

    number = 0
    output = "screenshots/file_{}.png"
    to_png = mss.tools.to_png

    while "there are screenshots":
        img = queue.get()
        if img is None:
            break

        to_png(img.rgb, img.size, output=output.format(number))
        number += 1


if __name__ == "__main__":
    # The screenshots queue
    queue = Queue()  # type: Queue

    # 2 processes: one for grabing and one for saving PNG files
    Process(target=grab, args=(queue,)).start()
    Process(target=save, args=(queue,)).start()

New in version 5.0.0.

BGRA to RGB

Different possibilities to convert raw BGRA values to RGB:

def mss_rgb(im):
    """ Better than Numpy versions, but slower than Pillow. """
    return im.rgb


def numpy_flip(im):
    """ Most efficient Numpy version as of now. """
    frame = numpy.array(im, dtype=numpy.uint8)
    return numpy.flip(frame[:, :, :3], 2).tobytes()


def numpy_slice(im):
    """ Slow Numpy version. """
    return numpy.array(im, dtype=numpy.uint8)[..., [2, 1, 0]].tobytes()


def pil_frombytes(im):
    """ Efficient Pillow version. """
    return Image.frombytes('RGB', im.size, im.bgra, 'raw', 'BGRX').tobytes()


with mss.mss() as sct:
    im = sct.grab(sct.monitors[1])
    rgb = pil_frombytes(im)
    ...

New in version 3.2.0.