#include #ifndef WIN32 #include #endif #include #include #include #include #include #include #include /* FileReader.cpp Written by: Michael Götting, Robert Haschke (2006) University of Bielefeld AG Neuroinformatik mgoettin@techfak.uni-bielefeld.de */ using namespace std; namespace icl { inline void replace_newline (string::value_type& c) { if (c == '\n') c = ' '; } //-------------------------------------------------------------------------- string FileReader::hashPattern(const std::string& sFileName) { std::string::size_type iSuffixPos=string::npos; unsigned int nHashes=0; // count number of hashes directly before file suffix analyseHashes (sFileName, nHashes, iSuffixPos); if (nHashes) { // and replace them by [0-9] regular expressions ostringstream oss; for (unsigned int i=1; i <= nHashes; ++i) { oss << sFileName.substr(0, iSuffixPos-nHashes); for (unsigned int j=1; j <= i; ++j) oss << "[0-9]"; oss << sFileName.substr(iSuffixPos) << " "; } return oss.str(); } return sFileName; } //-------------------------------------------------------------------------- FileReader::FileReader(string sPattern) // {{{ open { FUNCTION_LOG(""); #ifndef WIN32 wordexp_t match; char **ppcFiles; // remove newlines from sPattern std::for_each (sPattern.begin(), sPattern.end(), replace_newline); // search for file matching the pattern(s) switch (wordexp (sPattern.c_str(), &match, WRDE_UNDEF)) { case 0: break; case WRDE_BADCHAR: throw ICLException ("illegal chars in pattern (|, &, ;, <, >, (, ), {, }"); break; case WRDE_BADVAL: throw ICLException ("encountered undefined shell variable"); break; case WRDE_NOSPACE: throw ICLException ("out of memory"); break; case WRDE_SYNTAX: throw ICLException ("syntax error, e.g. unbalanced parentheses or quotes"); break; } ppcFiles = match.we_wordv; for (unsigned int i=0; i < match.we_wordc; ++i) { bool bGzipped; switch (getFileType (ppcFiles[i], bGzipped)) { case ioFormatSEQ: readSequenceFile (ppcFiles[i]); break; case ioFormatUnknown: break; // skip unknown file types default: m_vecFileName.push_back(ppcFiles[i]); break; } } wordfree(&match); #else bool bGzipped; switch (getFileType (sPattern, bGzipped)) { case ioFormatSEQ: readSequenceFile (sPattern); break; case ioFormatUnknown: break; // skip unknown file types default: m_vecFileName.push_back(sPattern); break; } #endif this->init (); } // }}} //-------------------------------------------------------------------------- FileReader::FileReader(const string& sPrefix, const string& sType, int iObjStart, int iObjEnd, int iImageStart, int iImageEnd) // {{{ open { FUNCTION_LOG(""); ostringstream ossFileInit; ossFileInit << "." << sType; bool bDummy; ioformat eFormat = getFileType (ossFileInit.str(), bDummy); if (eFormat == ioFormatSEQ || eFormat == ioFormatUnknown) throw ICLException (string("not supported file type ") + sType); //---- Build filename ---- for (int i=iObjStart;i<=iObjEnd;i++) { for (int j=iImageStart;j<=iImageEnd;j++) { ostringstream ossObjectCnt, ossFile; ossFile << sPrefix << i << "__" << j << "." << sType; ossObjectCnt << i << "__" << j; m_vecFileName.push_back(ossFile.str()); m_vecObjectCnt.push_back(ossObjectCnt.str()); } } this->init (); } // }}} //-------------------------------------------------------------------------- FileReader::FileReader(const FileReader& other) : m_poCurImg(0) { FUNCTION_LOG(""); *this = other; } //-------------------------------------------------------------------------- FileReader& FileReader::operator=(const FileReader& other) { // {{{ open FUNCTION_LOG(""); if (!this->m_bBuffered) delete this->m_poCurImg; this->m_vecFileName = other.m_vecFileName; this->m_vecObjectCnt = other.m_vecObjectCnt; this->m_vecImgBuffer = other.m_vecImgBuffer; this->m_bBuffered = other.m_bBuffered; this->m_iCurImg = other.m_iCurImg; this->m_poCurImg = 0; // setup the jpeg error routine once jpgCinfo.err = jpeg_std_error(&jpgErr); jpgErr.error_exit = icl_jpeg_error_exit; return *this; } // }}} //-------------------------------------------------------------------------- FileReader::~FileReader () // {{{ open { FUNCTION_LOG(""); for (ImageBuffer::iterator it=m_vecImgBuffer.begin(), end=m_vecImgBuffer.end(); it != end; ++it) delete *it; if (!m_bBuffered) delete m_poCurImg; } // }}} //-------------------------------------------------------------------------- void FileReader::init() // {{{ open { FUNCTION_LOG(""); m_bIgnoreDesired = true; m_bBuffered = false; m_iCurImg = 0; m_poCurImg = 0; if (m_vecFileName.size () == 0) throw ICLException ("empty file list"); // setup the jpeg error routine once jpgCinfo.err = jpeg_std_error(&jpgErr); jpgErr.error_exit = icl_jpeg_error_exit; } // }}} //-------------------------------------------------------------------------- const ImgBase* FileReader::grab(ImgBase **ppoDst) { // {{{ open if (m_bBuffered) { m_poCurImg = m_vecImgBuffer[m_iCurImg]; } else { readImage (m_vecFileName[m_iCurImg], &m_poCurImg); } // forward to next image next (); if (m_bIgnoreDesired) { m_eDesiredDepth = m_poCurImg->getDepth(); m_oDesiredParams = m_poCurImg->getParams(); } // if we can return the internal image directly (without conversion), do it: if (ppoDst == 0 && (m_bIgnoreDesired || (m_eDesiredDepth == m_poCurImg->getDepth() && m_oDesiredParams == m_poCurImg->getParams()))) return m_poCurImg; // otherwise get pointer to output image instance: ImgBase *poDst = prepareOutput (ppoDst); // and convert the image m_oConverter.apply (m_poCurImg, poDst); return poDst; } // }}} //-------------------------------------------------------------------------- void FileReader::readImage(const string& sFileName, ImgBase** ppoDst) { // {{{ open FUNCTION_LOG(""); //---- Variable definition ---- FileInfo oInfo (sFileName); // set some defaults oInfo.oROI = Rect(); // full ROI try { switch (oInfo.eFileFormat) { case ioFormatICL: case ioFormatPNM: readHeaderPNMICL (oInfo); ensureCompatible (ppoDst, oInfo.eDepth, oInfo.oImgSize, oInfo.iNumChannels, oInfo.eFormat, oInfo.oROI); (*ppoDst)->setTime(oInfo.timeStamp); readDataPNMICL (*ppoDst, oInfo); break; case ioFormatJPG: readHeaderJPG (oInfo); ensureCompatible (ppoDst, oInfo.eDepth, oInfo.oImgSize, oInfo.iNumChannels, oInfo.eFormat, oInfo.oROI); (*ppoDst)->setTime(oInfo.timeStamp); readDataJPG ((*ppoDst)->asImg(), oInfo); break; case ioFormatCSV: readHeaderCSV (oInfo); ensureCompatible (ppoDst, oInfo.eDepth, oInfo.oImgSize, oInfo.iNumChannels, oInfo.eFormat, oInfo.oROI); (*ppoDst)->setTime(oInfo.timeStamp); readDataCSV (*ppoDst, oInfo); break; default: throw ICLException (string("not supported file type")); } } catch (ICLException &e) { if (oInfo.fp) closeFile (oInfo); throw; } closeFile (oInfo); } // }}} //-------------------------------------------------------------------------- FileReader::FileList FileReader::bufferImages (bool bStopOnError) // {{{ open { FUNCTION_LOG(""); FileList vecErrorList; if (m_bBuffered) return vecErrorList; // do not buffer twice // if we enter buffering mode and already read images by calling grab // we must free the internally allocated image first if (m_poCurImg && !m_bBuffered) { delete m_poCurImg; m_poCurImg = 0; } // clear buffer from previous trial for (ImageBuffer::iterator it=m_vecImgBuffer.begin(), end=m_vecImgBuffer.end(); it != end; ++it) delete *it; m_vecImgBuffer.clear(); for (FileList::iterator it=m_vecFileName.begin(), end=m_vecFileName.end(); it != end; ++it) { m_poCurImg = 0; // force reallocation of new image pointer try { readImage (*it, &m_poCurImg); } catch (ICLException &e) { // in any case, report the error vecErrorList.push_back (*it); if (bStopOnError) return vecErrorList; // create some dummy image to insert to buffer vector if (!m_poCurImg) { m_poCurImg = imgNew (depth8u, Size(1,1), formatGray); } } m_vecImgBuffer.push_back (m_poCurImg); } // only on successful reading of all images m_bBuffered = true; return vecErrorList; } // }}} //-------------------------------------------------------------------------- void FileReader::readSequenceFile (const std::string& sFileName) // {{{ open { FUNCTION_LOG(""); string sFile; ifstream streamSeq (sFileName.c_str(),ios::in); if(!streamSeq) { ERROR_LOG("Can't open sequence file: " << sFileName); } while(streamSeq) { getline (streamSeq, sFile); if (!sFile.empty()) { // ignore empty lines m_vecFileName.push_back(sFile); } } streamSeq.close(); } // }}} //-------------------------------------------------------------------------- void FileReader::setCSVHeader (depth d, const ImgParams& p) { // {{{ open m_CSVDepth = d; m_CSVParams = p; } // }}} void FileReader::readHeaderCSV (FileInfo &oInfo) { // {{{ open // use m_CSV parameters as default settings oInfo.eDepth = m_CSVDepth; oInfo.oImgSize = m_CSVParams.getSize(); oInfo.oROI=m_CSVParams.getROI(); oInfo.iNumChannels = m_CSVParams.getChannels(); oInfo.eFormat = m_CSVParams.getFormat(); //oInfo.timeStamp = 0; // overwrite these settings by file name contents int pos=oInfo.sFileName.find("-ICL:",0)+5; StrTok tok(oInfo.sFileName.substr(pos),":"); if(tok.hasMoreTokens()){ StrTok tok2(tok.nextToken(),"x");// WxHxC if(tok2.hasMoreTokens()){ oInfo.oImgSize.width=atoi((tok2.nextToken()).c_str()); // W } if(tok2.hasMoreTokens()){ oInfo.oImgSize.height=atoi((tok2.nextToken()).c_str()); // H } if(tok2.hasMoreTokens()){ oInfo.iNumChannels=atoi((tok2.nextToken()).c_str()); //C } } if(tok.hasMoreTokens()){ oInfo.eDepth=translateDepth(tok.nextToken()); //D } if(tok.hasMoreTokens()){ StrTok tok2(tok.nextToken(),"."); if(tok2.hasMoreTokens()){ oInfo.eFormat=translateFormat(tok2.nextToken()); //F } } } // }}} template void FileReader::readCSVTmpl(Img* poImg, FileInfo &oInfo) { // {{{ open FUNCTION_LOG(""); //Img16s *poImg16s = poImg->asImg(); T *pc2Buf[3]; pc2Buf[0] = poImg->getData (0); pc2Buf[1] = poImg->getData (1); pc2Buf[2] = poImg->getData (2); openFile (oInfo, "rb"); // open file for reading char *pcBuf=0; int fsize=20*oInfo.oImgSize.width; //20 = max char length of double (should be 14) +1 (komma) + 5 bonus pcBuf=(char*)malloc(fsize*sizeof(char)); for (int c=0;cgetDepth()) { case depth8u: readCSVTmpl(poImg->asImg(),oInfo); break; case depth16s: readCSVTmpl(poImg->asImg(),oInfo); break; case depth32s: readCSVTmpl(poImg->asImg(),oInfo); break; case depth32f: readCSVTmpl(poImg->asImg(),oInfo); break; case depth64f: readCSVTmpl(poImg->asImg(),oInfo); break; default: ICL_INVALID_DEPTH; break; } } // }}} //-------------------------------------------------------------------------- void FileReader::readHeaderPNMICL (FileInfo &oInfo) { // {{{ open FUNCTION_LOG(""); openFile (oInfo, "rb"); // open file for reading char acBuf[1024], *pcBuf; istringstream iss; if (oInfo.eFileFormat != ioFormatICL) { //---- Read the magic number (for non-ICL format only) ---- if (!gzgets (oInfo.fp, acBuf, 1024) || acBuf[0] != 'P') throw InvalidFileFormatException(); switch (acBuf[1]) { case '6': oInfo.eFormat = formatRGB; break; case '5': oInfo.eFormat = formatGray; break; default: throw InvalidFileFormatException(); } } //---- Set default values ---- oInfo.iNumChannels = getChannelsOfFormat(oInfo.eFormat); oInfo.iNumImages = 1; oInfo.eDepth = depth8u; // {{{ Read special header info do { if (!gzgets (oInfo.fp, acBuf, 1024)) throw InvalidFileFormatException(); // skip withe space in beginning of line pcBuf = acBuf; while (*pcBuf && isspace(*pcBuf)) ++pcBuf; if (*pcBuf && *pcBuf != '#') break; // no more comments: break from loop // process comment iss.str (pcBuf+1); string sKey, sValue; iss >> sKey; if (sKey == "NumFeatures" || sKey == "NumImages") { iss >> oInfo.iNumImages; oInfo.iNumChannels *= oInfo.iNumImages; } else if (sKey == "ROI") { iss >> oInfo.oROI.x; iss >> oInfo.oROI.y; iss >> oInfo.oROI.width; iss >> oInfo.oROI.height; continue; } else if (sKey == "ImageDepth") { // ignore image depth for all formats but ICL if (oInfo.eFileFormat != ioFormatICL) continue; iss >> sValue; // might throw an InvalidDepthException oInfo.eDepth = translateDepth (sValue); continue; } else if (sKey == "Format") { iss >> sValue; // might throw an InvalidFormatException oInfo.eFormat = translateFormat(sValue.c_str()); } else if (sKey == "TimeStamp") { Time::value_type t; iss >> t; oInfo.timeStamp = Time::microSeconds(t); continue; } //---- Is num channels in depence to the format ---- if (getChannelsOfFormat(oInfo.eFormat) != oInfo.iNumChannels) oInfo.eFormat = formatMatrix; } while (true); // }}} // read image size iss.str (pcBuf); iss >> oInfo.oImgSize.width; iss >> oInfo.oImgSize.height; oInfo.oImgSize.height = oInfo.oImgSize.height / oInfo.iNumImages; // skip line with maximal pixel value if (!gzgets (oInfo.fp, acBuf, 1024)) throw InvalidFileFormatException(); } // }}} void FileReader::readDataPNMICL(ImgBase* poImg, FileInfo &oInfo) { // {{{ open FUNCTION_LOG(""); if (oInfo.iNumImages == oInfo.iNumChannels || oInfo.eFileFormat == ioFormatICL) { // file format is non-interleaved, i.e. grayscale or icl proprietary int iDim = poImg->getDim () * getSizeOf (poImg->getDepth ()); for (int i=0;igetDataPtr(i), iDim) != iDim) throw InvalidFileFormatException (); } } else if (poImg->getDepth() == depth8u) { // file format is interleaved, i.e. RGB or something similar Img8u *poImg8u = poImg->asImg(); int iLines = oInfo.oImgSize.height; int iDim = 3 * oInfo.oImgSize.width; icl8u *pcBuf = new icl8u[iDim]; icl8u *pc; for (int i=0;igetData (i*3); icl8u *pcG = poImg8u->getData (i*3+1); icl8u *pcB = poImg8u->getData (i*3+2); for (int l=0; lnext) { if (pMarker->marker != JPEG_COM) continue; char acBuf[1025] = ""; memcpy (acBuf, pMarker->data, pMarker->data_length); acBuf[pMarker->data_length] = '\0'; // terminating null istringstream iss (acBuf); string sKey, sValue; iss >> sKey; if (sKey == "TimeStamp") { Time::value_type t; iss >> t; oInfo.timeStamp = Time::microSeconds(t); } else if (sKey == "ROI") { iss >> oInfo.oROI.x; iss >> oInfo.oROI.y; iss >> oInfo.oROI.width; iss >> oInfo.oROI.height; } } /* Step 4: set parameters for decompression */ /* Step 5: Start decompressor */ (void) jpeg_start_decompress(&jpgCinfo); /* After jpeg_start_decompress() we have the correct scaled * output image dimensions available, as well as the output colormap * if we asked for color quantization. */ oInfo.eDepth = depth8u; // can only read depth8u oInfo.oImgSize.width = jpgCinfo.output_width; oInfo.oImgSize.height = jpgCinfo.output_height; switch (jpgCinfo.out_color_space) { case JCS_GRAYSCALE: oInfo.eFormat = formatGray; break; case JCS_RGB: oInfo.eFormat = formatRGB; break; case JCS_YCbCr: oInfo.eFormat = formatYUV; break; default: throw ICLException("unknown color space"); } oInfo.iNumChannels = getChannelsOfFormat (oInfo.eFormat); } // }}} //-------------------------------------------------------------------------- void FileReader::readDataJPG(Img* poImg, FileInfo &oInfo) // {{{ open { FUNCTION_LOG(""); icl8u *pcBuf = 0; // update jump context to allow proper throw if (setjmp(jpgErr.setjmp_buffer)) { /* If we get here, the JPEG code has signaled an error. * We need to clean up the JPEG object and signal the error to the caller */ jpeg_destroy_decompress(&jpgCinfo); if (oInfo.iNumChannels == 3) delete[] pcBuf; throw InvalidFileFormatException(); } ICLASSERT (jpgCinfo.output_components == oInfo.iNumChannels); int iRowDim = jpgCinfo.output_width * jpgCinfo.output_components; icl8u *pcR=0, *pcG=0, *pcB=0; if (oInfo.iNumChannels == 1) pcBuf = poImg->getData (0); else if (oInfo.iNumChannels == 3) { pcBuf = new icl8u[iRowDim]; pcR = poImg->getData (0); pcG = poImg->getData (1); pcB = poImg->getData (2); } else {ERROR_LOG ("This should not happen."); return;} /* Step 6: while (scan lines remain to be read) */ /* jpeg_read_scanlines(...); */ while (jpgCinfo.output_scanline < jpgCinfo.output_height) { /* jpeg_read_scanlines expects an array of pointers to scanlines. * Here the array is only one element long, but you could ask for * more than one scanline at a time if that's more convenient. */ (void) jpeg_read_scanlines(&jpgCinfo, &pcBuf, 1); if (oInfo.iNumChannels == 1) pcBuf += iRowDim; else { // deinterleave three channel data icl8u *pc = pcBuf; for (int c=0; c