Commit 6deedee8969a7ccae3e743508a869e26ef8e4d1b

Authored by Austin Blanton
1 parent c982b052

Add SeqReader to Stream to process Caltech Pedestrian seq files

Showing 1 changed file with 202 additions and 11 deletions
openbr/plugins/stream.cpp
  1 +#include <fstream>
1 #include <QReadWriteLock> 2 #include <QReadWriteLock>
2 #include <QWaitCondition> 3 #include <QWaitCondition>
3 #include <QThreadPool> 4 #include <QThreadPool>
4 #include <QSemaphore> 5 #include <QSemaphore>
5 #include <QMap> 6 #include <QMap>
6 -#include <opencv/highgui.h> 7 +#include <QQueue>
7 #include <QtConcurrent> 8 #include <QtConcurrent>
  9 +#include <opencv/highgui.h>
8 #include "openbr_internal.h" 10 #include "openbr_internal.h"
9 -  
10 #include "openbr/core/common.h" 11 #include "openbr/core/common.h"
11 #include "openbr/core/opencvutils.h" 12 #include "openbr/core/opencvutils.h"
12 #include "openbr/core/qtutils.h" 13 #include "openbr/core/qtutils.h"
13 14
14 using namespace cv; 15 using namespace cv;
  16 +using namespace std;
15 17
16 namespace br 18 namespace br
17 { 19 {
@@ -203,6 +205,13 @@ public: @@ -203,6 +205,13 @@ public:
203 virtual bool getNextTemplate(Template & output)=0; 205 virtual bool getNextTemplate(Template & output)=0;
204 protected: 206 protected:
205 Template basis; 207 Template basis;
  208 + string getAbsolutePath(QString filename)
  209 + {
  210 + // Yes, we should specify absolute path:
  211 + // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python
  212 + QString fileName = (Globals->path.isEmpty() ? "" : Globals->path + "/") + filename;
  213 + return QFileInfo(fileName).absoluteFilePath().toStdString();
  214 + }
206 }; 215 };
207 216
208 static QMutex openLock; 217 static QMutex openLock;
@@ -236,12 +245,9 @@ public: @@ -236,12 +245,9 @@ public:
236 qDebug("Video not open!"); 245 qDebug("Video not open!");
237 } 246 }
238 } else { 247 } else {
239 - // Yes, we should specify absolute path:  
240 - // http://stackoverflow.com/questions/9396459/loading-a-video-in-opencv-in-python  
241 - QString fileName = (Globals->path.isEmpty() ? "" : Globals->path + "/") + input.file.name;  
242 // On windows, this appears to not be thread-safe 248 // On windows, this appears to not be thread-safe
243 QMutexLocker lock(&openLock); 249 QMutexLocker lock(&openLock);
244 - video.open(QFileInfo(fileName).absoluteFilePath().toStdString()); 250 + video.open(getAbsolutePath(input.file.name));
245 } 251 }
246 252
247 return video.isOpened(); 253 return video.isOpened();
@@ -319,6 +325,182 @@ protected: @@ -319,6 +325,182 @@ protected:
319 bool data_ok; 325 bool data_ok;
320 }; 326 };
321 327
  328 +class SeqReader : public TemplateProcessor
  329 +{
  330 +public:
  331 + SeqReader() {}
  332 +
  333 + bool open(Template &input)
  334 + {
  335 + basis = input;
  336 +
  337 + seqFile.open(getAbsolutePath(input.file.name).c_str(), ios::in | ios::binary | ios::ate);
  338 + if (!isOpen()) return false;
  339 +
  340 + int headSize = 1024;
  341 + // start at end of file to get full size
  342 + int fileSize = seqFile.tellg();
  343 + if (fileSize < headSize) {
  344 + qDebug("No header in seq file");
  345 + return false;
  346 + }
  347 +
  348 + // first 4 bytes store 0xEDFE, next 24 store 'Norpix seq '
  349 + char *firstFour = new char[4], *nextTwentyFour;
  350 + seqFile.seekg(0, ios::beg);
  351 + seqFile.read(firstFour, 4);
  352 + nextTwentyFour = readText(24);
  353 + if (firstFour[0] != (char)0xED || firstFour[1] != (char)0xFE || strncmp(nextTwentyFour, "Norpix seq", 10) != 0) {
  354 + qDebug("Invalid header in seq file");
  355 + return false;
  356 + }
  357 +
  358 + // next 8 bytes for version (skipped below) and header size (1024), then 512 for descr
  359 + seqFile.seekg(4, ios::cur);
  360 + int hSize = readInt();
  361 + if (hSize != headSize) {
  362 + qDebug("Invalid header size");
  363 + return false;
  364 + }
  365 + char *desc = readText(512);
  366 + basis.file.set("Description", QString(desc));
  367 +
  368 + width = readInt();
  369 + height = readInt();
  370 + // get # channels from bit depth
  371 + numChan = readInt()/8;
  372 + int imageBitDepthReal = readInt();
  373 + if (imageBitDepthReal != 8) {
  374 + qDebug("Invalid bit depth");
  375 + return false;
  376 + }
  377 + // the size of just the image part of a raw img
  378 + imgSizeBytes = readInt();
  379 +
  380 + int imgFormatInt = readInt();
  381 + if (imgFormatInt == 100 || imgFormatInt == 200 || imgFormatInt == 101) {
  382 + imgFormat = "raw";
  383 + } else if (imgFormatInt == 102 || imgFormatInt == 201 || imgFormatInt == 103 ||
  384 + imgFormatInt == 1 || imgFormatInt == 2) {
  385 + imgFormat = "compressed";
  386 + } else {
  387 + qFatal("unsupported image format");
  388 + }
  389 +
  390 + numFrames = readInt();
  391 + // skip empty int
  392 + seqFile.seekg(4, ios::cur);
  393 + // the size of a full raw file, with extra crap after img data
  394 + trueImgSizeBytes = readInt();
  395 +
  396 + // gather all the frame positions in an array
  397 + seekPos.reserve(numFrames);
  398 + // start at end of header
  399 + seekPos.append(headSize);
  400 + // extra 8 bytes at end of img
  401 + int extra = 8;
  402 + for (int i=1; i<numFrames; i++) {
  403 + int s;
  404 + // compressed images have different sizes
  405 + // the first byte at the beginning of the file
  406 + // says how big the current img is
  407 + if (imgFormat == "compressed") {
  408 + int lastPos = seekPos[i-1];
  409 + seqFile.seekg(lastPos, ios::beg);
  410 + int currSize = readInt();
  411 + s = lastPos + currSize + extra;
  412 +
  413 + // but there might be 16 extra bytes instead of 8...
  414 + if (i == 1) {
  415 + seqFile.seekg(s, ios::beg);
  416 + char *zero = new char[1];
  417 + seqFile.read(zero, 1);
  418 + if (zero[0] == 0) {
  419 + s += 8;
  420 + extra += 8;
  421 + }
  422 + }
  423 + }
  424 + // raw images are all the same size
  425 + else {
  426 + s = headSize + (i*trueImgSizeBytes);
  427 + }
  428 +
  429 + seekPos.enqueue(s);
  430 + }
  431 +
  432 + return true;
  433 + }
  434 +
  435 + bool isOpen()
  436 + {
  437 + return seqFile.is_open();
  438 + }
  439 +
  440 + void close()
  441 + {
  442 + seqFile.close();
  443 + }
  444 +
  445 + bool getNextTemplate(Template &output)
  446 + {
  447 + if (!isOpen()) {
  448 + qDebug("Seq not open");
  449 + return false;
  450 + }
  451 + // if we've reached the last frame, we're done
  452 + if (seekPos.size() == 0) return false;
  453 +
  454 + seqFile.seekg(seekPos.dequeue(), ios::beg);
  455 +
  456 + Mat temp;
  457 + // let imdecode do all the work to decode the compressed img
  458 + if (imgFormat == "compressed") {
  459 + int imgSize = readInt() - 4;
  460 + vector<char> imgBuf(imgSize);
  461 + seqFile.read(&imgBuf[0], imgSize);
  462 + // flags < 0 means load image as-is (keep color info if available)
  463 + imdecode(imgBuf, -1, &temp);
  464 + }
  465 + // raw images can be loaded straight into a Mat
  466 + else {
  467 + char *imgBuf = new char[imgSizeBytes];
  468 + seqFile.read(imgBuf, imgSizeBytes);
  469 + int type = (numChan == 1 ? CV_8UC1 : CV_8UC3);
  470 + temp = Mat(height, width, type, imgBuf);
  471 + }
  472 +
  473 + output.file = basis.file;
  474 + output.m() = temp;
  475 + return true;
  476 + }
  477 +private:
  478 + int readInt()
  479 + {
  480 + int num;
  481 + seqFile.read((char*)&num, 4);
  482 + return num;
  483 + }
  484 +
  485 + // apparently the text in seq files is 16 bit characters (UTF-16?)
  486 + // since we don't really need the last byte, snad since it gets interpreted as
  487 + // a terminating char, let's just grab the first byte for storage
  488 + char* readText(int bytes)
  489 + {
  490 + char *text = new char[bytes], *ret = new char[bytes/2];
  491 + seqFile.read(text, bytes);
  492 + for (int i=0; i<bytes; i+=2) {
  493 + ret[i/2] = text[i];
  494 + }
  495 + return ret;
  496 + }
  497 +
  498 +protected:
  499 + ifstream seqFile;
  500 + QQueue<int> seekPos;
  501 + int width, height, numChan, imgSizeBytes, trueImgSizeBytes, numFrames;
  502 + QString imgFormat;
  503 +};
322 504
323 // Interface for sequentially getting data from some data source. 505 // Interface for sequentially getting data from some data source.
324 // Given a TemplateList, return single template frames sequentially by applying a TemplateProcessor 506 // Given a TemplateList, return single template frames sequentially by applying a TemplateProcessor
@@ -514,11 +696,16 @@ protected: @@ -514,11 +696,16 @@ protected:
514 if (frameSource) 696 if (frameSource)
515 frameSource->close(); 697 frameSource->close();
516 698
  699 + Template curr = this->templates[current_template_idx];
517 if (mode == br::Idiocy::Auto) 700 if (mode == br::Idiocy::Auto)
518 { 701 {
519 delete frameSource; 702 delete frameSource;
520 - if (this->templates[this->current_template_idx].empty())  
521 - frameSource = new VideoReader(); 703 + if (curr.empty()) {
  704 + if (curr.file.name.right(3) == "seq")
  705 + frameSource = new SeqReader();
  706 + else
  707 + frameSource = new VideoReader();
  708 + }
522 else 709 else
523 frameSource = new DirectReturn(); 710 frameSource = new DirectReturn();
524 } 711 }
@@ -529,11 +716,15 @@ protected: @@ -529,11 +716,15 @@ protected:
529 } 716 }
530 else if (mode == br::Idiocy::StreamVideo) 717 else if (mode == br::Idiocy::StreamVideo)
531 { 718 {
532 - if (!frameSource)  
533 - frameSource = new VideoReader(); 719 + if (!frameSource) {
  720 + if (curr.file.name.right(3) == "seq")
  721 + frameSource = new SeqReader();
  722 + else
  723 + frameSource = new VideoReader();
  724 + }
534 } 725 }
535 726
536 - open_res = frameSource->open(this->templates[current_template_idx]); 727 + open_res = frameSource->open(curr);
537 if (!open_res) 728 if (!open_res)
538 { 729 {
539 current_template_idx++; 730 current_template_idx++;