skip to navigation
skip to content

Planet Python

Last update: May 11, 2025 01:43 PM UTC

May 09, 2025


Real Python

The Real Python Podcast – Episode #248: Experiments With Gen AI, Knowledge Graphs, Workflows, and Python

Are you looking for some projects where you can practice your Python skills? Would you like to experiment with building a generative AI app or an automated knowledge graph sentiment analysis tool? This week on the show, we speak with Raymond Camden about his journey into Python, his work in developer relations, and the Python projects featured on his blog.

May 09, 2025 12:00 PM UTC


Daniel Roy Greenfeld

Exploring flexicache

An exploration of using flexicache for caching in Python.

May 09, 2025 07:54 AM UTC


Python Insider

Python 3.14.0 beta 1 is here!

May 09, 2025 05:08 AM UTC

May 08, 2025


Python Engineering at Microsoft

Python in Visual Studio Code – May 2025 Release

The May 2025 release includes updates in the Python Environments extension, a new color picker added by Pylance, branch coverage support, and more!

The post Python in Visual Studio Code – May 2025 Release appeared first on Microsoft for Python Developers Blog.

May 08, 2025 10:39 PM UTC


eGenix.com

PyDDF Python Spring Sprint 2025

The following text is in German, since we're announcing a Python sprint in Düsseldorf, Germany.

Ankündigung

Python Meeting Spring Sprint 2025 in
Düsseldorf

Samstag, 24.05.2025, 10:00-18:00 Uhr
Sonntag, 25.05.2025. 10:00-18:00 Uhr

Eviden / Atos Information Technology GmbH, Am Seestern 1, 40547 Düsseldorf

Informationen

Das Python Meeting Düsseldorf (PyDDF) veranstaltet mit freundlicher Unterstützung von Eviden Deutschland ein Python Sprint Wochenende.

Der Sprint findet am Wochenende 24/25.5.2025 in der Eviden / Atos Niederlassung, Am Seestern 1, in Düsseldorf statt.Folgende Themengebiete sind als Anregung bereits angedacht:
Natürlich können die Teilnehmenden weitere Themen vorschlagen und umsetzen.

Anmeldung, Kosten und weitere Infos

Alles weitere und die Anmeldung findet Ihr auf der Meetup Sprint Seite:

WICHTIG: Ohne Anmeldung können wir den Gebäudezugang nicht vorbereiten. Eine spontane Anmeldung am Sprint Tag wird daher vermutlich nicht funktionieren.

Teilnehmer sollten sich zudem in der PyDDF Telegram Gruppe registrieren, da wir uns dort koordinieren:

Über das Python Meeting Düsseldorf

Das Python Meeting Düsseldorf ist eine regelmäßige Veranstaltung in Düsseldorf, die sich an Python-Begeisterte aus der Region wendet.

Einen guten Überblick über die Vorträge bietet unser PyDDF YouTube-Kanal, auf dem wir Videos der Vorträge nach den Meetings veröffentlichen.

Veranstaltet wird das Meeting von der eGenix.com GmbH, Langenfeld, in Zusammenarbeit mit Clark Consulting & Research, Düsseldorf.

Viel Spaß !

Marc-André Lemburg, eGenix.com

May 08, 2025 09:00 AM UTC


Test and Code

pytest-metadata - provides access to test session metadata

pytest-metadata is described as a plugin for pytest that provides access to test session metadata. 
That is such a humble description for such a massively useful plugin. 
If you're already using pytest-html, you have pytest-metadata already installed, as pytest-metadata is one of the dependencies for pytest-html.
However, pytest-metadata is very useful even on its own.

Links:

If you've got other plugins that work well with pytest-metadata, please let me know.


Sponsored by: 

Help support the show AND learn pytest: 


★ Support this podcast on Patreon ★

May 08, 2025 05:57 AM UTC


Seth Michael Larson

A(nimal Cros)SCII

May 08, 2025 12:00 AM UTC

May 07, 2025


The Python Coding Stack

A Story About Parameters and Arguments in Python Functions • "AI Coffee" Grand Opening This Monday

Parameters and arguments • Positional and keyword arguments • Parameters with default values • *args and **kwargs • Positional-only and keyword-only arguments • Let's discuss all of this over coffee

May 07, 2025 08:19 PM UTC


death and gravity

Process​Thread​Pool​Executor: when I‍/‍O becomes CPU-bound

...in which we build a hybrid concurrent.futures executor that runs I‍/‍O bound tasks on all available CPUs, thus evading the limitations imposed by the dreaded global interpreter lock on the humble ThreadPoolExecutor.

May 07, 2025 06:00 PM UTC


Django Weblog

Django security releases issued: 5.2.1, 5.1.9 and 4.2.21

In accordance with our security release policy, the Django team is issuing releases for Django 5.2.1, Django 5.1.9 and Django 4.2.21. These releases address the security issues detailed below. We encourage all users of Django to upgrade as soon as possible.

CVE-2025-32873: Denial-of-service possibility in strip_tags()

django.utils.html.strip_tags() would be slow to evaluate certain inputs containing large sequences of incomplete HTML tags. This function is used to implement the striptags template filter, which was thus also vulnerable. django.utils.html.strip_tags() now raises a SuspiciousOperation exception if it encounters an unusually large number of unclosed opening tags.

Thanks to Elias Myllymäki for the report.

This issue has severity "moderate" according to the Django security policy.

Affected supported versions

  • Django main
  • Django 5.2
  • Django 5.1
  • Django 4.2

Resolution

Patches to resolve the issue have been applied to Django's main, 5.2, 5.1, and 4.2 branches. The patches may be obtained from the following changesets.

CVE-2025-32873: Denial-of-service possibility in strip_tags()

The following releases have been issued

The PGP key ID used for this release is Natalia Bidart: 2EE82A8D9470983E

General notes regarding security reporting

As always, we ask that potential security issues be reported via private email to security@djangoproject.com, and not via Django's Trac instance, nor via the Django Forum. Please see our security policies for further information.

May 07, 2025 02:00 PM UTC


Real Python

How to Use Loguru for Simpler Python Logging

In this tutorial, you'll learn how to use Loguru to quickly implement better logging in your Python applications. You'll spend less time wrestling with logging configuration and more time using logs effectively to debug issues.

May 07, 2025 02:00 PM UTC


John Cook

Converting between quaternions and rotation matrices

In the previous post I wrote about representing rotations with quaternions. This representation has several advantages, such as making it clear how rotations compose. Rotations are often represented as matrices, and so it’s useful to be able to go between the two representations. A unit-length quaternion (q0, q1, q2, q3) represents a rotation by an […]

The post Converting between quaternions and rotation matrices first appeared on John D. Cook.

May 07, 2025 01:52 PM UTC


Daniel Roy Greenfeld

TIL: ^ bitwise XOR

How to mark a comparison of booleans as True or False using bitwise XOR.

May 07, 2025 03:21 AM UTC

May 06, 2025


PyCoder’s Weekly

Issue #680: Thread Safety, Pip 25.1, DjangoCon EU Wrap-Up, and More (May 6, 2025)

May 06, 2025 07:30 PM UTC


Ari Lamstein

Course Review: Build an AI chatbot with Python

For a while now I’ve been wanting to learn more about LLMs. The problem has been that I wasn’t sure where to start. So when Kevin Markham launched his course Build an AI chatbot with Python I jumped at the chance to take it. I had previously taken Kevin’s course on Pandas and enjoyed his […]

May 06, 2025 04:10 PM UTC


Real Python

Using the Python subprocess Module

In this video course, you'll learn how to use Python’s subprocess module to run and control external programs from your scripts. You'll start with launching basic processes and progress to interacting with them as they execute.

May 06, 2025 02:00 PM UTC


Python Software Foundation

Announcing Python Software Foundation Fellow Members for Q1 2025! 🎉

May 06, 2025 12:13 PM UTC


Real Python

Quiz: Python Logging With the Loguru Library

Think you know Python logging? Take this quick Loguru quiz to test your knowledge of formatting, sinks, rotation, and more!

May 06, 2025 12:00 PM UTC

May 05, 2025


PyCon

Asking the Key Questions: Q&A with the PyCon US 2025 keynote speakers

May 05, 2025 05:19 PM UTC


Real Python

Sets in Python

In this tutorial, you'll learn how to work effectively with Python's set data type. You'll learn how to define set objects and discover the operations that they support. By the end of the tutorial, you'll have a good feel for when a set is an appropriate choice in your programs.

May 05, 2025 02:00 PM UTC


Talk Python to Me

#504: Developer Trends in 2025

What trends and technologies should you be paying attention to today? Are there hot new database servers you should check out? Or will that just be a flash in the pan? I love these forward looking episodes and this one is super fun. I've put together an amazing panel: Gina Häußge, Ines Montani, Richard Campbell, and Calvin Hendryx-Parker. We dive into the recent Stack Overflow Developer survey results as a sounding board for our thoughts on rising and falling trends in the Python and broader developer space.

May 05, 2025 08:00 AM UTC


Python Bytes

#431 Nerd Gas

Topics include pirel: Python release cycle in your terminal, FastAPI Cloud, and Python's new t-strings.

May 05, 2025 08:00 AM UTC


Python GUIs

Build an Image Noise Reduction Tool with Streamlit and OpenCV — Clean up noisy images using OpenCV

Image noise is a random variation of brightness or color in images, which can make it harder to discern finer details in a photo. Noise is an artefact of how the image is captured. In digital photography, sensor electronic noise causes random fuzziness over the true image. It is more noticeable in low light, where the lower signal from the sensor is amplified, amplifying the noise with it. Similar noisy artefacts are also present in analog photos and film, but there it is caused by the film grain. Finally, you can also see noise-like artefacts introduced by lossy compression algorithms such as JPEG.

Noise reduction or denoising improves the visual appearance of a photo and can be an important step in a larger image analysis pipeline. Eliminating noise can make it easier to identify features algorithmically. However, we need to ensure that the denoised image is still an accurate representation of the original capture.

Denoising is a complex topic. Fortunately, several different algorithms are available. In this tutorial, we'll use algorithms from OpenCV and build them into a Streamlit app. The app will allow a user to upload images, choose from the common noise reduction algorithms, such as Gaussian Blur, Median Blur, Minimum Blur, and Maximum Blur, and adjust the strength of the noise reduction using a slider. The user can then download the resulting noise-reduced image.

By the end of this tutorial, you will --

There's quite a lot to this example, so we'll break it down into small steps to make sure we understand how everything works.

Setting Up the Working Environment

In this tutorial, we'll use the Streamlit library to build the noise reduction app's GUI.

To perform the denoising, we'll be using OpenCV. Don't worry if you're not familiar with this library, we'll be including working examples you can copy for everything we do.

With that in mind, let's create a virtual environment and install our requirements into it. To do this, you can run the following commands:

sh
$ mkdir denoise/
$ cd denoise
$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install streamlit opencv-python pillow numpy
cmd
> mkdir denoise/
> cd denoise
> python -m venv venv
> venv\Scripts\activate.bat
(venv)> pip install streamlit opencv-python pillow numpy
sh
$ mkdir denoise/
$ cd denoise
$ python -m venv venv
$ source venv/bin/activate
(venv)$ pip install streamlit opencv-python pillow numpy

With these commands, you create a denoise/ folder for storing your project. Inside that folder, you create a new virtual environment, activate it, and install Streamlit, OpenCV, Pillow & numpy.

For platform-specific troublshooting, check the Working With Python Virtual Environments tutorial.

Building the Application Outline

We'll start by constructing a simple Streamlit application and then expand it from there.

python
import streamlit as st

# Set the title of our app.
st.title("Noise Reduction App")

Save this file as app.py and use the following command to run it:

python
streamlit run app.py

Streamlit will start up and will launch the application in your default web browser.

The Streamlit application title displayed in the browser The Streamlit application title displayed in the browser.

If it doesn't launch by itself, you can see the web address to open in the console.

The Streamlit application launch message showing the local server address where the app can be viewed The Streamlit application launch message showing the local server address where the app can be viewed.

Now that we have the app working, we can step through and build up our app.

Uploading an Image with Streamlit

First we need a way to upload an image to denoise. Streamlit provides a simple .file_uploader method which can be used to upload an image from your computer. This is a generic file upload handler, but you can provide both a message to display (to specify what to upload) and constrain the file types that are supported.

Below we define a file_uploader which shows a message "Choose an image..." and accepts JPEG and PNG images.

python
import streamlit as st

# Set the title of our app.
st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])

print(uploaded_file)

For historic reasons, JPEG images can have both .jpg or .jpeg extensions, so we include both in the list.

Run the code and you'll see the file upload box in the app. Try uploading a file.

Streamlit application with a file-upload widget Streamlit application with a file-upload widget.

The uploaded image is stored in the variable uploaded_file. Before a file is uploaded, the value of uploaded_file will be None. Once the user uploads an image, this variable will contain an UploadedFile object.

python
None
UploadedFile(file_id='73fd9a97-9939-4c02-b9e8-80bd2749ff76', name='headcake.jpg', type='image/jpeg', size=652805, _file_urls=file_id: "73fd9a97-9939-4c02-b9e8-80bd2749ff76"
upload_url: "/_stcore/upload_file/7c881339-82e4-4d64-ba20-a073a11f7b60/73fd9a97-9939-4c02-b9e8-80bd2749ff76"
delete_url: "/_stcore/upload_file/7c881339-82e4-4d64-ba20-a073a11f7b60/73fd9a97-9939-4c02-b9e8-80bd2749ff76"
)

We can use this UploadedFile object to load and display the image in the browser.

How Streamlit Works

If you're used to writing Python scripts the behavior of the script and file upload box might be a confusing. Normally a script would execute from top to bottom, but here the value of uploaded_file is changing and the print statement is being re-run as the state changes.

There's a lot of clever stuff going on under the hood here, but in simple terms the Streamlit script is being re-evaluated in response to changes. On each change the script runs again, from top to bottom. But importantly, the state of widgets is not reset on each run.

When we upload a file, that file gets stored in the state of the file upload widget and this triggers the script to re-start. When it gets to the st.file_uploader call, that UploadedFile object will be returned immediately from the stored state. It can then affect the flow of the code after it.

The following code allows you to see these re-runs more clearly, by displaying the current timestamp in the header. Every time the code is re-executed this number will update.

python
from time import time

import streamlit as st

# Set the title of our app.
st.title(f"Noise Reduction App {int(time())}")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])

Try uploading an image and then removing it. You'll see the timestamp in the title change each time. This is the script being re-evaluated in response to changes in the widget state.

Loading and Displaying the Uploaded Image

While we can upload an image, we can't see it yet. Let's implement that now.

As mentioned, the uploaded file is available as an UploadedFile object in the uploaded_file variable. This object can be passed directly to st.image to display the image back in the browser. You can also add a caption and auto resize the image to the width of the application.

python
import numpy as np
import streamlit as st
from PIL import Image

st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])


if uploaded_file is not None:
    st.image(image, caption="Uploaded Image", use_container_width=True)

Run this and upload an image. You'll see the image appear under the file upload widget.

Streamlit application showing an uploaded image Streamlit application showing an uploaded image.

Converting the Image for Processing

While the above works fine for displaying the image in the browser, we want to process the image through the OpenCV noise reduction algorithms. For that we need to get the image into a format which OpenCV recognizes. We can do that using Pillow & NumPy.

The updated code to handle this conversion is shown below.

python
import numpy as np
import streamlit as st
from PIL import Image

st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])


if uploaded_file is not None:
    # Convert the uploaded file to a PIL image.
    image = Image.open(uploaded_file)

    # Convert the image to an RGB NumPy array for processing.
    image = image.convert("RGB")
    image = np.array(image)

    # Displaying the RGB image.
    st.image(image, caption="Uploaded Image", use_container_width=True)

In this code, the uploaded file is opened using Pillow's Image.open() method, which reads the image into a PIL image format. The image is then converted into Pillows RGB format, for consistency (discarding transparent for example). This regular format is then converted into a NumPy array which OpenCV requires for processing.

Helpfully, Streamlit's st.image method also understands the NumPy RGB image format, so we can pass the image array directly to it. This will be useful when we want to display the processed image, since we won't need to convert it before doing that.

If you run the above it will work exactly as before. But now we have our uploaded image available as an RGB array in the image variable. We'll use that to do our processing next.

Configuring the Noise Reduction Algorithm

The correct noise reduction strategy depends on the image and type of noise present. For a given image you may want to try different algorithms and adjust the extent of the noise reduction. To accommodate that, we're going to add two new controls to our application -- an algorithm drop down, and a kernel size slider.

The first presents a select box from which the user can choose which algorithm to use. The second allows the user to configure the behavior of the given algorithm -- specifically the size of the area being considered by each algorithm when performing noise reduction.

python
import numpy as np
import streamlit as st
from PIL import Image

st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])

algorithm = st.selectbox(
    "Select noise reduction algorithm",
    (
        "Gaussian Blur Filter",
        "Median Blur Filter",
        "Minimum Blur Filter",
        "Maximum Blur Filter",
        "Non-local Means Filter",
    ),
)

kernel_size = st.slider("Select kernel size", 1, 10, step=2)


if uploaded_file is not None:
    # Convert the uploaded file to a PIL image.
    image = Image.open(uploaded_file)

    # Convert the image to an RGB NumPy array for processing.
    image = image.convert("RGB")
    image = np.array(image)

    # Displaying the RGB image.
    st.image(image, caption="Uploaded Image", use_container_width=True)

When you run this you'll see the new widgets in the UI. The uploaded image is displayed last since it is the last thing to be added.

The algorithm selection and configuration widgets shown in the app The algorithm selection and configuration widgets shown in the app.

The slider for the kernel size allows the user to adjust the kernel size, which determines the strength of the noise reduction effect. The kernel is a small matrix used in convolution to blur or process the image for noise removal. The larger the kernel size, the stronger the effect will be but also the more blurring or distortion you will see in the image.

The removal of noise is always a balancing act between noise and accuracy of the image.

The slider ranges from 1 to 10, with a step of 2 (i.e., possible kernel sizes are 1, 3, 5, 7, and 9).

The kernel size must be an odd number to maintain symmetry in the image processing algorithms.

Performing the Noise Reduction

Now we have all the parts in place to actually perform noise reduction on the image. The final step is to add the calls to OpenCV's noise reduction algorithms and show the resulting, noise-reduced image back in the UI.

python
import cv2
import numpy as np
import streamlit as st
from PIL import Image

st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])

algorithm = st.selectbox(
    "Select noise reduction algorithm",
    (
        "Gaussian Blur Filter",
        "Median Blur Filter",
        "Minimum Blur Filter",
        "Maximum Blur Filter",
        "Non-local Means Filter",
    ),
)

kernel_size = st.slider("Select kernel size", 1, 10, step=2)


if uploaded_file is not None:
    # Convert the uploaded file to a PIL image.
    image = Image.open(uploaded_file)

    # Convert the image to an RGB NumPy array for processing.
    image = image.convert("RGB")
    image = np.array(image)

    # Displaying the RGB image.
    st.image(image, caption="Uploaded Image", use_container_width=True)

    # Applying the selected noise reduction algorithm based on user selection
    if algorithm == "Gaussian Blur Filter":
        denoised_image = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
    elif algorithm == "Median Blur Filter":
        denoised_image = cv2.medianBlur(image, kernel_size)
    elif algorithm == "Minimum Blur Filter":
        kernel = np.ones((kernel_size, kernel_size), np.uint8)
        denoised_image = cv2.erode(image, kernel, iterations=1)
    elif algorithm == "Maximum Blur Filter":
        kernel = np.ones((kernel_size, kernel_size), np.uint8)
        denoised_image = cv2.dilate(image, kernel, iterations=1)
    elif algorithm == "Non-local Means Filter":
        denoised_image = cv2.fastNlMeansDenoisingColored(
            image, None, kernel_size, kernel_size, 7, 15
        )

    # Displaying the denoised image in RGB format
    st.image(denoised_image, caption="Denoised Image", use_container_width=True)

If you run this you can now upload your images and apply denoising to them. Try changing the algorithm and adjusting the kernel size parameter to see the effect it has on the noise reduction. The denoised image is displayed at the bottom with the caption "Denoised Image".

Each of the noise reduction strategies is described below. The median blur and non-local means methods are the most effective for normal images.

Gaussian Blur Filter

Gaussian blur smoothens the image by applying a Gaussian function to a pixel's neighbors. The kernel size determines the area over which the blur is applied, with larger kernels leading to stronger blurs. This method preserves edges fairly well and is often used in preprocessing for tasks like object detection.

Gaussian blur filter applied to a image using a 3x3 kernel Gaussian blur filter applied to a image using a 3x3 kernel.

This is effective at removing light noise, at the expense of sharpness.

Median Blur Filter

Median blur reduces noise by replacing each pixel's value with the median value from the surrounding pixels, making it effective against salt-and-pepper noise. It preserves edges better than Gaussian blur but can still affect the sharpness of fine details.

Median blur filter applied to a image using a 3x3 kernel window Median blur filter applied to a image using a 3x3 kernel window.

Median blur noise reduction (kernel size = 7) Median blur noise reduction (kernel size = 7).

Median blur noise reduction (kernel size = 5) Median blur noise reduction (kernel size = 5).

Minimum Blur (Erosion)

This filter uses the concept of morphological erosion. It shrinks bright areas in the image by sliding a small kernel over it. This filter is effective for removing noise in bright areas but may distort the overall structure if applied too strongly.

Erosion algorithm applied to an image using 3x3 kernel window Erosion algorithm applied to an image using 3x3 kernel window.

This works well to remove light noise from dark regions.

Erosion noise reduction (kernel size = 5) Erosion noise reduction (kernel size = 5).

Maximum Blur (Dilation)

In contrast to erosion, dilation expands bright areas and is effective in eliminating dark noise spots. However, it can result in the expansion of bright regions, altering the shape of objects in the image.

Dilation algorithm applied to an image using 3x3 kernel window Dilation algorithm applied to an image using 3x3 kernel window.

This works well to remove dark noise from light regions.

Non-Local Means Denoising

This method identifies similar regions from across the image, then combines these together to average out the noise. This works particularly well in images with repeating regions, or flat areas of color, but less well when the image has too much noise to be able to identify the similar regions.

Non-local means noise reduction on smoke from birthday candles (kernel size = 5). Non-local means noise reduction example.

Improving the Layout

It's not very user friendly having the input and output images one above the other, as you need to scroll up and down to see the effect of the algorithm. Streamlit has support for arranging widgets in columns. We'll use that to put the two images next to one another.

To create columns in Streamlit you use st.columns() passing in the number of columns to create. This returns column objects (as many as you request) which can be used as context managers to wrap your widget calls. In code, this looks like the following:

python
    # Displaying the denoised image in RGB format
    col1, col2 = st.columns(2)

    with col1:
        st.image(image, caption="Uploaded Image", use_container_width=True)

    with col2:
        st.image(denoised_image, caption="Denoised Image", use_container_width=True)

Here we call st.columns(2) creating two columns, returning into col1 and col2. We then use these with with to wrap the two st.image calls. This puts them into two adjacent columns.

Run this and you'll see the two images next to one another. This makes it much easier to see the impact of changes in the algorithm or parameters.

The source and processed image arranged next to one another using columns The source and processed image arranged next to one another using columns.

Downloading the Denoised Image

Our application now allows users to upload images and process them to remove noise, with a configurable noise removal algorithm and kernel size. The final step is to allow users to download and save the processed image somewhere.

You can actually just right-click and use your browser's option to Save the image if you like. But adding this to the UI makes it more explicit and allows us to offer different image output formats.

First, we need to import the io module. In a normal image processing script, you could simply save the generated image to disk. Our Streamlit app could be running on a server somewhere, and saving the result to the server isn't useful: we want to be able to send it to the user. For that, we need to send it to the web browser. We browsers don't understand Python objects, so we need to save our image data to a simple bytes object. The io module allows us to do that.

Add an import for Python's io module to the imports at the top of the code.

python
import io

import cv2
import numpy as np
import streamlit as st
from PIL import Image

Now under the rest of the code we can add the widgets and logic for saving and presenting the image as a download. First add a select box to choose the image format.

python
    # ..snipped the rest of the code.

    # Dropdown to select the file format for downloading
    file_format = st.selectbox("Select output format", ("PNG", "JPEG"))

Next we need to take our denoised_image and convert this from a NumPy array back to a PIL image. Then we can use Pillow's native methods for saving the image to a simple bytestream, which can be sent to the web browser.

python
    # Converting NumPy array to PIL image in RGB mode
    denoised_image_pil = Image.fromarray(denoised_image)

    # Creating a buffer to store the image data in the selected format
    buf = io.BytesIO()
    denoised_image_pil.save(buf, format=file_format)
    byte_data = buf.getvalue()

Since OpenCV operations return a NumPy array (the same format we provide it with) it must be converted back to a PIL image before saving. The io.BytesIO() creates an in-memory file buffer to write to. That way we don't need to actually save the image. We write the image using the Image .save() method in the requested file format.

Note that this saved image is in an actual PNG/JPEG image format at this point, not just pure image data.

We can retrieve the bytes data from the buffer using .getvalue(). The resulting byte_data is a raw bytes object that can be passed to the web browser. This is handled by a Streamlit download button.

python
    # Button to download the processed image
    st.download_button(
        label="Download Image",
        data=byte_data,
        file_name=f"denoised_image.{file_format.lower()}",
        mime=f"image/{file_format.lower()}"
    )

Notice we've also set the filename and mimetype, using the selected file_format variable.

If you're adding additional file formats, be aware that the mimetypes are not always 1:1 with the file extensions. In this case we've used .jpeg since the mimetype is image/jpeg.

Improving the Code Structure

The complete code so far is shown below.

python
import io

import cv2
import numpy as np
import streamlit as st
from PIL import Image

st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])

algorithm = st.selectbox(
    "Select noise reduction algorithm",
    (
        "Gaussian Blur Filter",
        "Median Blur Filter",
        "Minimum Blur Filter",
        "Maximum Blur Filter",
        "Non-local Means Filter",
    ),
)

kernel_size = st.slider("Select kernel size", 1, 10, step=2)


if uploaded_file is not None:
    # Convert the uploaded file to a PIL image.
    image = Image.open(uploaded_file)

    # Convert the image to an RGB NumPy array for processing.
    image = image.convert("RGB")
    image = np.array(image)

    # Applying the selected noise reduction algorithm based on user selection
    if algorithm == "Gaussian Blur Filter":
        denoised_image = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
    elif algorithm == "Median Blur Filter":
        denoised_image = cv2.medianBlur(image, kernel_size)
    elif algorithm == "Minimum Blur Filter":
        kernel = np.ones((kernel_size, kernel_size), np.uint8)
        denoised_image = cv2.erode(image, kernel, iterations=1)
    elif algorithm == "Maximum Blur Filter":
        kernel = np.ones((kernel_size, kernel_size), np.uint8)
        denoised_image = cv2.dilate(image, kernel, iterations=1)
    elif algorithm == "Non-local Means Filter":
        denoised_image = cv2.fastNlMeansDenoisingColored(
            image, None, kernel_size, kernel_size, 7, 15
        )

    # Displaying the denoised image in RGB format
    col1, col2 = st.columns(2)

    with col1:
        st.image(image, caption="Uploaded Image", use_container_width=True)

    with col2:
        st.image(denoised_image, caption="Denoised Image", use_container_width=True)

    # Dropdown to select the file format for downloading
    file_format = st.selectbox("Select output format", ("PNG", "JPEG", "JPG"))

    # Converting NumPy array to PIL image in RGB mode
    denoised_image_pil = Image.fromarray(denoised_image)

    # Creating a buffer to store the image data in the selected format
    buf = io.BytesIO()
    denoised_image_pil.save(buf, format=file_format)
    byte_data = buf.getvalue()

    # Button to download the processed image
    st.download_button(
        label="Download Image",
        data=byte_data,
        file_name=f"denoised_image.{file_format.lower()}",
        mime=f"image/{file_format.lower()}",
    )

If you run the completed app you can now upload images, denoise them using the different algorithms and kernel parameters and then save them as JPEG or PNG format images.

However, we can still improve this. There is a lot of code nested under the if uploaded_file is not None: branch, and the logic and processing steps aren't well organized -- everything runs together, mixed in with the UI. When developing UI applications it's a good habit to separate UI and non-UI code where possible (logic vs. presentation). That keeps related code together in the same context, aiding readability and maintainability.

Below is the same code refactored to move the file opening, denoising and file exporting logic out into separate handler functions.

python
import io

import cv2
import numpy as np
import streamlit as st
from PIL import Image


def image_to_array(file_to_open):
    """Load a Streamlit image into an array."""
    # Convert the uploaded file to a PIL image.
    image = Image.open(file_to_open)

    # Convert the image to an RGB NumPy array for processing.
    image = image.convert("RGB")
    image = np.array(image)
    return image


def denoise_image(image, algorithm, kernel_size):
    """Apply a denoising algorithm to the provided image, with the given kernel size."""
    # Applying the selected noise reduction algorithm based on user selection
    if algorithm == "Gaussian Blur Filter":
        denoised_image = cv2.GaussianBlur(image, (kernel_size, kernel_size), 0)
    elif algorithm == "Median Blur Filter":
        denoised_image = cv2.medianBlur(image, kernel_size)
    elif algorithm == "Minimum Blur Filter":
        kernel = np.ones((kernel_size, kernel_size), np.uint8)
        denoised_image = cv2.erode(image, kernel, iterations=1)
    elif algorithm == "Maximum Blur Filter":
        kernel = np.ones((kernel_size, kernel_size), np.uint8)
        denoised_image = cv2.dilate(image, kernel, iterations=1)
    elif algorithm == "Non-local Means Filter":
        denoised_image = cv2.fastNlMeansDenoisingColored(
            image, None, kernel_size, kernel_size, 7, 15
        )
    return denoised_image


def image_array_to_bytes(image_to_convert):
    """Given an image array, convert it to a bytes object."""

    # Converting NumPy array to PIL image in RGB mode
    image_pil = Image.fromarray(image_to_convert)

    # Creating a buffer to store the image data in the selected format
    buf = io.BytesIO()
    image_pil.save(buf, format=file_format)
    byte_data = buf.getvalue()
    return byte_data


st.title("Noise Reduction App")

uploaded_file = st.file_uploader("Choose an image...", type=["jpg", "jpeg", "png"])

algorithm = st.selectbox(
    "Select noise reduction algorithm",
    (
        "Gaussian Blur Filter",
        "Median Blur Filter",
        "Minimum Blur Filter",
        "Maximum Blur Filter",
        "Non-local Means Filter",
    ),
)

kernel_size = st.slider("Select kernel size", 1, 10, step=2)


if uploaded_file is not None:
    image = image_to_array(uploaded_file)
    denoised_image = denoise_image(image, algorithm, kernel_size)

    # Displaying the denoised image in RGB format
    col1, col2 = st.columns(2)

    with col1:
        st.image(image, caption="Uploaded Image", use_container_width=True)

    with col2:
        st.image(denoised_image, caption="Denoised Image", use_container_width=True)

    # Dropdown to select the file format for downloading
    file_format = st.selectbox("Select output format", ("PNG", "JPEG", "JPG"))

    byte_data = image_array_to_bytes(denoised_image)

    # Button to download the processed image
    st.download_button(
        label="Download Image",
        data=byte_data,
        file_name=f"denoised_image.{file_format.lower()}",
        mime=f"image/{file_format.lower()}",
    )

As you can see, the main flow of the code now consists entirely of Streamlit UI setup code and calls to the processing functions we have defined. Both the UI and processing code is now easier to read and maintain.

In larger projects you may choose to move the functions out into a separate files of related functions and import them instead.

Conclusion

In this tutorial, you created an image noise reduction application using Streamlit and OpenCV. The app allows users to upload images, apply different noise reduction algorithms, and download the denoised image.

It also allows the user to customize the kernel size, which controls the strength of the effect. This makes the app useful for a variety of noise types and image processing tasks.

Streamlit makes it simple to build powerful web applications, taking the power of Python's rich ecosystem and making it available through the browser.

May 05, 2025 06:00 AM UTC


Seth Michael Larson

Voicemail for notifications

May 05, 2025 12:00 AM UTC

May 03, 2025


Techiediaries - Django

The Full Python Cheatsheet: From Basics to Data Science

To help beginners and professionals alike, we are sharing a comprehensive Python cheatsheet that covers everything from the basic syntax to powerful data science libraries. Bookmark it, print it, share it—this is the companion you didn’t know you needed.

May 03, 2025 12:00 AM UTC