I should rather think about dividing my program into processing part and visualization part?
I don't have a good comprehensive answer (i.e. I don't think I have found a solution to the problem), but here is my approach.
I added "debug image listener interface" to the main algorithm classes. The interface contains a method that will "receive" some images which are generated during the normal execution of the main algorithm. The implementer of that listener class will decide what to do with that image, such as saving it.
The separation of "extension points" (the interface, which serves as callbacks or hooks) and the implementations (the debug image listener implementations, which are responsible for saving them to disk or analyzing them on-the-fly) is what allows separation of production code and development code.
This is acceptable in C++ because, in C++, OpenCV matrix handles (
cv::Mat) behave similar to references to the underlying matrices. Passing a handle for read-only purpose does not require a copy of the underlying memory. This means handle-passing is memory-efficient.
For more complicated use cases, this is what I have tried:
- Pass a descriptive string along with each image. The string may describe the step which generates that intermediate image.
- Pass a string which can be used as a filename (i.e. characters from the descriptive string which are not valid as filename characters had been stripped out)
- Add a second method to the interface, which allows interrogating whether a particular intermediate image should be saved or not.
- Convert between multi-channel and high-bit-depth (integer-valued) images via Morton code, so that matrices that are not PNG-compatible can be encoded in PNG.
As you can see, logging of 2D images requires a lot of small tricks, and appears to be far from being a solved problem.
Finally, image algorithm development sometimes require a lot of R&D, which is typically done as a separate software project (or "software artifact" - tangible by-products from software development process). These artifacts are worth maintenance. Without these artifacts, fixing of algorithm-level defects or algorithm-level improvements, or even parameter tuning (as opposed to implementation or coding defects) can become impossible.