Some links on this page are affiliate links: if you buy through them we may earn a commission, at no extra cost to you.
Decode the Base64 payload into bytes, wrap those bytes in io.BytesIO, and open the stream with Pillow. If the input is a data URI such as data:image/png;base64,..., remove everything through the first comma before decoding. Call load() if you need Pillow to read the image pixels before the stream goes away.
Convert Base64 to a Pillow image in memory
Python’s base64.b64decode() returns bytes; it does not create an image object. Pillow’s Image.open() can read image bytes through a file-like object, and io.BytesIO provides that bridge without writing a temporary file. The function below accepts either raw Base64 or a data URI and rejects invalid Base64 rather than silently ignoring unexpected characters.
import base64
import binascii
import io
from PIL import Image
def image_from_base64(value: str) -> Image.Image:
"""Return a Pillow Image from raw Base64 or a data URI."""
payload = value.split(",", 1)[1] if "," in value else value
try:
raw = base64.b64decode(payload, validate=True)
except (binascii.Error, ValueError) as exc:
raise ValueError("Invalid Base64 image data") from exc
image = Image.open(io.BytesIO(raw))
image.load()
return image
# Examples:
image = image_from_base64(encoded_value)
print(image.format, image.size, image.mode)
image.save("output.png")
Install Pillow in the Python environment where the script runs with python -m pip install Pillow. The standard-library base64 and io modules need no separate installation.
What the function does
- Separate the payload. A data URI carries metadata before a comma and the Base64 payload after it. The code splits once, so additional commas in the payload are not treated as more separators. A raw Base64 string is passed through unchanged.
- Decode strictly.
validate=Truerejects characters outside the Base64 alphabet instead of discarding them. Missing or incorrect padding is still an error; the function reports it as invalid input. - Make a binary stream.
io.BytesIO(raw)lets Pillow read the decoded bytes using its file-like input interface. - Open and load.
Image.open()identifies a supported image format from the bytes. It is lazy: callingload()reads the image data before the function returns.
Handle raw Base64 and data URIs correctly
First identify which form your caller supplies. A value copied from a browser may be a complete data URI; an API field may contain only the encoded payload. Passing the entire data URI into strict Base64 decoding fails because the prefix is not Base64 image data.
#1 Best Overall
- 16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting
- Optical Zoom: 4x optical zoom with a 27mm wide angle lens for flexible framing indoors or outdoors
- Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
- Memory Support: Works with Class 10 SD, SDHC, or SDXC cards up to 512GB
- LCD Screen and Battery: 2.7in LCD screen with 2 AA alkaline batteries for convenient on-the-go use
| Input form | What to decode | Example |
|---|---|---|
| Raw Base64 | The whole string | iVBORw0KGgo... |
| Data URI | Only the content after the first comma | data:image/png;base64,iVBORw0KGgo... |
The MIME-type label in a data URI can be useful context, but it is not a conversion instruction. Pillow identifies the image format from the decoded file bytes. If you want to accept only data URIs in a particular application, check the header before extracting the payload rather than treating any comma-containing string as a valid data URI.
Minimal code for known-clean input
If the value is already a clean raw Base64 string and you do not need explicit validation, the essential in-memory operation is:
import base64
import io
from PIL import Image
img = Image.open(io.BytesIO(base64.b64decode(encoded_image)))
For a known data URI, separate its header explicitly:
The Tool Desk
Outbyte PC Repair FREERepair Windows errors before they cause bigger problemsFix Now →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #2
- 16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting
- Optical Zoom: 5x optical zoom with a 28mm wide angle lens for flexible framing indoors or outdoors
- Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
- Memory Support: Works with Class 10 SD, SDHC, or SDXC cards up to 512GB
- LCD Screen and Battery: 2.7in LCD screen and a rechargeable lithium-ion battery for on-the-go use
header, encoded = data_uri.split(",", 1)
img = Image.open(io.BytesIO(base64.b64decode(encoded, validate=True)))
Use the complete function when inputs can be malformed or arrive in either form. The minimal versions leave input-shape and error handling to the caller.
Save, preserve, or convert the decoded image
Decoding and opening do not turn one image format into another. The opened Pillow image retains its source format and mode; the filename extension you choose is not itself a conversion. To save an image, choose a matching extension and, when needed, specify the format explicitly:
img.save("output.png")
# Or specify the format explicitly:
img.save("output.webp", format="WEBP")
If the desired output format requires a different color mode, convert the mode before saving. For example, JPEG does not support an alpha channel, so an image with transparency needs an explicit background-compositing decision before it can be saved as JPEG. Do not assume that changing .png to .jpg changes the image encoding.
Rank #3
- Latest Digital Camera Built-in Fill Light : This compact digital camera is paired with a powerful CMOS processor and image stabilization to help you take & record the most exciting moments in 44 MP quality images & FHD 1080P quality videos anywhere, anytime. Plus, there is also a built-in fill light to help you take high quality pictures even in low light&dark settings, making this the perfect camera for all indoors/outdoors situations.
- Long-Lasting Battery Life & 16X Digital Zoom :This point and shoot camera will retain its battery charge even after long use. The controls and functions are easy to operate making this the perfect choice for children, teens and younger. This kids camera supports 16x digital zoom, you can zoom in or out the subject by pressing the W/T button for taking still photos to zoom in or out on distant objects and capture all the details you need.
- Multifunctional & Portable Digital Camera: This cheap digital camera is slim enough to fit in your pocket. You'll easily be able to take it with you on all your indoor/outdoor activities and adventures and ideal for beginners, children and teenagers. This kids digital camera is equipped with 20 filters, anti-shaking, self-timer, continuous shooting, date stamp, time-lapse recording, smile capture, internal MIC and speaker (recording sound videos), great for your daily photography needs.
- WEBCAM & PAUSE FUNCTION : More than just a FHD 1080p digital camera, it also works as a webcam for video calls and vlogging. Connect the camera to the computer, press shutter and power button at the same time and the camera will automatically turn on webcam mode for all your video calling and live streaming needs. The pause function allows you to pause when seeing playback videos.
- A Must Have Photography Device : This digital camera with SD card made from high-quality materials, this retro camera is safe and durable. Perfect for all ages to develop & improve their photographic abilities and observation skills. Our dedicated and experienced 24/7 support team is available for all after purchase troubleshooting, questions and technical help.
Inspect img.format, img.mode, and img.size to see what Pillow opened. Format describes the recognized file type, mode describes the pixel representation, and size is the image dimensions. If you need a durable artifact, use save(); otherwise, the image can be used directly in memory.
Keep the stream alive when Pillow is loading lazily
Image.open() is lazy: opening identifies the image, but pixel data may be read only when needed. In the function above, image.load() runs while the BytesIO stream is still alive. This is useful when returning the image for later processing, because it avoids relying on a stream that has already been closed.
If you omit load(), keep the stream available for as long as the image may need to read from it. This matters particularly when opening an image inside a with block and then using it after that block. Animated images also need care: loading one frame does not necessarily materialize every frame. Keep the image and its backing stream available while accessing additional frames.
Rank #4
- 16MP Sensor: Captures detailed photos with a CMOS sensor for everyday shooting
- Optical Zoom: 5x optical zoom with a 28mm wide angle lens for flexible framing indoors or outdoors
- Full HD Video: Records 1080p video for travel clips, family moments, or simple vlogging
- Memory Support: Works with Class 10 SD, SDHC, or SDXC cards up to 512GB
- LCD Screen and Battery: 2.7in LCD screen and a rechargeable lithium-ion battery for on-the-go use
Choose strict decoding or URL-safe Base64 deliberately
The decoder call is part of input validation. base64.b64decode(payload, validate=True) is a useful default when the sender is expected to provide standard Base64: unexpected characters are rejected, and incorrect padding raises an error. It does not repair truncated input.
Some systems explicitly use the URL-safe Base64 alphabet. If the sender confirms that encoding, use Python’s base64.urlsafe_b64decode() rather than assuming standard Base64. Follow the sender’s padding convention; do not switch decoder types just because a standard decode failed. Similarly, remove whitespace only when you know it was introduced by formatting or transport. With strict validation, whitespace is rejected rather than silently discarded.
Do these 3 things before closing this tab:
1Clear out junk files and repair common Windows errors2Fix the driver behind crashes, sound loss and screen glitches3Repair Windows errors before they cause bigger problemsTroubleshoot decoding and image errors
| Symptom | Likely cause | Fix |
|---|---|---|
binascii.Error: Incorrect padding |
The value is truncated or required = padding is missing. |
Obtain the complete value from its source. Do not invent padding or bytes to conceal a truncated payload. |
Invalid-character error with validate=True |
The data URI header or whitespace was included, the value contains unrelated characters, or the sender used URL-safe Base64. | Separate the data-URI payload, confirm the source encoding, and use the matching decoder. Remove whitespace only if it is known to be transport formatting. |
PIL.UnidentifiedImageError |
The decoded bytes are not a complete supported image, the wrong part of the input was decoded, or the bytes use another encoding. | Check that decoding used the payload rather than the header and that the sender supplied a complete supported image file. Base64 transports bytes; it does not make arbitrary bytes an image. |
Image access fails after a with block |
The image was opened lazily and its underlying stream was closed before pixel data was read. | Call load() before the stream closes, or keep the stream alive for later access. |
| Only the first animation frame is available | Loading one frame does not necessarily materialize every frame. | Keep the opened image and its stream available while reading further frames. |
| Saved image does not match its extension | Changing the filename suffix does not convert the format. | Save using the intended Pillow format and convert the image mode if the target format requires it. |
Or skip the browser setup
If the image you need is a web page screenshot, ScreenshotNeo returns the screenshot as an image response, so you can pass the response bytes straight to Pillow instead of first converting Base64. The API accepts one GET request for a URL and can return PNG, JPEG, WebP, or PDF. See the ScreenshotNeo API documentation for its request options.
import io
import requests
from PIL import Image
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
image = Image.open(io.BytesIO(r.content))
image.load()
This is a separate path from decoding Base64: the HTTP response body contains image bytes, so there is no Base64 decoder step. ScreenshotNeo removes cookie banners, newsletter popups, and chat widgets before the shot; bot checks, blank pages, failed loads, timeouts, and cache hits are not billed. Its MCP server gives AI agents screenshot tools, and the free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Learn about ScreenshotNeo or sign up for 1,000 free screenshots a month with no card.
FAQ
Does Base64 decoding itself verify that the bytes are an image?
No. It decodes text into bytes. Pillow’s image-opening step determines whether those bytes form a supported image.
Can I use the Pillow image without saving it to disk?
Yes. The BytesIO stream keeps the image in memory, and you can pass the loaded Pillow image to other code without creating a temporary file.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
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.

