OpenCV (Open Source Computer Vision Library) is the de-facto standard for real-time computer-vision and image-processing tasks. A modern C++ (C++17/20) workflow gives you:
std::thread
, ranges, and coroutines for
high-performance pipelines.
• OpenCV 4.11.0 (18 Feb 2025) is the latest stable branch.
• OpenCV 5.x (alpha) is under active development—major API
clean-ups, Apache-2 license, revamped dnn
module, FP16 support, and
universal intrinsics are planned.
# macOS + Homebrew
brew install opencv
# Ubuntu 22.04
sudo apt update && sudo apt install libopencv-dev python3-opencv
Building from source lets you enable CUDA, OpenCL, or extra modules
(e.g. opencv_contrib
):
git clone --depth 1 https://github.com/opencv/opencv.git
git clone --depth 1 https://github.com/opencv/opencv_contrib.git
cmake -S opencv -B build \
-DOPENCV_EXTRA_MODULES_PATH=../opencv_contrib/modules \
-DCMAKE_BUILD_TYPE=Release \
-DWITH_CUDA=ON -DWITH_OPENCL=ON \
-DBUILD_opencv_python3=OFF \
-DCMAKE_INSTALL_PREFIX=/usr/local
cmake --build build -j$(nproc)
sudo cmake --install build
# CMakeLists.txt (C++17 example)
cmake_minimum_required(VERSION 3.16)
project(OpencvDemo LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 17)
find_package(OpenCV 4.5 REQUIRED)
add_executable(demo main.cpp)
target_link_libraries(demo PRIVATE opencv::opencv)
cv::Mat
— the N-dimensional MatrixMat()
Empty constructor (no allocation).Mat(rows, cols, type[, Scalar value])
— allocate & optionally
fill..at<T>(row,col)
— element access with bounds check..reshape()
, .clone()
, .release()
for
shape manipulation & memory control.// Create a 3×3 single-channel 8-bit matrix filled with 255
cv::Mat ones = cv::Mat::ones(3, 3, CV_8UC1) * 255;
• cv::Point<T>
(2-D/3-D points),
• cv::Size
(width, height),
• cv::Scalar
(variadic pixel constants, e.g. B,G,R,A).
These are shallow POD-like wrappers, trivially copyable.
imread / imwrite
cv::Mat img = cv::imread("lena.png", cv::IMREAD_COLOR);
if(img.empty()) throw std::runtime_error("File not found");
cv::imwrite("copy.jpg", img);
imshow
, waitKey
)cv::imshow("Preview", img);
cv::waitKey(0); // block until key press
cv::VideoCapture cap(0); // webcam
cv::VideoWriter writer("out.mp4",
cv::VideoWriter::fourcc('a','v','c','1'),
30, cv::Size(640,480));
cv::Mat frame;
while(cap.read(frame)){
writer.write(frame);
cv::imshow("Live", frame);
if(cv::waitKey(1)==27) break; // Esc
}
cvtColor
cv::Mat gray;
cv::cvtColor(img, gray, cv::COLOR_BGR2GRAY);
resize
cv::Mat small;
cv::resize(img, small, cv::Size(), 0.5, 0.5, cv::INTER_AREA);
GaussianBlur
— low-pass, remove high-freq noise.medianBlur
— salt-and-pepper denoising.bilateralFilter
— edge-preserving smoothing.cv::Mat blur;
cv::GaussianBlur(img, blur, cv::Size(5,5), 1.2);
cv::Mat edges;
cv::Canny(gray, edges, 50, 150);
cv::Mat binary;
cv::threshold(gray, binary, 128, 255, cv::THRESH_BINARY | cv::THRESH_OTSU);
auto kernel = cv::getStructuringElement(cv::MORPH_RECT, {3,3});
cv::Mat dilated;
cv::dilate(binary, dilated, kernel);
cv::Point2f srcTri[3]{{0,0},{img.cols-1,0},{0,img.rows-1}};
cv::Point2f dstTri[3]{{0,0},{img.cols*0.8f,0},{img.cols*0.2f,img.rows*0.9f}};
auto M = cv::getAffineTransform(srcTri,dstTri);
cv::Mat warped;
cv::warpAffine(img, warped, M, img.size());
auto R = cv::getRotationMatrix2D({img.cols/2.f,img.rows/2.f}, 45, 1.0);
cv::warpAffine(img, warped, R, img.size());
cv::Mat canvas = cv::Mat::zeros(400,600,CV_8UC3);
cv::rectangle(canvas, {50,50},{200,150}, {255,0,0}, 2);
cv::circle(canvas, {300,200}, 80, {0,255,0}, cv::FILLED);
cv::putText(canvas, "OpenCV!", {60,300},
cv::FONT_HERSHEY_SIMPLEX, 1.2, {255,255,255}, 2);
auto orb = cv::ORB::create(1000);
std::vector<cv::KeyPoint> kps1, kps2;
cv::Mat desc1, desc2;
orb->detectAndCompute(img1, cv::noArray(), kps1, desc1);
orb->detectAndCompute(img2, cv::noArray(), kps2, desc2);
cv::BFMatcher matcher(cv::NORM_HAMMING);
std::vector<cv::DMatch> matches;
matcher.match(desc1, desc2, matches);
Starting from OpenCV 4.4, SIFT’s patent expired—now fully in the main
tree; simply use cv::SIFT::create()
.
std::vector<cv::Point3f> objPts;
for(int i=0;i<9;i++)
for(int j=0;j<6;j++)
objPts.emplace_back(i*25.f, j*25.f, 0); // 25 mm grid
std::vector<std::vector<cv::Point3f>> obj(1,objPts);
std::vector<std::vector<cv::Point2f>> img(1,imageCorners);
cv::Mat K, distCoeffs;
cv::calibrateCamera(obj, img, gray.size(), K, distCoeffs, cv::noArray(), cv::noArray());
cv::Ptr<cv::StereoSGBM> sgbm = cv::StereoSGBM::create(0, 128, 5);
cv::Mat disparity16S;
sgbm->compute(leftGray, rightGray, disparity16S);
cv::Mat disp;
disparity16S.convertTo(disp, CV_32F, 1/16.0f);
dnn
) Moduleauto net = cv::dnn::readNetFromONNX("yolov8n.onnx");
net.setPreferableBackend(cv::dnn::DNN_BACKEND_CUDA);
net.setPreferableTarget(cv::dnn::DNN_TARGET_CUDA_FP16);
cv::Mat blob = cv::dnn::blobFromImage(img, 1/255.0, {640,640},
{0,0,0}, true, false);
net.setInput(blob);
auto outs = net.forward(); // returns cv::Mat
permute
.net.enableWinograd()
for faster 3×3 conv (CPU).cv::vconcat
.cv::UMat uimg;
img.copyTo(uimg); // lazy GPU upload
cv::GaussianBlur(uimg, uimg, {7,7}, 1.5); // runs on GPU if available
Declarative data-flow graphs with lazy compilation; outperform imperative pipelines on heterogeneous targets.
OpenCV uses OpenMP/TBB by default. You can wrap
cv::parallel_for_
for custom loops.
cv::Mat
move-semantics (no deep copy on assignment).constexpr
& enum class
for flag sets.std::filesystem
for portable paths.fmt
& spdlog
for logging.opencv_perf
.#opencv
Slack & Forum