Praktikum 1 - Aufnahme und Aufbereitung der Daten einer Stereo-Kamera
Aufgabenblatt
Gesamtziel
Aufeinander aufbauend wird in den drei Praktikumsversuchen ein gr¨oßeres Projekt bearbeitet. Ziel ist es, Handbewegungen in den aufgenommenen Bildern zu unterscheiden. Die Implementierung der Bildverarbeitungsmethoden erfolgt mit der C++ Bibliothek OpenCV und dem Kamera SDK DepthAI.
Lernziele des ersten Praktikums
- Lesen fremden Codes
- Einbinden und Verwendung von externen Bibliotheken einschließlich des Gebrauchs der Dokumentation
- Einlesen von Datein von der kamera und Darstellung von Bildern mittels OpenCV
- Schreiben und Lesen von Videodatein
- Arbeiten mit Übergabeparametern
Aufgaben
Nutzen Sie für Ihre Implementierung das Beispielprogramm RGB_Depth_alignment.cpp aus dem moodle-Kurs. Achten Sie bei allen Ergänzungen darauf, alle m¨oglichen auftretenden Fehler durch eine geeignete Fehlerbehandlung abzufangen.
Hol dir den Source Code:
RGB_Depth_alignment.cpp (Source Code)- Vereinfachen Sie den Code zunächst so, dass nur noch das Tiefenbild und das Farbbild angezeigt werden. Nutzen Sie zur Colorierung des Tiefenbildes die JET-Farbtabelle
- Untersuchen Sie den Einfluss eines erweiterten Disparitätsbereichs, um dichter an der Kamera befindliche Objekte auch noch darstellen zu können (Stichwort: extended_disparity).
- Ergänzen Sie das Programm so, dass Übergabeparameter verwendet werden können. Bei Aufruf des Programms mit einer “1” soll später die Auswertung des Live-Kamerabildes starten.
- Bei Übergabe einer “2” sollen zusätzlich zur Anzeige zwei Videos (Tiefenbild, Farbbild) aufgezeichnet werden. In dem Fall soll später keine Auswertung erfolgen. Nutzen Sie hierfür die OpenCV-Klasse VideoWriter.
- Bei Übergabe einer “3” soll die Auswertung auf den aufgezeichneten Videos erfolgen. In dem Fall soll kein Zugriff auf die Kamera erforderlich sein. Hierzu können Sie die VideoCapture-Klasse verwenden.
Lösung
Aufgabe 1
Entferne die markierten Stellen aus dem Code um nur noch das Tiefenbild und das Farbbild anzuzeigen:
auto rgbWindowName = "rgb";
auto depthWindowName = "depth";
auto blendedWindowName = "rgb-depth";
cv::namedWindow(rgbWindowName);
cv::namedWindow(depthWindowName);
cv::namedWindow(blendedWindowName);
int defaultValue = (int)(rgbWeight * 100);
cv::createTrackbar("RGB Weight %", blendedWindowName, &defaultValue, 100, updateBlendWeights);Um die JET-Farbtabelle zu nutzen, ändere in der Funktion cv::applyColorMap() COLORMAP_HPT zu COLORMAP_JET:
if(1) cv::applyColorMap(frame[name], frame[name], cv::COLORMAP_JET);Aufgabe 2
Füge setExtendedDisparity zu der Stereo-Kamera hinzu:
stereo->setDefaultProfilePreset(dai::node::StereoDepth::PresetMode::HIGH_DENSITY);
// LR-check is required for depth alignment
stereo->setLeftRightCheck(true);
stereo->setDepthAlign(dai::CameraBoardSocket::CAM_A);
stereo->setExtendedDisparity(true); // Aktiviere extended disparityAufgabe 3
Um Übergabeparameter zu verwenden, muss die Funktion main() angepasst werden:
int main(int argc, char** argv) {
int arg = 0;
if (argc > 1) {
arg = std::stoi(argv[1]);
}
if(arg == 3) {
playVideo();
return 0;
}
// ...
}Aufgabe 4
Damit wir Videos aufzeichnen können, müssen wir den Inhalt in der while(true)-Schleife anpassen:
while(true) {
std::unordered_map<std::string, std::shared_ptr<dai::ImgFrame>> latestPacket;
auto queueEvents = device.getQueueEvents(queueNames);
for(const auto& name : queueEvents) {
auto packets = device.getOutputQueue(name)->tryGetAll<dai::ImgFrame>();
auto count = packets.size();
if(count > 0) {
latestPacket[name] = packets[count - 1];
}
}
for(const auto& name : queueNames) {
if(latestPacket.find(name) != latestPacket.end()) {
if(name == depthWindowName) {
frame[name] = latestPacket[name]->getFrame();
auto maxDisparity = stereo->initialConfig.getMaxDisparity();
frame[name].convertTo(frame[name], CV_8UC1, 255. / maxDisparity);
cv::applyColorMap(frame[name], frame[name], cv::COLORMAP_JET);
} else {
frame[name] = latestPacket[name]->getCvFrame();
}
cv::imshow(name, frame[name]);
if(arg == 2) {
bool isColor = (frame[name].type() == CV_8UC3);
if(!writerInitializedrgb || !writerInitializeddepth) {
int codec = cv::VideoWriter::fourcc('M','J','P','G');
double fps = 30.0;
if(name == "depth") {
writerdepth.open("live_depth.avi", codec, fps, frame[name].size(), isColor);
writerInitializeddepth = true;
}
if(name == "rgb") {
writerrgb.open("live_rgb.avi", codec, fps, frame[name].size(), isColor);
writerInitializedrgb = true;
}
std::cout << "Press q to terminate" << std::endl;
}
if(name == "depth") {
writerdepth.write(frame[name]);
}
if(name == "rgb") {
writerrgb.write(frame[name]);
}
}
}
}
int key = cv::waitKey(1);
if(key == 'q' || key == 'Q') {
if(arg == 2) {
writerrgb.release();
writerdepth.release();
}
return 0;
}
}Aufgabe 5
Hierfür nutzen wir eine separate Funktion playVideo() außerhalb der main()-Funktion. In Aufgabe 3 haben wir bereits einen Verweis auf diese Funktion hinzugefügt.:
int playVideo() {
while(true) {
cv::VideoCapture caprgb("live_rgb.avi"); // RGB Video
cv::VideoCapture capdepth("live_depth.avi"); // Depth Video
// Überprüfen ob Videos geöffnet werden konnten
if (!caprgb.isOpened() || !capdepth.isOpened()) {
std::cerr << "Error opening video stream or file" << std::endl;
return -1;
}
cv::Mat frame;
cv::Mat frame2;
while(true) {
caprgb >> frame; // RGB Frame
capdepth >> frame2; // Depth Frame
if (frame.empty() || frame2.empty()) {
std::cerr << "End of video stream" << std::endl;
return 0;
}
// Display RGB and Depth Frame
cv::imshow("rgb", frame);
cv::waitKey(30);
cv::imshow("depth", frame2);
cv::waitKey(30);
}
cv::destroyAllWindows();
}
return 0;
}Abschluss
Vollständiger Source Code:
RGB_Depth_alignment_complete.cpp (Source Code)