OpenCV
multithreading
computer vision
programming
2019

How to properly Multithread in OpenCV in 2019?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

OpenCV already parallelizes many internal operations, so "multithreading OpenCV" usually does not mean sprinkling threads around random function calls. The real question is how to structure your pipeline so capture, preprocessing, inference, and display do not block one another, while avoiding unsafe sharing of cv::Mat data.

Start With OpenCV's Built-In Parallelism

Many OpenCV operations can already use multiple cores through backend libraries such as TBB, OpenMP, or platform-specific threading backends. Before writing your own threads, measure whether the default library behavior is already good enough.

For custom per-pixel or per-row work, OpenCV provides cv::parallel_for_.

cpp
1#include <opencv2/opencv.hpp>
2
3cv::Mat src = cv::imread("image.png", cv::IMREAD_GRAYSCALE);
4cv::Mat dst(src.size(), src.type());
5
6cv::parallel_for_(cv::Range(0, src.rows), [&](const cv::Range& range) {
7    for (int r = range.start; r < range.end; ++r) {
8        for (int c = 0; c < src.cols; ++c) {
9            dst.at<uchar>(r, c) = 255 - src.at<uchar>(r, c);
10        }
11    }
12});

This is usually safer than manually creating a thread per row or per tile.

Pipeline Parallelism Is Often Better Than Function-Level Threads

For video applications, a common pattern is:

  • one thread captures frames
  • one worker thread processes frames
  • one thread or the main loop displays results

That reduces blocking between stages.

For example, VideoCapture.read() can block on camera or file I/O. If capture and processing happen on the same thread, throughput can drop badly.

Be Careful With cv::Mat Sharing

cv::Mat uses reference counting, which makes copies cheap, but that does not mean arbitrary concurrent mutation is safe.

Safe rule of thumb:

  • multiple threads may read the same immutable Mat
  • if one thread writes, give it its own independent copy or its own output buffer

Use clone() or copyTo() when you need ownership of a writable image independent of another thread.

Avoid Oversubscription

A common performance failure is combining:

  • your own thread pool
  • OpenCV internal threading
  • other threaded libraries such as DNN backends

This can create more runnable threads than the CPU can handle efficiently.

Sometimes fewer threads are faster because there is less scheduling overhead and cache contention.

A Producer-Consumer Sketch

cpp
1#include <opencv2/opencv.hpp>
2#include <mutex>
3#include <queue>
4#include <thread>
5#include <condition_variable>
6
7std::queue<cv::Mat> q;
8std::mutex m;
9std::condition_variable cv_ready;
10
11void producer() {
12    cv::VideoCapture cap(0);
13    cv::Mat frame;
14    while (cap.read(frame)) {
15        std::lock_guard<std::mutex> lock(m);
16        q.push(frame.clone());
17        cv_ready.notify_one();
18    }
19}

The important detail is frame.clone(). Without it, the queued Mat may alias reused capture memory.

GUI Calls Usually Belong on One Thread

Functions such as imshow and event-loop handling often behave best when kept on the main thread. Even when processing happens elsewhere, do not assume every UI-related OpenCV call is happy inside arbitrary worker threads.

Measure Before and After

A proper multithreading strategy depends on workload:

  • CPU-bound image transforms
  • camera I/O
  • DNN inference
  • disk decoding

Profile the pipeline. Do not assume manual threading helps just because the machine has many cores.

Common Pitfalls

A common mistake is writing to the same cv::Mat from multiple threads without partitioning the memory or isolating outputs.

Another mistake is manually adding threads around code that OpenCV already parallelizes internally, which can reduce performance instead of improving it.

Developers also often forget that camera capture and GUI display have different threading constraints from pure image math.

Summary

  • OpenCV already parallelizes many operations, so measure before adding manual threads.
  • Use cv::parallel_for_ for custom data-parallel loops.
  • For video, pipeline threading is often more useful than random function-level threading.
  • Treat writable cv::Mat data as thread-owned unless you explicitly coordinate access.
  • Avoid oversubscription by balancing your own threads with OpenCV's internal parallelism.

Course illustration
Course illustration

All Rights Reserved.