Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.

OpenCV does not capture your desktop by itself. Use a screen-capture library such as MSS to obtain the pixels, convert the result to a NumPy array in OpenCV’s BGR order, and then display or process that array. The following MSS example captures the rectangle whose upper-left corner is (100, 80) and whose size is 640 × 400:

import cv2
import mss
from mss.models import Region

region = Region(left=100, top=80, width=640, height=400)

with mss.MSS() as sct:
    shot = sct.grab(region)
    frame = shot.to_numpy(channels="BGR")

    cv2.imshow("Captured region", frame)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

left and top are screen coordinates; width and height are dimensions. MSS supplies the screenshot, NumPy holds the pixels, and OpenCV performs the computer-vision work.

What OpenCV contributes to a screen capture

A desktop screenshot is a stream of pixels obtained from the operating system. OpenCV is the processing layer: it can resize, crop, convert to grayscale, detect edges, threshold, compare frames, or write an image file. A separate capture API must first obtain those pixels.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

MSS and PyAutoGUI are two practical choices. MSS returns a screenshot object that can be converted directly to a NumPy array. PyAutoGUI returns an image object from its screenshot() function; convert that image to an array before passing it to OpenCV.

Install the Python packages

Install OpenCV, NumPy, and MSS in the environment that will run the script:

python -m pip install opencv-python numpy mss

Install PyAutoGUI only if you want the alternative workflow:

python -m pip install pyautogui

Run the examples in a normal desktop session. Operating-system permissions, headless behavior, protected surfaces, and high-DPI coordinate mapping vary by platform and are not uniform guarantees of these libraries.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Capture one rectangular area with MSS

Use a dimension-based region

The documented Region form is explicit and easy to read:

import cv2
import mss
from mss.models import Region

region = Region(left=100, top=80, width=640, height=400)

with mss.MSS() as sct:
    shot = sct.grab(region)
    frame = shot.to_numpy(channels="BGR")

    cv2.imshow("Captured region", frame)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

The capture object is opened with a context manager and closed automatically. shot is MSS’s screenshot object; frame is the NumPy array consumed by OpenCV.

Save the result instead of displaying it

For a file, replace the window calls with cv2.imwrite():

import cv2
import mss
from mss.models import Region

region = Region(left=100, top=80, width=640, height=400)

with mss.MSS() as sct:
    frame = sct.grab(region).to_numpy(channels="BGR")
    ok = cv2.imwrite("area.png", frame)

if not ok:
    raise RuntimeError("OpenCV could not write area.png")

OpenCV’s image-writing functions infer the format from the filename extension. Check the Boolean return value when a failed write must be detected programmatically.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use a dictionary or a PIL-style box

MSS also accepts a dictionary region. Its keys use the same dimension convention:

region = {"left": 100, "top": 80, "width": 640, "height": 400}

It also accepts a PIL-style four-value box, but this convention is different: (left, top, right, bottom), not width and height. For example, (100, 80, 740, 480) describes the same 640-by-400 rectangle. Mixing these conventions is a common cause of unexpectedly large, small, or misplaced captures.

Keep the capture open for repeated frames

For video-like processing, create one MSS object outside the loop and grab the same region on each iteration:

import cv2
import mss
from mss.models import Region

region = Region(left=100, top=80, width=640, height=400)

with mss.MSS() as sct:
    while True:
        shot = sct.grab(region)
        frame = shot.to_numpy(channels="BGR")

        cv2.imshow("Live region", frame)
        key = cv2.waitKey(1) & 0xFF
        if key == ord("q"):
            break

cv2.destroyAllWindows()

Press q to stop. Keeping the capture object open avoids repeatedly constructing it. This example demonstrates the loop structure; it is not a speed measurement. The useful capture rate depends on the operating system, display setup, region size, and the processing performed inside the loop.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why the BGR conversion matters

MSS documents ScreenShot.to_numpy() with selectable channel ordering. Request channels="BGR" when the array will go to OpenCV. OpenCV expects colors in BGR order, while RGB data interpreted as BGR swaps red and blue. Grayscale operations may hide the mistake, but color display, masking, and channel-specific analysis will not.

If you already have an RGB array, convert it explicitly:

bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)

Do not perform this conversion on an array that MSS has already returned as BGR.

Capture a selected monitor or a region spanning monitors

Understand MSS monitor entries

MSS exposes monitor geometry. Entry 0 represents the complete virtual desktop; entries after zero represent individual displays. Each record supplies its left, top, width, and height.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
import mss

with mss.MSS() as sct:
    for index, monitor in enumerate(sct.monitors):
        print(index, monitor)

To capture a rectangle relative to a chosen monitor, add that monitor’s origin to the local coordinates:

import cv2
import mss
from mss.models import Region

monitor_index = 1
local_left, local_top = 40, 60
width, height = 800, 500

with mss.MSS() as sct:
    monitor = sct.monitors[monitor_index]
    region = Region(
        left=monitor["left"] + local_left,
        top=monitor["top"] + local_top,
        width=width,
        height=height,
    )
    frame = sct.grab(region).to_numpy(channels="BGR")

cv2.imwrite("monitor-area.png", frame)

A display positioned left of or above the primary display can have negative virtual-desktop coordinates. Use the values reported by MSS rather than assuming every monitor starts at a positive coordinate.

Process the captured pixels with OpenCV

Once frame is a BGR NumPy array, ordinary OpenCV operations apply. This example creates a grayscale image and an edge map while showing both:

import cv2
import mss
from mss.models import Region

region = Region(left=100, top=80, width=640, height=400)

with mss.MSS() as sct:
    frame = sct.grab(region).to_numpy(channels="BGR")
    gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    edges = cv2.Canny(gray, 50, 150)

cv2.imshow("Original", frame)
cv2.imshow("Edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()

The capture boundary and the computer-vision pipeline are separate: change the rectangle without changing the processing code, or feed a saved image into the same processing functions.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use PyAutoGUI when its image API fits better

PyAutoGUI documents a region screenshot call whose tuple is (left, top, width, height):

import cv2
import numpy as np
import pyautogui

image = pyautogui.screenshot(region=(100, 80, 640, 400))
rgb = np.array(image)
bgr = cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR)

cv2.imshow("PyAutoGUI region", bgr)
cv2.waitKey(0)
cv2.destroyAllWindows()

The returned image object must be converted to an array before OpenCV processing. Confirm its channel order and convert RGB to BGR as shown. Do not assume that PyAutoGUI’s image object has MSS’s to_numpy(channels=...) method.

Which API should you choose?

Consideration MSS PyAutoGUI
Capture result MSS screenshot object, then NumPy via to_numpy() Image object from screenshot(), then NumPy conversion
Documented region form Region or dictionary: left, top, width, height; also PIL-style left, top, right, bottom Tuple: left, top, width, height
OpenCV color hand-off Request BGR directly with to_numpy(channels="BGR") Check the image’s channels and convert RGB to BGR when needed
Performance verdict No universal winner is established here; measure your own workload if throughput matters

Choose based on the API and coordinate convention that make your application least error-prone. A workload-specific benchmark is more meaningful than a general claim about speed.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshoot common failures

The window is black, empty, or never appears

  • Confirm that the script is running in an interactive desktop session rather than an unsupported headless context.
  • Check operating-system screen-recording or capture permissions. The exact setting and behavior differ by platform.
  • Verify that the selected rectangle lies inside the virtual desktop and that its width and height are positive.
  • For protected or DRM-controlled content, capture availability can be restricted by the operating system or application.

The image has red and blue reversed

The array is probably RGB but is being interpreted as BGR. Request BGR from MSS or apply cv2.cvtColor(rgb, cv2.COLOR_RGB2BGR) exactly once.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The rectangle is offset or the dimensions are wrong

  • For Region, a dictionary, and PyAutoGUI, use left, top, width, height.
  • For the PIL-style MSS box, use left, top, right, bottom.
  • On multiple monitors, inspect sct.monitors and include the selected monitor’s reported origin. Negative origins are valid.
  • High-DPI scaling can make application-reported coordinates differ from physical pixel coordinates. Validate the mapping on the target operating system instead of assuming a single rule.

cv2.imshow() closes immediately

A HighGUI window needs an event wait. Use cv2.waitKey(0) for a still image or a short delay such as cv2.waitKey(1) inside a loop, then call cv2.destroyAllWindows().

The saved file is missing or unreadable

Check the Boolean result of cv2.imwrite(), use a writable absolute or verified working directory, and choose a filename extension supported by the OpenCV build.

The loop consumes too many resources

Capture only the rectangle you need, keep one MSS object open, and avoid expensive processing when a frame has not changed. If you need a specific frame rate, add your own timing and measure on the deployment machine; the documented examples do not establish a universal capture speed.

Or skip the browser setup

If your actual target is a public web page rather than pixels from your local desktop, ScreenshotNeo provides a website screenshot API. It is not a replacement for capturing an arbitrary local monitor area, but it removes the need to configure a browser for URL captures. Before the shot it accepts cookie or consent banners and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each cleanup step can be disabled. Bot checks or CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and the response identifies the page verdict and billing status in X-Page-Verdict and X-Billed headers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

See the ScreenshotNeo API documentation for all options. A one-call cURL request is:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

The equivalent Python request is:

import requests

r = requests.get(
    "https://api.screenshotneo.com/v1/shot",
    params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
    timeout=90,
)
r.raise_for_status()
open("shot.webp", "wb").write(r.content)

In Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot failed: ${res.status}`);
const fs = await import('node:fs/promises');
await fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer()));

ScreenshotNeo also offers an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. Every feature is on every plan: 1,000 screenshots per month are free with no card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try a URL capture.

Frequently Asked Questions

Can these libraries capture DRM-protected video or secure application surfaces?

There is no universal guarantee. Protected-content policies are enforced by the operating system or application and can produce a blank or restricted capture; test the exact environment and obtain permission where required.

How should I choose coordinates when a window moves?

Recalculate the rectangle from the window’s current position before each grab, or capture a stable monitor-relative area. MSS and PyAutoGUI accept coordinates; they do not, by themselves, identify a moving application window.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The Bottom Line

Use MSS to capture the rectangle, request BGR data, and let OpenCV process the resulting NumPy array. Keep the capture object open for loops, treat monitor origins and coordinate conventions explicitly, and use PyAutoGUI when its image API is a better fit.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.