Skip to content

COMP: Migrate OpenCV Video Bridge from legacy C API to C++ API#5994

Merged
hjmjohnson merged 4 commits intoInsightSoftwareConsortium:mainfrom
hjmjohnson:opencv-videoio-smart-pointers
Apr 1, 2026
Merged

COMP: Migrate OpenCV Video Bridge from legacy C API to C++ API#5994
hjmjohnson merged 4 commits intoInsightSoftwareConsortium:mainfrom
hjmjohnson:opencv-videoio-smart-pointers

Conversation

@hjmjohnson
Copy link
Copy Markdown
Member

@hjmjohnson hjmjohnson commented Mar 31, 2026

Summary

Migrates Modules/Video/BridgeOpenCV from the OpenCV legacy C API to the
modern C++ API. The C API (IplImage*, CvCapture*, CvVideoWriter*,
cvCaptureFromFile, cvQueryFrame, CV_CAP_PROP_*, etc.) was deprecated in
OpenCV 2.x, moved to optional headers in OpenCV 3.x, and removed entirely in
OpenCV 4.0
, causing build failures on any system with OpenCV 4+.

Changes per commit

1. Fix OpenCVBasicTypeBridge template deduction (OpenCV 3/4)

  • The cv::Matx<T, VRows, VColumns> specialization used unsigned int template
    parameters but cv::Matx uses int. Cast (unsigned int)VRows in the partial
    specialization to allow the compiler to deduce both sides correctly.

2. Migrate OpenCVVideoIO to C++ API

  • Remove four custom deleter structs (IplImageDeleter, CvCaptureDeleter, etc.)
    and all std::unique_ptr wrappers around C API types.
  • Replace member types with RAII C++ value types: cv::Mat, cv::VideoCapture,
    cv::VideoWriter — these manage their own lifetime with no custom cleanup needed.
  • Rewrite all methods using the C++ API: cv::VideoCapture::isOpened(),
    operator>> for frame reads, cv::VideoWriter::write(), cv::cvtColor(),
    cv::VideoWriter::fourcc().
  • Replace memcpy with std::copy_n for type-safe buffer copy.
  • Remove conditional OpenCV 2.4/3.x C header includes; use only
    opencv2/videoio.hpp and opencv2/imgproc.hpp.

3. Migrate OpenCVVideoCapture and test files to C++ API

  • Replace IplImage* frame handling in retrieve() with cv::Mat non-owning
    constructor wrapping the ITK frame buffer directly (no copy needed).
  • Replace CV_FOURCC(...) macro with cv::VideoWriter::fourcc(...) (×2 in
    constructors).
  • Replace all CV_CAP_PROP_* macros with cv::CAP_PROP_* scoped enums in
    set()/get() methods.
  • In test files: replace CvCapture*/cvCaptureFromCAM/cvReleaseCapture with
    cv::VideoCapture; replace all remaining CV_CAP_PROP_*/CV_FOURCC macro
    usage; remove legacy C headers (videoio_c.h, imgproc_c.h, highgui.h).

Backward Compatibility

All C++ API replacements used have been available since OpenCV 2.0 or earlier.
This PR does not raise the minimum required OpenCV version.

Legacy C API (removed in OpenCV 4) Modern C++ Replacement Available Since
IplImage* / cvCreateImageHeader cv::Mat(rows, cols, type, ptr) OpenCV 1.x (C++ wrappers)
CvCapture* / cvCaptureFromFile cv::VideoCapture(filename) OpenCV 2.0
CvCapture* / cvCaptureFromCAM cv::VideoCapture(index) OpenCV 2.0
cvQueryFrame / cvGetNextImage cv::VideoCapture::operator>> OpenCV 2.0
cvGetCaptureProperty cv::VideoCapture::get(propId) OpenCV 2.0
cvSetCaptureProperty cv::VideoCapture::set(propId, val) OpenCV 2.0
CvVideoWriter* / cvCreateVideoWriter cv::VideoWriter(file, fourcc, fps, size) OpenCV 2.0
cvWriteFrame cv::VideoWriter::write(frame) OpenCV 2.0
cvReleaseCapture / cvReleaseVideoWriter RAII (destructor / .release()) OpenCV 2.0
cvCvtColor / CV_BGR2RGB cv::cvtColor / cv::COLOR_BGR2RGB OpenCV 2.0
CV_FOURCC('M','P','4','2') macro cv::VideoWriter::fourcc('M','P','4','2') OpenCV 2.1
CV_CAP_PROP_POS_FRAMES macro cv::CAP_PROP_POS_FRAMES scoped enum OpenCV 3.0
CV_CAP_PROP_FPS macro cv::CAP_PROP_FPS scoped enum OpenCV 3.0
CV_CAP_PROP_FOURCC macro cv::CAP_PROP_FOURCC scoped enum OpenCV 3.0
CV_CAP_PROP_FRAME_WIDTH/HEIGHT macros cv::CAP_PROP_FRAME_WIDTH/HEIGHT enums OpenCV 3.0
CV_CAP_PROP_POS_AVI_RATIO macro cv::CAP_PROP_POS_AVI_RATIO scoped enum OpenCV 3.0
CV_CAP_PROP_POS_MSEC macro cv::CAP_PROP_POS_MSEC scoped enum OpenCV 3.0

Note: The cv::CAP_PROP_* scoped enums were introduced in OpenCV 3.0.
The CV_CAP_PROP_* macro aliases for them still exist in OpenCV 3.x (via
videoio_c.h) but were removed in OpenCV 4.0. Since ITK already requires
OpenCV 3+ for other reasons, using cv::CAP_PROP_* directly is safe.

Test plan

  • Verify CI builds pass on Linux, macOS, Windows with OpenCV 4.x
  • Verify all OpenCV Video Bridge tests pass (ctest -R OpenCV)

🤖 Generated with Claude Code

@github-actions github-actions bot added type:Compiler Compiler support or related warnings area:Video Issues affecting the Video module labels Mar 31, 2026
@hjmjohnson hjmjohnson marked this pull request as draft March 31, 2026 14:57
@hjmjohnson hjmjohnson force-pushed the opencv-videoio-smart-pointers branch from d0d56ba to 982662e Compare March 31, 2026 18:31
@github-actions github-actions bot added the type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct label Mar 31, 2026
@hjmjohnson hjmjohnson changed the title COMP: Use std::unique_ptr with custom deleters in OpenCVVideoIO COMP: Migrate OpenCV Video Bridge from legacy C API to C++ API Mar 31, 2026
Comment on lines +167 to +170
using ITKDataType = itk::Matrix<T, VRows, VColumns>;
using ITKDataType = itk::Matrix<T, (unsigned int)VRows, (unsigned int)VColumns>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't use C-style cast! Is an explicit conversion really necessary? (If so, you may consider unsigned{VRows} and unsigned{VColumns}, which are 100% type safe.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — replaced both C-style casts with static_cast<unsigned int>(VRows) and static_cast<unsigned int>(VColumns). The explicit conversion is necessary here: the template parameter VRows is deduced as int (to match cv::Matx), but itk::Matrix requires unsigned int, so a cast in the specialization pattern is required for deduction to succeed. Squashed into the same commit.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, but are you sure that the explicit cast is necessary? Do you get a warning, otherwise?

itk::Matrix is declared as follows:

template <typename T, unsigned int VRows = 3, unsigned int VColumns = 3> 
class ITK_TEMPLATE_EXPORT Matrix 

template <typename T, unsigned int VRows = 3, unsigned int VColumns = 3>
class ITK_TEMPLATE_EXPORT Matrix

I think it should be perfectly fine to pass a signed integer as argument for VRows or VColumns, for example itk::Matrix<float, 2, 2>. (2 is a signed int.)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did get a failure that this fixed. It is hard to test this code in all scenarios, so I figured that being explicit is a "won't hurt" condition.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, there is a guideline: Avoid casts

So I would only add static_cast when it is really necessary 🤷 That's why I suggested to just remove the cast. Or otherwise use modern T{ x } conversion.

hjmjohnson and others added 3 commits March 31, 2026 14:16
cv::Matx uses int template params; itk::Matrix uses unsigned int.
When the same non-type parameter appeared in both sides of the partial
specialization pattern, the compiler could not unify the deduction from
itk::Matrix<T, uint(2), uint(3)> (unsigned int) with cv::Matx<T, 2, 3>
(int), causing the specialization to be dropped and the empty primary
template to be selected instead.

Fix: use int for VRows/VColumns (matching OpenCV) as the deduction
variable, and place a static_cast<unsigned int> in the ITK Matrix side of the
pattern. C++17 allows integral-cast constant expressions in partial
specialization patterns, and the compiler can invert a simple integral
cast during deduction.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace CV_CAP_PROP_* macros with cv::CAP_PROP_* scoped enums,
CV_FOURCC with cv::VideoWriter::fourcc(), CvCapture*/cvCaptureFromCAM/
cvReleaseCapture with cv::VideoCapture, and remove legacy C API headers
(videoio_c.h, imgproc_c.h, highgui.h) that were removed in OpenCV 4.
@hjmjohnson hjmjohnson force-pushed the opencv-videoio-smart-pointers branch from 982662e to 9cb4845 Compare March 31, 2026 19:17
@hjmjohnson hjmjohnson marked this pull request as ready for review April 1, 2026 01:01
@hjmjohnson hjmjohnson requested a review from dzenanz April 1, 2026 01:04
@hjmjohnson
Copy link
Copy Markdown
Member Author

@dzenanz This looks like perhaps it is something that supports Kitware projects. Do you know a good candidate to review this PR?

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 1, 2026

Greptile Summary

This PR migrates Modules/Video/BridgeOpenCV from the OpenCV C API (removed in OpenCV 4.0) to the modern C++ API, fixing build failures on any system with OpenCV 4+. All seven changed files are updated consistently: raw pointer members (IplImage*, CvCapture*, CvVideoWriter*) are replaced with RAII value types (cv::Mat, cv::VideoCapture, cv::VideoWriter), all deprecated macros (CV_FOURCC, CV_CAP_PROP_*) are replaced with their scoped-enum equivalents, and version-guarded legacy header blocks are collapsed to clean modern includes. The template deduction fix in itkOpenCVBasicTypeBridge.h is a correct solution to the unsigned int vs int mismatch between itk::Matrix and cv::Matx. The migration is thorough and the RAII ownership model is applied correctly throughout.

Key findings:

  • OpenWriter() in itkOpenCVVideoIO.cxx sets m_WriterOpen = true unconditionally without checking m_Writer.isOpened() — if the writer fails to open silently, all subsequent Write() calls will no-op, discarding frames with no diagnostic output.
  • The new isContinuous() guard added to the cross-validation memcmp in itkOpenCVVideoIOTest.cxx silently bypasses the pixel comparison instead of failing the test when the guard is false.
  • ReadImageInformation() correctly delegates file-existence/openability checking to CanReadFile() before re-opening, so no missing isOpened() guard is needed on the file path.

Confidence Score: 5/5

  • Safe to merge — both remaining findings are P2 quality/robustness suggestions with no impact on the primary read path.
  • All findings are P2: the OpenWriter check is a pre-existing gap that the new API makes easy to close, and the isContinuous() guard affects only test coverage quality. Neither blocks correct behavior on the read path or represents a regression introduced by this PR. The migration itself is correct and complete.
  • itkOpenCVVideoIO.cxx (OpenWriter check) and itkOpenCVVideoIOTest.cxx (isContinuous guard)

Important Files Changed

Filename Overview
Modules/Video/BridgeOpenCV/include/itkOpenCVBasicTypeBridge.h Template parameters changed from unsigned int to int (matching cv::Matx) with static_cast<unsigned int> for the itk::Matrix partial specialization; correctly resolves deduction mismatch between OpenCV and ITK template conventions.
Modules/Video/BridgeOpenCV/include/itkOpenCVVideoCapture.hxx Removes all legacy IplImage/C-header includes; replaces CV_FOURCC/CV_CAP_PROP_* macros with C++ equivalents; retrieve() now wraps the ITK buffer in a non-owning cv::Mat (correct rows/cols ordering) and uses cv::cvtColor directly — clean migration with correct dimension mapping.
Modules/Video/BridgeOpenCV/include/itkOpenCVVideoIO.h Cleans up version-guarded legacy includes; replaces IplImage*/CvCapture*/CvVideoWriter* raw pointer members with RAII cv::Mat/cv::VideoCapture/cv::VideoWriter value members; also removes the duplicate private: label.
Modules/Video/BridgeOpenCV/src/itkOpenCVVideoIO.cxx Full C→C++ API migration: removes manual cvRelease* calls, adds explicit RGBA read path, replaces memcpy with std::copy_n; OpenWriter() sets m_WriterOpen = true unconditionally without checking m_Writer.isOpened() — silent data loss if writer fails to open.
Modules/Video/BridgeOpenCV/test/itkOpenCVVideoIOTest.cxx Migrates cross-validation capture from CvCapture*/cvQueryFrame to cv::VideoCapture/operator>>; adds isContinuous() guard around memcmp that silently skips comparison when false rather than failing the test.
Modules/Video/BridgeOpenCV/test/itkOpenCVVideoCaptureTest.cxx Drops legacy videoio_c.h/imgproc_c.h conditional includes; replaces all CV_CAP_PROP_*/CV_FOURCC macros with cv::CAP_PROP_*/cv::VideoWriter::fourcc — straightforward, correct migration.
Modules/Video/BridgeOpenCV/test/itkOpenCVVideoIOFactoryTest.cxx Replaces CvCapture*/cvCaptureFromCAM/cvReleaseCapture with cv::VideoCapture/isOpened()/release() — clean, correct migration.

Sequence Diagram

sequenceDiagram
    participant Caller
    participant OpenCVVideoIO
    participant cv_VideoCapture as cv::VideoCapture
    participant cv_VideoWriter as cv::VideoWriter

    Note over OpenCVVideoIO: Read path (C++ API)
    Caller->>OpenCVVideoIO: ReadImageInformation()
    OpenCVVideoIO->>cv_VideoCapture: open(filename) / open(cameraIndex)
    cv_VideoCapture-->>OpenCVVideoIO: isOpened()
    OpenCVVideoIO->>cv_VideoCapture: operator>> (tempFrame)
    OpenCVVideoIO->>cv_VideoCapture: get(CAP_PROP_FRAME_COUNT/FPS/WIDTH/HEIGHT)
    cv_VideoCapture-->>OpenCVVideoIO: metadata

    Caller->>OpenCVVideoIO: Read(buffer)
    OpenCVVideoIO->>cv_VideoCapture: operator>> (tempIm)
    OpenCVVideoIO->>OpenCVVideoIO: cvtColor BGR→RGB (or BGRA→RGBA)
    OpenCVVideoIO->>OpenCVVideoIO: std::copy_n(m_CVImage.data → buffer)

    Note over OpenCVVideoIO: Write path (C++ API)
    Caller->>OpenCVVideoIO: SetWriterParameters(fps, dim, fourCC, nChannels)
    OpenCVVideoIO->>OpenCVVideoIO: VideoWriter::fourcc(...)
    Caller->>OpenCVVideoIO: Write(buffer)
    OpenCVVideoIO->>cv_VideoWriter: open(filename, fourcc, fps, size)
    OpenCVVideoIO->>OpenCVVideoIO: cvtColor (GRAY/RGB → BGR)
    OpenCVVideoIO->>cv_VideoWriter: write(m_CVImage)

    Note over OpenCVVideoIO: Cleanup (RAII)
    Caller->>OpenCVVideoIO: FinishReadingOrWriting()
    OpenCVVideoIO->>cv_VideoCapture: release()
    OpenCVVideoIO->>cv_VideoWriter: release()
Loading

Reviews (2): Last reviewed commit: "COMP: Replace legacy OpenCV C API in Vid..." | Re-trigger Greptile

@hjmjohnson hjmjohnson enabled auto-merge April 1, 2026 12:30
Pre-existing condition: OpenWriter() set m_WriterOpen = true
unconditionally after calling cvCreateVideoWriter() (C API) without
checking for a null return. The C++ API migration in the previous
commit preserved this bug by not calling m_Writer.isOpened() after
m_Writer.open().

OpenReader() already guards m_ReaderOpen = true with an isOpened()
check and throws on failure; align OpenWriter() to the same pattern.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Member

@dzenanz dzenanz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I have some program which uses Video\BridgeOpenCV\include\itkOpenCVBasicTypeBridge.h, and it keeps working after these changes.

@hjmjohnson hjmjohnson merged commit 01aa5e1 into InsightSoftwareConsortium:main Apr 1, 2026
13 of 18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:Video Issues affecting the Video module type:Compiler Compiler support or related warnings type:Testing Ensure that the purpose of a class is met/the results on a wide set of test cases are correct

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants