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

Tekkotsu Hardware Abstraction Layer 5.1CVS
Generated Fri Mar 16 05:30:34 2012 by Doxygen 1.6.3