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.