Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

ImageStreamDriver.cc

Go to the documentation of this file.
00001 #include "ImageStreamDriver.h"
00002 #include "Shared/get_time.h"
00003 #include "Shared/MarkScope.h"
00004 #include "Shared/RobotInfo.h"
00005 #include "Shared/Config.h"
00006 #include "Shared/ImageUtil.h"
00007 #include "Shared/debuget.h"
00008 #include "Shared/InverseMarkScope.h"
00009 
00010 #include <png.h>
00011 
00012 using namespace std; 
00013 
00014 const char * ImageStreamDriver::formatNames[NUM_FORMATS+1] = { "yuv", "png", "jpeg", "tekkotsu", NULL };
00015 //declare explicit instances of the NamedEnumerations we're using
00016 //(cuts down on some pretty significant binary size / debugging symbol bloat)
00017 INSTANTIATE_NAMEDENUMERATION_STATICS(ImageStreamDriver::format_t);
00018 
00019 const std::string ImageStreamDriver::autoRegisterDriver = DeviceDriver::getRegistry().registerType<ImageStreamDriver>("ImageStream");
00020 
00021 void ImageStreamDriver::plistValueChanged(const plist::PrimitiveBase& pl) {
00022   if(&pl==&format) {
00023     CommPort * comm = getComm(commName);
00024     if(comm!=NULL) {
00025       disconnect(comm);
00026       connect(comm);
00027     }
00028   } else {
00029     DataStreamDriver::plistValueChanged(pl);
00030   }
00031 }
00032 
00033 bool ImageStreamDriver::readData(std::istream& is) {
00034   RCRegion * region=readImage(is);
00035   if(region==NULL)
00036     return false;
00037   if(!paceInput) try {
00038     // loop to run through any buffer backlog, get most recent image
00039     region->AddReference(); // to mark this region 'in use' so readImage doesn't use it for the next image
00040     char firstByte;
00041     while(is.readsome(&firstByte,1)>0 && is) {
00042       is.putback(firstByte); // had data, put it back in the buffer so we can process it
00043       RCRegion * newRegion = readImage(is);
00044       if(newRegion!=NULL) {
00045         ASSERT(region!=newRegion,"ImageStreamDriver handed out same region twice");
00046         // we're using the new region, put the old one back in the queue for recycling
00047         region->RemoveReference();
00048         region=newRegion;
00049         region->AddReference();
00050       }
00051       testCancel();
00052     } 
00053     region->RemoveReference();
00054   } catch(...) {
00055     region->RemoveReference();
00056     throw;
00057   }
00058 
00059   setImage(region);
00060   return true;
00061 }
00062 
00063 RCRegion * ImageStreamDriver::readImage(std::istream& is) {
00064   format_t expectedFormat = format; // cache format in case a new one is requested, next image will still be old one
00065   
00066   // block until we have data... might cancel out while we're waiting for an image to come
00067   char firstByte;
00068   is.get(firstByte);
00069   if(!is)
00070     return NULL;
00071   is.unget();
00072   testCancel();
00073   timestamp = get_time();
00074   
00075   const size_t HEADER_SIZE = sizeof(ImageHeader);
00076   
00077   RCRegion* region = (payloadSize==0) ? NULL : getUnusedRegion(payloadSize, 0);
00078   // a reference to region is kept in the regions list, so it's OK if we return/throw/cancel, doesn't leak
00079   unsigned int layer=0;
00080   
00081   switch(expectedFormat) {
00082     case FORMAT_YUV: {
00083       size_t imgSize = CameraResolutionX*CameraResolutionY*3;
00084       payloadSize = HEADER_SIZE + imgSize;
00085       if(region==NULL) {
00086         region = getUnusedRegion(payloadSize, 0);
00087       } else if(region->Size()<payloadSize) {
00088         // not a leak - old region is still in regions list, we'll dereference it on a future getUnusedRegion call
00089         region = getUnusedRegion(payloadSize, 0);
00090       }
00091       char * payload = region->Base();
00092       is.read(payload+HEADER_SIZE, imgSize);
00093       testCancel();
00094       if(!is)
00095         return NULL;
00096       new (payload) ImageHeader(sid, layer, CameraResolutionX, CameraResolutionY, 3, ++frameNumber, timestamp, nextName());
00097     } break;
00098       
00099     case FORMAT_PNG: {
00100       if (sid == ProjectInterface::visRawDepthSID) {
00101         size_t w=0,h=0,c=0;
00102         if(region==NULL) {
00103           // first frame, get meta-information to find buffer size
00104           if(!image_util::decodePNG(is,w,h,c)) {
00105             cerr << "ImageStreamDriver JPEG header decompression failed" << endl;
00106             return NULL;
00107           }
00108           payloadSize = HEADER_SIZE + w*h*2;
00109           region = getUnusedRegion(payloadSize, 0);
00110         }
00111         char * img=region->Base() + HEADER_SIZE;
00112         size_t imgSize=region->Size()-HEADER_SIZE;
00113         // usually size will be the same, assume we can reuse previous buffer
00114         if(!image_util::decodePNGToDepth(is,w,h,c,img,imgSize)) {
00115           // maybe image grew...
00116           if(w*h*c<=imgSize) { // nope should've worked, must be corrupted
00117             cerr << "ImageStreamDriver PNG decompression failed" << endl;
00118             return NULL;
00119           }
00120           // resize buffer and try again
00121           payloadSize = HEADER_SIZE + w*h*2;
00122           // not a leak - old region is still in regions list, we'll dereference it on a future getUnusedRegion call
00123           region = getUnusedRegion(payloadSize, 0);
00124           img=region->Base() + HEADER_SIZE;
00125           imgSize=region->Size()-HEADER_SIZE;
00126           if(!image_util::decodePNGToDepth(is,w,h,c,img,imgSize)) { // ok, must be corrupted as well
00127             cerr << "ImageStreamDriver PNG decompression failed after resize" << endl;
00128             return NULL;
00129           }
00130         }
00131         testCancel();
00132 
00133         payloadSize = HEADER_SIZE + w*h*2;
00134         new (region->Base()) ImageHeader(sid, layer, w, h, 2, ++frameNumber, timestamp, nextName());
00135       }
00136       // Regular images
00137       else {
00138         size_t w=0,h=0,c=0;
00139         if(region==NULL) {
00140           // first frame, get meta-information to find buffer size
00141           if(!image_util::decodePNG(is,w,h,c)) {
00142             cerr << "ImageStreamDriver JPEG header decompression failed" << endl;
00143             return NULL;
00144           }
00145           payloadSize = HEADER_SIZE + w*h*c;
00146           region = getUnusedRegion(payloadSize, 0);
00147         }
00148         char * img=region->Base() + HEADER_SIZE;
00149         size_t imgSize=region->Size()-HEADER_SIZE;
00150         // usually size will be the same, assume we can reuse previous buffer
00151         if(!image_util::decodePNG(is,w,h,c,img,imgSize)) {
00152           // maybe image grew...
00153           if(w*h*c<=imgSize) { // nope should've worked, must be corrupted
00154             cerr << "ImageStreamDriver PNG decompression failed" << endl;
00155             return NULL;
00156           }
00157           // resize buffer and try again
00158           payloadSize = HEADER_SIZE + w*h*c;
00159           // not a leak - old region is still in regions list, we'll dereference it on a future getUnusedRegion call
00160           region = getUnusedRegion(payloadSize, 0);
00161           img=region->Base() + HEADER_SIZE;
00162           imgSize=region->Size()-HEADER_SIZE;
00163           if(!image_util::decodePNG(is,w,h,c,img,imgSize)) { // ok, must be corrupted as well
00164             cerr << "ImageStreamDriver PNG decompression failed after resize" << endl;
00165             return NULL;
00166           }
00167         }
00168         testCancel();
00169 
00170         payloadSize = HEADER_SIZE + w*h*c;
00171         new (region->Base()) ImageHeader(sid, layer, w, h, c, ++frameNumber, timestamp, nextName());
00172       }
00173     } break;
00174       
00175     case FORMAT_JPEG: {
00176       size_t w=0,h=0,c=0;
00177       if(region==NULL) {
00178         // first frame, get meta-information to find buffer size
00179         if(!image_util::decodeJPEG(is,w,h,c)) {
00180           cerr << "ImageStreamDriver JPEG header decompression failed" << endl;
00181           return NULL;
00182         }
00183         payloadSize = HEADER_SIZE + w*h*c;
00184         region = getUnusedRegion(payloadSize, 0);
00185       }
00186       char * img=region->Base() + HEADER_SIZE;
00187       size_t imgSize=region->Size()-HEADER_SIZE;
00188       // usually size will be the same, assume we can reuse previous buffer
00189       if(!image_util::decodeJPEG(is,w,h,c,img,imgSize)) {
00190         // maybe image grew...
00191         if(w*h*c<=imgSize) { // nope should've worked, must be corrupted
00192           cerr << "ImageStreamDriver JPEG decompression failed" << endl;
00193           return NULL;
00194         }
00195         // resize buffer and try again
00196         payloadSize = HEADER_SIZE + w*h*c;
00197         // not a leak - old region is still in regions list, we'll dereference it on a future getUnusedRegion call
00198         region = getUnusedRegion(payloadSize, 0);
00199         img=region->Base() + HEADER_SIZE;
00200         imgSize=region->Size()-HEADER_SIZE;
00201         if(!image_util::decodeJPEG(is,w,h,c,img,imgSize)) { // ok, must be corrupted as well
00202           cerr << "ImageStreamDriver JPEG decompression failed after resize" << endl;
00203           return NULL;
00204         }
00205       }
00206       testCancel();
00207 
00208       payloadSize = HEADER_SIZE + w*h*c;
00209       new (region->Base()) ImageHeader(sid, layer, w, h, c, ++frameNumber, timestamp, nextName());
00210     } break;
00211       
00212     case FORMAT_TEKKOTSU: {
00213       // ugh, LoadSave handles FILE* and char*, but not iostreams...
00214       // gonna be a little ugly :(
00215       
00216       unsigned int width=CameraResolutionX, height=CameraResolutionY, components=3;
00217       
00218       // RAW CAM BEHAVIOR HEADER
00219       char tmp[256];
00220       unsigned int len;
00221       string fmt;
00222       is.read(tmp,LoadSave::getSerializedSize(len));
00223       LoadSave::decode(len,tmp,LoadSave::getSerializedSize(len));
00224       if(len!=13) {
00225         cerr << "Expecting Tekkotsu image format, but image header doesn't match! (len==" << len << ")" << endl;
00226         return NULL;
00227       }
00228       is.read(tmp,len+1);
00229       if(strncmp(tmp,"TekkotsuImage",len+1)!=0) {
00230         tmp[len+2]='\0';
00231         cerr << "Expecting Tekkotsu image format, but image header doesn't match! (" << tmp << ")" << endl;
00232         return NULL;
00233       }
00234       
00235       int encoding; //Config::vision_config::RawCamConfig::encoding_t
00236       is.read(tmp,LoadSave::getSerializedSize(encoding));
00237       LoadSave::decode(encoding,tmp,LoadSave::getSerializedSize(encoding));
00238       if(encoding==Config::vision_config::ENCODE_SINGLE_CHANNEL)
00239         components=1;
00240       int compression; //Config::vision_config::RawCamConfig::compression_t
00241       is.read(tmp,LoadSave::getSerializedSize(compression));
00242       LoadSave::decode(compression,tmp,LoadSave::getSerializedSize(compression));
00243       is.read(tmp,LoadSave::getSerializedSize(width));
00244       LoadSave::decode(width,tmp,LoadSave::getSerializedSize(width));
00245       is.read(tmp,LoadSave::getSerializedSize(height));
00246       LoadSave::decode(height,tmp,LoadSave::getSerializedSize(height));
00247       unsigned int remote_timestamp;
00248       is.read(tmp,LoadSave::getSerializedSize(remote_timestamp));
00249       LoadSave::decode(remote_timestamp,tmp,LoadSave::getSerializedSize(remote_timestamp));
00250       unsigned int fnum;
00251       is.read(tmp,LoadSave::getSerializedSize(fnum));
00252       LoadSave::decode(fnum,tmp,LoadSave::getSerializedSize(fnum));
00253       
00254       // have everything we need to set up output header...
00255       payloadSize = HEADER_SIZE + width*height*components;
00256       if(region==NULL) {
00257         region = getUnusedRegion(payloadSize, 0);
00258       } else if(region->Size() < payloadSize) {
00259         // not a leak - old region is still in regions list, we'll dereference it on a future getUnusedRegion call
00260         region = getUnusedRegion(payloadSize, 0);
00261       }
00262       
00263       unsigned int remain = width*height*components;
00264       char * b=region->Base()+HEADER_SIZE;
00265       
00266       for(unsigned int i=0; i<components; ++i) {
00267         // FILTER BANK GENERATOR HEADER
00268         is.read(tmp,LoadSave::getSerializedSize(len));
00269         LoadSave::decode(len,tmp,LoadSave::getSerializedSize(len));
00270         if(len!=8) {
00271           cerr << "Expecting FbkImage image format, but header doesn't match! (len==" << len << ")" << endl;
00272           return NULL;
00273         }
00274         is.read(tmp,len+1);
00275         if(strncmp(tmp,"FbkImage",len+1)!=0) {
00276           tmp[len+2]='\0';
00277           cerr << "Expecting FbkImage image format, but image header doesn't match! (" << tmp << ")" << endl;
00278           return NULL;
00279         }
00280         unsigned int lwidth;
00281         is.read(tmp,LoadSave::getSerializedSize(lwidth));
00282         LoadSave::decode(lwidth,tmp,LoadSave::getSerializedSize(lwidth));
00283         unsigned int lheight;
00284         is.read(tmp,LoadSave::getSerializedSize(lheight));
00285         LoadSave::decode(lheight,tmp,LoadSave::getSerializedSize(lheight));
00286         unsigned int lyr;
00287         is.read(tmp,LoadSave::getSerializedSize(lyr));
00288         LoadSave::decode(lyr,tmp,LoadSave::getSerializedSize(lyr));
00289         unsigned int chan_id;
00290         is.read(tmp,LoadSave::getSerializedSize(chan_id));
00291         LoadSave::decode(chan_id,tmp,LoadSave::getSerializedSize(chan_id));
00292         
00293         // GENERATOR SUBCLASS HEADER
00294         is.read(tmp,LoadSave::getSerializedSize(len));
00295         LoadSave::decode(len,tmp,LoadSave::getSerializedSize(len));
00296         is.read(tmp,len+1);
00297         if(strcmp(tmp,"blank")==0) {
00298           int useChan=(components==1)?i:chan_id;
00299           int off=useChan;
00300           for(unsigned int y=0; y<height; y++) {
00301             for(unsigned int x=0; x<width; x++) {
00302               b[off]=128;
00303               off+=components;
00304             }
00305           }
00306         } else if(strcmp(tmp,"RawImage")==0) {
00307           int useChan=(components==1)?i:chan_id;
00308           vector<char> chan(lwidth*lheight);
00309           is.read(&chan.front(),chan.size());
00310           copyImage(b,width,height,components,&chan.front(),lwidth,lheight,useChan);
00311           
00312         } else if(strcmp(tmp,"JPEGGrayscale")==0) {
00313           int useChan=(components==1)?i:chan_id;
00314           is.read(tmp,LoadSave::getSerializedSize(len));
00315           LoadSave::decode(len,tmp,LoadSave::getSerializedSize(len));
00316           vector<char> jpeg(len);
00317           is.read(&jpeg.front(),jpeg.size());
00318           size_t jwidth,jheight,jcomp;
00319           image_util::decodeJPEG(&jpeg.front(), jpeg.size(), jwidth, jheight, jcomp);
00320           if(jcomp!=1) {
00321             cerr << "Got color image where grayscale was expected" << endl;
00322             return NULL;
00323           }
00324           vector<char> chan(jwidth*jheight);
00325           size_t tsize=chan.size();
00326           char* tchan=&chan.front();
00327           if(!image_util::decodeJPEG(&jpeg.front(), jpeg.size(), jwidth, jheight, jcomp, tchan, tsize)) {
00328             cerr << "JPEG decompression failed" << endl;
00329             return NULL;
00330           }
00331           copyImage(b,width,height,components,&chan.front(),lwidth,lheight,useChan);
00332           
00333         } else if(strcmp(tmp,"JPEGColor")==0) {
00334           is.read(tmp,LoadSave::getSerializedSize(len));
00335           LoadSave::decode(len,tmp,LoadSave::getSerializedSize(len));
00336           vector<char> jpeg(len);
00337           is.read(&jpeg.front(),jpeg.size());
00338           size_t jwidth,jheight,jcomp;
00339           size_t tsize=remain;
00340           if(!image_util::decodeJPEG(&jpeg.front(), jpeg.size(), jwidth, jheight, jcomp, b, tsize)) {
00341             cerr << "JPEG decompression failed" << endl;
00342             return NULL;
00343           }
00344           i=components;
00345           
00346         } else {
00347           cerr << "Unknown image generator " << tmp << endl;
00348           return NULL;
00349         }
00350       }
00351       
00352       testCancel();
00353       if(!is)
00354         return NULL;
00355       new (region->Base()) ImageHeader(sid, layer, width, height, components, ++frameNumber, timestamp, nextName());
00356     } break;
00357   }
00358   ASSERTRETVAL(region!=NULL,"image memory region is still NULL after reading, unknown format?",NULL);
00359   return region;
00360 }
00361 
00362 void ImageStreamDriver::copyImage(char * buf, unsigned int width, unsigned int height, unsigned int channels, const char * chan, unsigned int lwidth, unsigned int lheight, unsigned int c) {
00363   if(lwidth==width && lheight==height) {
00364     // same size, straight copy
00365     for(unsigned int y=0;y<height;y++) {
00366       unsigned int datarowstart=y*width*channels+c;
00367       unsigned int tmprowstart=y*lwidth;
00368       for(unsigned int x=0;x<width;x++)
00369         buf[datarowstart+x*channels]=chan[tmprowstart+x];
00370     }
00371   } else {
00372     // upsample image
00373     //we'll linearly interpolate between pixels
00374 
00375     //hold edges, interpolate through middle:
00376     //  if we have 2 samples, scaling up to 4
00377     //   index: 0   1    2   3
00378     // maps to: 0  1/3  2/3  1
00379     float xsc=(lwidth-1)/(float)(width-1);
00380     float ysc=(lheight-1)/(float)(height-1);
00381     for(unsigned int y=0;y<height-1;y++) {
00382       unsigned int datarowstart=y*width*channels+c;
00383       float ty=y*ysc;
00384       unsigned int ly=(int)ty; //lower pixel index
00385       float fy=ty-ly; //upper pixel weight
00386       unsigned int tmprowstart=ly*lwidth;
00387       for(unsigned int x=0;x<width-1;x++) {
00388         float tx=x*xsc;
00389         unsigned int lx=(int)tx; //lower pixel index
00390         float fx=tx-lx; //upper pixel weight
00391         
00392         float lv=((int)chan[tmprowstart+lx]&0xFF)*(1-fx)+((int)chan[tmprowstart+lx+1]&0xFF)*fx;
00393         float uv=((int)chan[tmprowstart+lx+lwidth]&0xFF)*(1-fx)+((int)chan[tmprowstart+lx+1+lwidth]&0xFF)*fx;
00394         buf[datarowstart+x*channels]=(char)(lv*(1-fy)+uv*fy);
00395       }
00396       buf[datarowstart+(width-1)*channels]=chan[tmprowstart+lwidth-1];
00397     }
00398     unsigned int datarowstart=width*(height-1)*channels+c;
00399     unsigned int tmprowstart=lwidth*(lheight-1);
00400     for(unsigned int x=0;x<width-1;x++) {
00401       float tx=x*xsc;
00402       unsigned int lx=(int)tx; //lower pixel index
00403       float fx=tx-lx; //upper pixel weight
00404       buf[datarowstart+x*channels]=(char)(((int)chan[tmprowstart+lx]&0xFF)*(1-fx)+((int)chan[tmprowstart+lx+1]&0xFF)*fx);
00405     }
00406     buf[datarowstart+(width-1)*channels]=chan[tmprowstart+lwidth-1];
00407   }
00408 }
00409 
00410 void ImageStreamDriver::connect(CommPort* comm) {
00411   DataStreamDriver::connect(comm);
00412   format.addPrimitiveListener(this);
00413 }
00414 
00415 void ImageStreamDriver::disconnect(CommPort* comm) {
00416   format.removePrimitiveListener(this);
00417   DataStreamDriver::disconnect(comm);
00418 }
00419 
00420 /*! @file
00421  * @brief 
00422  * @author Ethan Tira-Thompson (ejt) (Creator)
00423  */

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Mon May 9 05:01:38 2016 by Doxygen 1.6.3