Dear Readers!
As mentioned in the last post of 2018, I’ve been spending my time with familiatizing myself with OpenCV, and during this time I (obviously) came across the following function: cv::namedWindow()
. As the name and documentation of this function suggests, it “creates a window that can be used as a placeholder for images and trackbars”. An obvious usage can be seen in the following snippet:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
cv::Mat img = cv::imread("./Img.png", cv::IMREAD_COLOR);
if (img.empty()) {
std::cerr << "Couldn't load image" << std::endl;
return -1;
}
cv::namedWindow("Image Window");
cv::imshow("Image Window", img);
cv::Mat img2 = doInterestingStuff(img);
// already open window will be used to display img2
cv::imshow("Image Window", img2);
cv::waitKey(0);
}
This will do what it says will do: display an image in a window named “Image Window”. In this simple case we could even omit calling the namedWindow
function, as calling imshow
by itself will create the window and display the image. So why use it? The purpose of namedWindow
is to use the window it provides as a single output for all our displaying needs, as calling imshow
subsequently more than once without it will result in multiple windows, even if the name of these windows is the same:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int main() {
cv::Mat img = cv::imread("./Img.png", cv::IMREAD_COLOR);
if (img.empty() || img2.empty()) {
std::cerr << "Couldn't load an image" << std::endl;
return -1;
}
cv::imshow("Image Window", img);
cv::Mat img2 = doInterestingStuff(img);
// a second window will be opened here
cv::imshow("Image window", img2);
cv::waitKey(0);
return 0;
}
The usefulness of namedWindow
is obvious at this point, but the less advertised part is how its scoping works. Let’s say we want to doInterestingStuff
with the image without returning a thing from that function, and yet still display the result in our window. If we examine how namedWindow
is defined, we quickly realize that it doesn’t return a thing, so we cannot pass the resulting window to doInterestingStuff
:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// none of it works
void doInterestingStuff(cv::Mat img, ??? window)
{
// ...
// display processed image
}
int main() {
// ...
cv::namedWindow("Image Window");
doInterestingStuff(img, window);
// ...
}
The solution is rather simple: don’t pass anything to anywhere. namedWindow
apparently creates a globally available window and the only thing one needs to do is to call imshow with the same window name as namedWindow
was called with:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void doInterestingStuff(cv::Mat img)
{
// ...
cv::imshow("Image Window", img);
}
int main() {
// ...
cv::namedWindow("Image Window");
doInterestingStuff(img);
// ...
}
Be careful though how and where you call namedWindow
, as it will persist no matter where you call it from, even if the code segment went out of scope already:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void doInterestingStuff(cv::Mat img)
{
cv::namedWindow("Image Window");
// ...
cv::imshow("Image Window", img);
}
int main() {
cv::Mat img = cv::imread("./Img.png", cv::IMREAD_COLOR);
cv::Mat img2 = cv::imread("./Img2.png", cv::IMREAD_COLOR);
if (img.empty() || img2.empty()) {
std::cerr << "Couldn't load an image" << std::endl;
return -1;
}
doInterestingStuff(img2);
// img will be shown instead of img2 in the window here. No new window is opened.
cv::imshow("Image Window", img);
cv::waitKey(0);
return 0;
}
Basically what this all means, is that just like any object created with the new
operator, one needs to keep track of all windows created with namedWindow
. We can think of this function as the equivalent of new
for opencv windows, where its counterpart is cv::destroyWindow
(or cv::destroyAllWindows
), just like delete
for other objects. This has serious implications, as if one is not careful enough, memory leaks are a given (especially when video files are involved).
So the lesson of the day is:
Use
cv::namedWindow
if you want a single window for all your display needs, but be careful about memory management if you have more complex solutions, as windows created in this fashion do not clean up after themselves automatically.
As always, thanks for reading.