I would suggest looking at Pools to easily create processes on the fly. If you need to have some shared state, in this case a boolean indicating a non-black image has been found, look at Managers.

Update: Here is an example of what I mean.

import multiprocessing.Manager as Manager
import multiprocessing.Pool as Pool

m = Manager()
p = Pool(processes=5)

state_info = m.dict()
state_info['image_found'] = False

def processImage(img):

    # ... Process Image ...

    if imageIsBlack(img):
        state_info['image_found'] = True
        p.terminate()

 p.apply(processImage, imageList)

 if state_info['image_found']:
     print 'There was a black image!!'
 else:
     print 'No black images were found.'
Answer from owobeid on Stack Overflow
Top answer
1 of 2
2

I would suggest looking at Pools to easily create processes on the fly. If you need to have some shared state, in this case a boolean indicating a non-black image has been found, look at Managers.

Update: Here is an example of what I mean.

import multiprocessing.Manager as Manager
import multiprocessing.Pool as Pool

m = Manager()
p = Pool(processes=5)

state_info = m.dict()
state_info['image_found'] = False

def processImage(img):

    # ... Process Image ...

    if imageIsBlack(img):
        state_info['image_found'] = True
        p.terminate()

 p.apply(processImage, imageList)

 if state_info['image_found']:
     print 'There was a black image!!'
 else:
     print 'No black images were found.'
2 of 2
2

Finally this works nicely for me. Copied it from an example here. For illustration purpose I have replaced my _isImgNonBlack function and the image sequence as a list of 0's and 1's, where 0 being a black image and 1 non-black image.

import multiprocessing

def isImgNonBlack(result_queue, imgSeq):
    for img in imgSeq:
        # If a non-black is found put a result
        if img==1:
            result_queue.put(1)

    # else put a zero as the result
    result_queue.put(0)

if __name__ == '__main__':
    processs = []
    result_queue = multiprocessing.Queue()
    nbProc = 20

    # making a fake list of images with 
    # 10,000 0's follwed by a single 1
    images = [0 for n in range(10000)]
    images.append(1)

    for n in range(nbProc): # start processes crawling for the result
        process = multiprocessing.Process(target=isImgNonBlack, args=[result_queue, images])
        process.start()
        processs.append(process)
        print 'Starting Process : %s' % process

    result = result_queue.get() # waits until any of the proccess have `.put()` a result

    for process in processs: # then kill them all off
        process.terminate()

    # finally print the result
    print "Seq have a non black img: %s" % result
🌐
Medium
medium.com › featurepreneur › speeding-up-image-processing-using-multiprocessing-in-python-ceeced8026bf
Speeding up image processing using multiprocessing in Python | by Santhosh Kannan | featurepreneur | Medium
February 3, 2023 - Multi-processing can be implemented by using the built-in multiprocessing module which contains the methods for creating and working with processes. However, an easier way to start a group of processes is by using the ProcessPoolExecutor which ...
Discussions

performance - Image processing using multiprocessing in Python - Code Review Stack Exchange
I have an image stack. I am trying to use multiprocessing to do some process on each image in stack to get new image, and then bitwise_or with the old image. Since multiprocessing won't keep the More on codereview.stackexchange.com
🌐 codereview.stackexchange.com
April 20, 2019
python - Multiprocessing on chunks of an image - Stack Overflow
This function takes a very long ... image) but seems like it should be easy to run in parallel on multiple cores. However, I can't for the life of me find a well documented, well explained example of doing something like this using the Multiprocessing package. Here is the code I am running right now as a toy example: import numpy as np import matplotlib.pyplot as plt from scipy import misc from skimage import color import multiprocessing from multiprocessing import Process #Some dumb ... More on stackoverflow.com
🌐 stackoverflow.com
Multiprocessing and loading images
Pickle support is more likely to work with Image objects than any of the individual image format types, but I wouldn't recommend it. Multiprocessing is a strange beast in Python, if you can do your processing completely in a separate process it might be worth it, passing back a small result ... More on github.com
🌐 github.com
February 5, 2018
python - Using multiprocessing on Image processing - Stack Overflow
I'm trying to speed up my processing of a PIL.Image, where I divide the image into small parts, search for the most similar image inside a database and then replace the original small part of the i... More on stackoverflow.com
🌐 stackoverflow.com
🌐
PyImageSearch
pyimagesearch.com › home › blog › multiprocessing with opencv and python
Multiprocessing with OpenCV and Python - PyImageSearch
December 29, 2022 - In this tutorial, you will learn ... and Python to perform feature extraction. You’ll learn how to use multiprocessing with OpenCV to parallelize feature extraction across the system bus, including all processors and cores on your computer. Today’s tutorial is inspired by PyImageSearch reader, Abigail. ... Hey Adrian, I just read your tutorial on image hashing with ...
🌐
Stack Exchange
codereview.stackexchange.com › questions › 217749 › image-processing-using-multiprocessing-in-python
performance - Image processing using multiprocessing in Python - Code Review Stack Exchange
April 20, 2019 - Later I bitwise_or the new image and old image by a for loop. Is there a way to avoid for-loop for bitwise_or to have a better performance? Thanks · import cv2 as cv import numpy as np from multiprocessing import Pool def process_img(idx, img): new_img = do_something(img) return idx, new_img def main() # ...
Top answer
1 of 2
3

I'd do something like this, starting with dependencies:

from multiprocessing import Pool
import numpy as np
from PIL import Image

# and some for testing
from random import random
from time import sleep

first I define a function to divide an image up into "chunks", sort of as you talked about:

def chunkit(ys, xs, blocksize=64):
    for y in range(0, ys, blocksize):
        yt = (y, min(ys, y + blocksize))
        for x in range(0, xs, blocksize):
            xt = (x, min(xs, x + blocksize))
            yield yt, xt

it's a lazy iterator, so this can go on for a while.

I then define my worker function:

def dumb_func(cc):
    (y0,y1), (x0,x1) = cc
    # convert to floats for ease of processing
    chunk = image[y0:y1,x0:x1] / 255.
    # random slow down for testing
    # sleep(random() ** 6)
    res = chunk ** 2
    # convert back to bytes for efficiency
    return cc, (res * 255).astype(np.uint8)

I make sure the source array stays as close to original format as possible for efficiency and send it back in the same format (this might take some fiddling, if you're dealing with other pixel formats obviously).

then I put it together:

if __name__ == '__main__':
    source = Image.open('tmp.jpeg')
    image = np.asarray(source)
    print("loaded", image.shape, image.dtype)

    with Pool() as pool:
        resit = pool.imap_unordered(
            dumb_func, chunkit(*image.shape[:2]))

        output = np.empty_like(image)
        for cc, res in resit:
            (y0,y1), (x0,x1) = cc
            output[y0:y1,x0:x1] = res

    im = Image.fromarray(output, 'RGB')
    im.save('out.jpeg')

this churns through a 15Mpixel image in a couple of seconds, with most of that spent loading/saving the image. it could probably be a lot more clever with array strides and cache friendliness, but hope that helps!

note: I think this code relies on CPython Unix style process forking semantics to make sure the image is shared between processes efficiently. not sure what would happen if you ran it on something else

2 of 2
1

I've been working on code for basically this same thing. Right now the goal is just to replace white pixels with transparent ones, but it seems to replace the entire image so there is a bug somewhere...It doesn't get an error within the multiprocessing module anymore though, so maybe it could serve as an example of how to load a Queue and then have your worker processes work on it!

from PIL import Image
from multiprocessing import Process, JoinableQueue
from threading import Thread
from time import time

def worker_function(q, new_data):
    while True:
        # print("Items in queue: {}".format(q.qsize()))
        index, pixel = q.get()
        if pixel[0] > 240 and pixel[1] > 240 and pixel[2] > 240:
            out_pixel = (0, 0, 0, 0)
        else:
            out_pixel = pixel
        new_data[index] = out_pixel
        q.task_done()

if __name__ == "__main__":
    start = time()
    q = JoinableQueue()

    my_image = Image.open('InputImage.jpg')
    my_image = my_image.convert('RGBA')
    datas = list(my_image.getdata())
    new_data = [0] * len(datas) # make a blank array the size of our image to fill later

    print('putting image into queue')
    for count, item in enumerate(datas):
        q.put((count, item))

    print('starting workers')
    worker_count = 50
    processes = []
    for i in range(worker_count):
        p = Process(target=worker_function, args=[q, new_data])
        p.daemon = True
        p.start()
    print('main thread waiting')
    q.join()
    my_image.putdata(new_data)
    my_image.save('output.png', "PNG")

    end = time()
    print('{:.3f} seconds elapsed'.format(end - start))

I think it's important to "protect" your code inside the if __name__ == "__main__" block otherwise the spawned processes seem to run it.

update

It looks like you need to implement a Manager() (or there are probably other ways I am ignorant of as well!). I got my code to run by altering it into:

from PIL import Image
from multiprocessing import Process, JoinableQueue, Manager
from threading import Thread
from time import time


def worker_function(q, new_data):
    while True:
        # print("Items in queue: {}".format(q.qsize()))
        index, pixel = q.get()
        if pixel[0] > 240 and pixel[1] > 240 and pixel[2] > 240:
            out_pixel = (0, 0, 0, 0)
        else:
            out_pixel = pixel
        new_data[index] = out_pixel
        q.task_done()


if __name__ == "__main__":
    start = time()
    q = JoinableQueue()
    my_image = Image.open('InputImage.jpg')
    my_image = my_image.convert('RGBA')
    datas = list(my_image.getdata())
    # new_data = [(0, 0, 0, 0)]*len(datas)
    manager = Manager()
    new_data = manager.list([(0, 0, 0, 0)]*len(datas))
    print(new_data)
    print('putting image into queue')
    for count, item in enumerate(datas):
        q.put((count, item))

    print('starting workers')
    worker_count = 50
    processes = []
    for i in range(worker_count):
        p = Process(target=worker_function, args=[q, new_data])
        p.daemon = True
        p.start()
    print('main thread waiting')
    q.join()
    print("Saving Image")
    my_image.putdata(new_data)
    my_image.save('output.png', "PNG")

    end = time()
    print('{:.3f} seconds elapsed'.format(end - start))

Although this doesn't seem like the fastest option! I'm sure there are other ways to increase speed. My code to do the same thing with Threads looks VERY similar:

from PIL import Image
from threading import Thread
from queue import Queue
import time

start = time.time()
q = Queue()

planeIm = Image.open('InputImage.jpg')
planeIm = planeIm.convert('RGBA')
datas = planeIm.getdata()
new_data = [0] * len(datas)

print('putting image into queue')
for count, item in enumerate(datas):
    q.put((count, item))

def worker_function():
    while True:
        # print("Items in queue: {}".format(q.qsize()))
        index, pixel = q.get()
        if pixel[0] > 240 and pixel[1] > 240 and pixel[2] > 240:
            out_pixel = (0, 0, 0, 0)
        else:
            out_pixel = pixel
        new_data[index] = out_pixel
        q.task_done()

print('starting workers')
worker_count = 100
for i in range(worker_count):
    t = Thread(target=worker_function)
    t.daemon = True
    t.start()
print('main thread waiting')
q.join()
print('Queue has been joined')
planeIm.putdata(new_data)
planeIm.save('output.png', "PNG")

end = time.time()

elapsed = end - start
print('{:3.3} seconds elapsed'.format(elapsed))

Yet, processing my image takes ~23 seconds with threads and ~170 seconds with multiprocessing!! I suspect this would come from the larger overhead needed to start Process objects, and the fact that my algorithm for processing each pixel is simple for now (just the if pixel[0] > 240 and pixel[1] > 240 and pixel[2] > 240: bit), so I'm likely not yielding the speed improvements that a complex pixel processing algorithm would get me. Also to note multiprocessing documentation

a single manager can be shared by processes on different computers over a network. They are, however, slower than using shared memory.

Which leads me to believe that there are alternatives that are faster.

🌐
Kaggle
kaggle.com › code › rsmits › using-multiprocessing-for-image-preprocessing
Using multiprocessing for image preprocessing
Checking your browser before accessing www.kaggle.com · Click here if you are not automatically redirected after 5 seconds
🌐
Python Tutorial
pythontutorial.net › home › python concurrency › python multiprocessing
Python Multiprocessing
June 2, 2023 - Each process will create a thumbnail for a picture: import time import os from PIL import Image, ImageFilter import multiprocessing filenames = [ 'images/1.jpg', 'images/2.jpg', 'images/3.jpg', 'images/4.jpg', 'images/5.jpg', ] def create_thumbnail(filename, size=(50,50), thumb_dir ='thumbs'): # open the image img = Image.open(filename) # apply the gaussian blur filter img = img.filter(ImageFilter.GaussianBlur()) # create a thumbnail img.thumbnail(size) # save the image img.save(f'{thumb_dir}/{os.path.basename(filename)}') # display a message print(f'{filename} was processed...') def main(): s
Find elsewhere
🌐
GitHub
github.com › python-pillow › Pillow › issues › 2995
Multiprocessing and loading images · Issue #2995 · python-pillow/Pillow
February 5, 2018 - So, I wanted to load different images using Pool from multiprocessing. The code is: from PIL import Image from multiprocessing import Pool def f(path): return Image.open(path) impath = ['/home/usr/Desktop/img1.jp2', '/home/usr/Desktop/im...
🌐
DEV Community
dev.to › tecnosam › multiprocessing-in-python-part-2-584g
Multiprocessing in Python (Part 2) - DEV Community
August 15, 2022 - We will be using multiprocessing for the CPU-intensive function, which is the function that detects and draws rectangles on the faces. from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor def optimised_solution(image_paths: list): # Define our pool executors with ThreadPoolExecutor() as thread_pool: with ProcessPoolExecutor() as process_pool: # We use threading to read our images images = thread_pool.map(read_image, image_paths) # We use multiprocessing to process our images processed_images = process_pool.map(draw_face,images) # We use multithreadfing to write our images to
🌐
Stack Overflow
stackoverflow.com › questions › 67761288 › using-multiprocessing-on-image-processing
python - Using multiprocessing on Image processing - Stack Overflow
Or why doesn't the multiprocessing decrease the computation time, since the workload per image slice should be big enough to offset the time needed to create processes etc.
🌐
Real Python
realpython.com › ref › stdlib › multiprocessing
multiprocessing | Python Standard Library – Real Python
Adding processes keeps paying off until you run out of tasks, or CPU cores to run them on. ... Suppose you have a directory of images and you want to apply a computationally expensive transformation to each image in parallel. You can use the multiprocessing package to speed up this task:
🌐
OneUptime
oneuptime.com › home › blog › how to use python's multiprocessing for cpu-intensive tasks
How to Use Python's multiprocessing for CPU-Intensive Tasks
January 6, 2025 - For batch operations like image processing, you can submit multiple items to the pool simultaneously. Using asyncio.gather allows concurrent execution while properly handling failures. from fastapi import FastAPI from concurrent.futures import ProcessPoolExecutor from multiprocessing import cpu_count import asyncio from typing import List app = FastAPI() executor = ProcessPoolExecutor(max_workers=cpu_count()) def process_image(image_data: bytes) -> dict: """Process a single image - runs in worker process""" # CPU-intensive image processing (resize, filter, etc.) return {"processed": True, "siz
🌐
Reddit
reddit.com › r/learnpython › multiprocessing with image in shared memory
r/learnpython on Reddit: multiprocessing with image in shared memory
February 2, 2018 -

So I am working on a script that is reading from a camera with opencv and I have multiple processes. One process is reading the camera and I have been able to share frame between them using a pipe but I need to do it faster. I found this posting but some of the answers are missing:

https://www.reddit.com/r/learnpython/comments/4u0xh8/fastest_way_to_share_numpy_arrays_between/

Can someone give me an example of what this means?

You would simply use ctypes to find this structure underlying a PhotoImage object, update its pixelPtr to point to the numpy array and adjust the pitch, pixelSize and offset to match your format of color data.

🌐
MachineLearningMastery
machinelearningmastery.com › home › blog › multiprocessing in python
Multiprocessing in Python - MachineLearningMastery.com
June 21, 2022 - Starting a new process and then joining it back to the main process is how multiprocessing works in Python (as in many other languages). The reason we want to run multiprocessing is probably to execute many different tasks concurrently for speed. It can be an image processing function, which we need to do on thousands of images.
🌐
Stack Overflow
stackoverflow.com › questions › 76250367 › parallel-image-processing-pipelines-using-python-and-multiprocessing
Parallel image processing pipelines using Python and `multiprocessing` - Stack Overflow
It sounds like you want most of the actual work to be done by third party tools and libraries, and it's likely that you don't need multiprocessing at all, but rather want to run these as asynchronous tasks. Both approaches can be done in Python - have a look at both, try something and ask specific questions about problems with your code on StackOverflow.
🌐
Towards Data Science
towardsdatascience.com › home › latest › applying python multiprocessing in 2 lines of code
Applying Python multiprocessing in 2 lines of code | Towards Data Science
March 5, 2025 - Detecting motion with OpenCV - image analysis for beginners · It is very easy to limit the maximum number of processes/cores/CPU's that the Pool will allow at any given time: just add the processes argument when you instantiate the Pool like below: ... Adding multiple processes to make our code run in parallel isn't difficult; the challenge lies in knowing when to apply which technique. In summary: the Pool object of the multiprocessing library offers three functions.
🌐
StrataScratch
stratascratch.com › blog › how-to-use-python-multiprocessing
How to Use Python Multiprocessing for Better Performance - StrataScratch
January 8, 2026 - Multiprocessing is used in real production environments, especially in machine learning. In this section, let’s see different use cases. You can use the NYC taxi dataset to apply batch preprocessing because it is large. To scale image or video processing, you can use Open Image Dataset.
🌐
Medium
medium.com › @CodingWithHannan › multiprocessing-in-python-to-accelerate-heavy-data-processing-70b8f47a73a5
Multiprocessing in Python to Accelerate Heavy Data Processing | by CodingWithHannan | Medium
August 23, 2025 - Why multiprocessing is necessary in Python. How multiprocessing differs from threading. Core mechanisms like Pool, Queue, and Process. Practical examples including image processing, numerical computations, and log analysis.