Liebe Leserinnen, liebe Leser,
wie es in dem letzten Beitrag 2018 erwähnt wurde, ich habe mich vor Kurzem in die OpenCV Bibliothek eingeführt, und während dieser Zeit habe ich (natürlich) die folgende Funktion begegnet: cv::namedWindow()
. Wie der Name und die Dokumentation es vermuten lässt, “erstellt sie ein Fenster, das als ein Platzhalter für Bilder und Trackbar verwendet werden kann”. Eine selbsterklärende Nutzung ist in den folgende Codeausschnitt zu sehen:
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);
}
Dieser Code tut, was drauf steht: Es zeigt ein Bild in einem Fenster namens “Image Window” an. In diesem einfachen Beispiel könnte der Aufruf der namedWindow
Funktion vermieden werden, weil imshow
auch ein Fenster erstellt, das das Bild anzeigt. Warum soll das dann benutzt werden? Der Zweck der namedWindow
Funktion ist die Nutztung des Fensters als die einzige Ausgang für alle Bilder, weil der nachfolgende Aufruf von imshow
ohne namedWindow
zu mehrere Fenster führen wird, auch wenn der Name dieser Fenster ist gleich:
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;
}
Die Nutzbarkeit von namedWindow
ist bei diesem Punkt selbsterklärend, aber der nicht so angekündigte Teil ist wie Scoping funktioniert. Was wäre, wenn die doInterestingStuff
Funktion mit einem Bild aufgerufen wird, ohne etwas zurückzugeben, und dann sollte die Ergebnis im Fenster dennoch gezeigt werden. Wenn man die Definition von namedWindow
unterscuht, wird man das sofort erkennen, dass diese Funktion keinen Rückgabewert hat, und deswegen kann das ergebene Fenster an der doInterestingStuff
Funktion nicht weitergegeben werden:
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);
// ...
}
Die Lösung ist ganz eifach: Gib nichts nirgendwo weiter. Anscheinend, namedWindow
erstellt ein global vorhandenes Fenster, und die einzige zu machende Sache ist der Aufruf von imshow
mit dem gleichen Namen wie namedWindow
:
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);
// ...
}
Man sollte dennoch mit dem Aufruf von namedWindow
vorsichtig sein, weil es ungeachtet des Aufrufortes andauert, auch wenn der Codeteil schnon den Scope verlässt:
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;
}
Einfach gesagt bedeutet das, dass alle durch namedWindow
erzeugtes Fenster verfolgen sollen, genauso wie ein durch den new
Operator erzeugtes Objekt. Man könnte an dieser Funktion denken, wie es ein Äquivalent von new
für OpenCV Fenster, wo das Gegenstück cv::destroyWindow
(oder cv::destroyAllWindows
) ist, genauso wie delete
ist für andere Objekte. Das hat ernste Auswirkungen, weil Speicherlecks (besonders bei Videodateien) garantiert sind, falls man nicht genug vorsichtig ist.
Die Lektion des Tages ist:
cv::namedWindow
sollte verwendet werden, falls ein einziges Fenster für alle Darstellungen genug ist. Man sollte dennoch mit der Speicherverwaltung vorsichtig sein, wenn man kompliziertere Programme hat, weil die Fenster, die auf diese Weise erstellt wurden, nicht automatisch zerstört werden.
Wie immer, vielen Dank fürs Lesen.