Outdated Drivers Are Slowing You Down
One free scan finds every outdated or missing driver and matches the right update for your exact hardware.Free scan · exact hardware matchWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallSome links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Use Pillow’s ImageGrab.grab() to capture the screen, then call getpixel((x, y)) on the returned image. For a guaranteed three-channel result, normalize the capture to RGB first:
from PIL import ImageGrab
image = ImageGrab.grab()
r, g, b = image.convert("RGB").getpixel((100, 100))
print(r, g, b)
The coordinate is measured in the image returned by grab(), and the tuple format follows that image’s mode. macOS captures are documented as RGBA, while other platforms normally return RGB. See the Pillow ImageGrab documentation and Image documentation for the API definitions.
Minimal example: capture one pixel
Install Pillow into the Python environment that will run the script:
python -m pip install --upgrade Pillow
Then capture the display and read one point:
from PIL import ImageGrab
image = ImageGrab.grab()
pixel = image.getpixel((100, 100))
print("mode:", image.mode)
print("pixel:", pixel)
getpixel((x, y)) accepts a two-item coordinate and returns the value at that location. On an RGB image, pixel is a three-item tuple in red, green, blue order. On an RGBA image it contains red, green, blue and alpha. The image is already in memory after grab(); no separate save operation is required.
#1 Best Overall
Why the tuple sometimes has three values and sometimes four
Pillow’s pixel APIs are mode-dependent. Inspect image.mode before unpacking the result, especially when the script runs on more than one operating system.
| Image mode | Typical value from getpixel() |
Use it when |
|---|---|---|
RGB |
(red, green, blue) |
You need three color channels and no alpha channel. |
RGBA |
(red, green, blue, alpha) |
You need transparency information as well as color. |
P |
A palette index, not direct RGB channels | The image uses an indexed color palette; convert before reading channel values. |
The ImageGrab reference documents RGB output on Windows and Linux and RGBA output on macOS. A palette-mode image returns an index into its palette, so treating that integer as a red value produces the wrong result. Pillow’s concepts documentation explains how modes determine pixel representations.
Normalize to exactly three channels
If dropping transparency is acceptable, convert the image before calling getpixel():
from PIL import ImageGrab
image = ImageGrab.grab()
rgb_image = image.convert("RGB")
r, g, b = rgb_image.getpixel((100, 100))
print(r, g, b)
This also handles a palette-mode image by resolving its palette to direct RGB channels. Conversion discards alpha, so preserve the original mode when transparency matters:
Rank #2
from PIL import ImageGrab
image = ImageGrab.grab()
value = image.getpixel((100, 100))
if image.mode == "RGBA":
r, g, b, a = value
print(f"red={r}, green={g}, blue={b}, alpha={a}")
else:
r, g, b = image.convert("RGB").getpixel((100, 100))
print(f"red={r}, green={g}, blue={b}")
Coordinates, bounding boxes and screen layout
Without arguments, ImageGrab.grab() captures the available desktop and the returned image’s coordinate system starts at its own top-left corner. The point (0, 0) is therefore the top-left pixel of that returned image, not necessarily a coordinate you should assume for every multi-monitor arrangement.
Use bbox to limit the capture:
from PIL import ImageGrab
# Capture a rectangle from the desktop.
region = ImageGrab.grab(bbox=(200, 120, 1000, 720))
# Coordinates are local to `region`, not the original desktop.
pixel = region.getpixel((10, 20))
print(pixel)
After applying a bounding box, index the cropped image with local coordinates. In this example, (10, 20) means 10 pixels from the crop’s left edge and 20 pixels from its top edge. Check the dimensions before indexing dynamically chosen points:
x, y = 10, 20
if 0 <= x < region.width and 0 <= y < region.height:
print(region.getpixel((x, y)))
else:
raise ValueError(f"Point {(x, y)} is outside {region.size}")
On Windows, the API also documents an all_screens option for including all monitors. Its result and coordinate range depend on the desktop arrangement, so inspect image.size and test a known point on the target display rather than hard-coding assumptions.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
from PIL import ImageGrab
image = ImageGrab.grab(all_screens=True)
print(image.size, image.mode)
print(image.getpixel((100, 100)))
macOS Retina captures and Pillow 12.3.0
Retina displays can produce a capture at roughly twice the logical screen scale. Pillow 12.3.0 added scale_down=True to request a 1x image on macOS. That option is documented in the ImageGrab reference and was added in the Pillow 12.3.0 release notes, dated 2026-07-01.
from PIL import ImageGrab
# Requires Pillow 12.3.0 or newer.
image = ImageGrab.grab(scale_down=True)
print(image.size, image.mode)
print(image.convert("RGB").getpixel((100, 100)))
Check the installed Pillow version before using this keyword. If the argument is rejected, upgrade Pillow or omit it and account for the captured image’s actual dimensions when translating logical UI coordinates to pixel coordinates. Do not assume that a point from a Retina screenshot maps one-to-one to a point measured in a window toolkit.
A complete helper function
This helper returns either RGB or RGBA while making the coordinate frame explicit:
from PIL import ImageGrab
def pixel_at(x, y, *, bbox=None, keep_alpha=False, scale_down=False):
"""Return a pixel from an ImageGrab capture.
x and y are local to the captured image. With bbox, they are not
original desktop coordinates.
"""
options = {}
if bbox is not None:
options["bbox"] = bbox
if scale_down:
options["scale_down"] = True # Pillow 12.3.0+
image = ImageGrab.grab(**options)
if not (0 <= x < image.width and 0 <= y < image.height):
raise ValueError(
f"({x}, {y}) is outside the captured image {image.size}"
)
if keep_alpha and image.mode == "RGBA":
return image.getpixel((x, y))
return image.convert("RGB").getpixel((x, y))
print(pixel_at(100, 100))
print(pixel_at(10, 20, bbox=(200, 120, 1000, 720)))
print(pixel_at(100, 100, keep_alpha=True, scale_down=True))
Only pass scale_down=True when the installed Pillow version supports it and the capture is running on a platform where the option is meaningful. The keep_alpha branch deliberately preserves RGBA only when that mode is present; otherwise the helper returns normalized RGB.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Repair Windows errors before they cause bigger problemsFix Now →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Platform prerequisites and capture limitations
- macOS: expect RGBA output according to the documentation, and grant the operating system’s screen-recording permission to the application running Python. Retina scaling can change the pixel dimensions.
- Windows: use
all_screens=Truewhen the capture must include every monitor. The desktop’s monitor arrangement determines the resulting coordinate space. - Linux: when the default X11 display cannot provide a capture, Pillow may fall back to available screenshot utilities. Those utilities, a graphical session and their permissions must exist on the host; a headless machine is not guaranteed to work.
These are environment-dependent behaviors, not guarantees that every workstation has a display server, permission or fallback utility configured.
Reading many pixels
For a single point, getpixel() is the direct API. If you need a large grid or every pixel, treat that as a separate bulk-processing problem: capture once, avoid calling ImageGrab.grab() for every point, and choose an image-processing representation suited to your workload. The official references used here do not publish a universal speed figure, so do not rely on an assumed pixels-per-second rate. Measure on the operating system, display scale and image size that your application will actually use.
Troubleshooting common failures
ModuleNotFoundError: No module named 'PIL'
Install the Pillow package in the same interpreter that launches the script:
python -m pip install --upgrade Pillow
The package is named Pillow, but the import namespace is PIL. In a virtual environment, activate that environment before installing.
Recommended Free Tools
The capture raises an operating-system or display error
Run the program inside an interactive graphical session, verify screen-capture permissions, and confirm that the Linux display server or required screenshot utility is available. A remote shell or headless container may not expose a capturable desktop.
Best Value
The result is four values instead of three
Print image.mode. macOS captures are documented as RGBA. Use image.convert("RGB") when alpha is not needed, or unpack all four values and retain the alpha channel.
The result is a single integer
The image may be palette mode (P). Convert it to RGB before reading channels:
rgb = image.convert("RGB")
red, green, blue = rgb.getpixel((x, y))
The sampled color is from the wrong place
Check whether a bbox was used. Coordinates passed to the cropped image are local to that crop. On Retina displays, compare logical UI coordinates with image.size and consider scale_down=True on Pillow 12.3.0 or newer.
IndexError or an invalid coordinate
Ensure 0 <= x < image.width and 0 <= y < image.height. Print image.size immediately after capture; a bounding box or display scale may have produced a smaller image than expected.
Or skip the browser setup:
If your real goal is a clean image of a public web page rather than a pixel from your local desktop, ScreenshotNeo provides a website screenshot API. It is not a replacement for ImageGrab when you need an application window or an arbitrary desktop coordinate, but it avoids installing and operating a browser automation stack.
One GET request returns a PNG, JPEG, WebP or PDF. The API accepts a URL and can remove cookie-consent banners, newsletter popups and chat widgets before capture. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads and cache hits are not billed; response headers identify the page verdict and billing status.
Python:
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)
cURL:
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
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}`);
See the ScreenshotNeo API documentation for request options. It also offers an MCP server with take_screenshot, get_page_info and capture_pdf tools for Claude, Cursor and other MCP clients. Every plan includes the features; the free plan provides 1,000 shots per month without a card, and paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account to try it.
Practical checklist
- Install Pillow and import
ImageGrabfromPIL. - Capture once with
ImageGrab.grab(), optionally supplyingbbox,all_screensor a supported scaling option. - Print
image.modeandimage.sizewhile developing. - Use
getpixel((x, y))with coordinates local to the returned image. - Convert to RGB for a guaranteed three-channel tuple, or preserve RGBA when alpha matters.
- Validate bounds and test on each target operating system and display scale.
Frequently Asked Questions
Does ImageGrab.grab() automatically sample the color under the mouse pointer?
No. It captures an image; your code must supply the pixel coordinate to getpixel(). Pointer tracking, if required, is a separate operating-system or GUI-toolkit task.
Can ScreenshotNeo capture a local desktop window like ImageGrab?
No. ScreenshotNeo accepts a web URL and captures that page. Use ImageGrab for local desktop content and ScreenshotNeo when the input is a webpage.
Quick Recap
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.

