Examples

Basics

One screenshot per monitor

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

Screenshot of the monitor 1

filename = sct.shot()
print(filename)

A screenshot to grab them all

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

Callback

Screenshot of the monitor 1 with a callback:

from pathlib import Path

import mss


def on_exists(fname: str) -> None:
    """Callback example when we try to overwrite an existing screenshot."""
    file = Path(fname)
    if file.is_file():
        newfile = file.with_name(f"{file.name}.old")
        print(f"{fname}{newfile}")
        file.rename(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)

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

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

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

Added in version 3.1.0.

PNG Compression

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

sct.compression_level = 2

Added in version 3.2.0.

Get PNG bytes, no file output

You can get the bytes of the PNG image:

with mss.mss() as sct:
    # The monitor or screen part to capture
    monitor = sct.monitors[1]  # or a region

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

    # Generate the PNG
    png = mss.tools.to_png(sct_img.rgb, sct_img.size)

Advanced

You can handle data using a custom class:

from typing import Any

import mss
from mss.models import Monitor
from mss.screenshot import ScreenShot


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

    def __init__(self, data: bytearray, monitor: Monitor, **_: Any) -> None:
        self.data = data
        self.monitor = monitor


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

Added in version 3.1.0.

GNU/Linux XShm backend

Select the XShmGetImage backend explicitly and inspect whether it is active or falling back to XGetImage:

from mss.linux.xshmgetimage import MSS as mss

with mss() as sct:
    screenshot = sct.grab(sct.monitors[1])
    print(f"Captured screenshot dimensions: {screenshot.size.width}x{screenshot.size.height}")

    print(f"shm_status: {sct.shm_status.name}")
    if sct.shm_fallback_reason:
        print(f"Falling back to XGetImage because: {sct.shm_fallback_reason}")
    else:
        print("MIT-SHM capture active.")

Added in version 10.2.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() :

from PIL import Image

import mss

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 = f"monitor-{num}.png"
        img.save(output)
        print(output)

Added in version 3.0.0.

Playing with pixels

This is an example using putdata() :

from PIL import Image

import mss

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

Added 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 numpy as np

import mss

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 = np.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(f"fps: {1 / (time.time() - last_time)}")

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

Added 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 numpy as np
from PIL import ImageGrab

import mss


def screen_record() -> int:
    # 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 = np.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() -> int:
    # 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 = np.asarray(sct.grab(mon))
        fps += 1

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

    return fps


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

Added 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: 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: 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 = Queue()

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

Added 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)
    ...

Added in version 3.2.0.