PARP Research Group University of Murcia, Spain


src/qvio/qvcameraworker.cpp

Go to the documentation of this file.
00001 /*
00002  *      Copyright (C) 2007, 2008, 2009. PARP Research Group.
00003  *      <http://perception.inf.um.es>
00004  *      University of Murcia, Spain.
00005  *
00006  *      This file is part of the QVision library.
00007  *
00008  *      QVision is free software: you can redistribute it and/or modify
00009  *      it under the terms of the GNU Lesser General Public License as
00010  *      published by the Free Software Foundation, version 3 of the License.
00011  *
00012  *      QVision is distributed in the hope that it will be useful,
00013  *      but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  *      GNU Lesser General Public License for more details.
00016  *
00017  *      You should have received a copy of the GNU Lesser General Public
00018  *      License along with QVision. If not, see <http://www.gnu.org/licenses/>.
00019  */
00020 
00024 
00025 #include <QTimer>
00026 
00027 #include <QVImage>
00028 #include <qvio/qvcameraworker.h>
00029 #include <qvip/qvipp/qvipp.h>
00030 
00031 #define DEFAULT_IMAGE_SIZE 128
00032 
00033 QVCameraWorker::QVCameraWorker(QString name): QVWorker(name)
00034         {
00035         // Input properties:
00036         addProperty<bool>("NoLoop", inputFlag, FALSE,"If the camera should be opened in no loop mode");
00037         addProperty<QString>("URL", inputFlag, QString(""),"URL of the video source to read");
00038         addProperty<int>("Cols", inputFlag, 0, "Suggested number of columns of the video");
00039         addProperty<int>("Rows", inputFlag, 0, "Suggested number of rows of the video");
00040         // RealTime property is also input (and as such can be initialized using the command line), but invisible to the GUI
00041         // (because once the camera has been linked, it cannot be relinked changing its synchronism):
00042         addProperty<bool>("RealTime", inputFlag|guiInvisible, FALSE, "If the camera should be opened in real time mode");
00043 
00044         // Output integer and boolean properties:
00045         addProperty<bool>("Opened", outputFlag, FALSE, "If the camera is correctly opened and working");
00046         addProperty<int>("FPS", outputFlag, 0, "FPS of the video");
00047         addProperty<int>("Frames", outputFlag, 0, "Number of read frames");
00048         addProperty<int>("ColsR", outputFlag, 0, "Actual number of columns of the video");
00049         addProperty<int>("RowsR", outputFlag, 0, "Actual number of rows of the video");
00050         addProperty<double>("Pos", outputFlag, 0.0, "Current time position of the video");
00051         addProperty<double>("Length", outputFlag, 0.0, "Length of the video (0.0 if not available)");
00052 
00053         // Output image properties:
00054         addProperty< QVImage<uChar,3> >("RGB image", outputFlag, QVImage<uChar,3>(), "Last grabbed RGB image");
00055         addProperty< QVImage<uChar,1> >("Y channel image",outputFlag, QVImage<uChar,1>(), "Last grabbed Y channel image");
00056         addProperty< QVImage<uChar,1> >("U channel image",outputFlag, QVImage<uChar,1>(), "Last grabbed U channel image");
00057         addProperty< QVImage<uChar,1> >("V channel image",outputFlag, QVImage<uChar,1>(), "Last grabbed V channel image");
00058 
00059         // Initial images (only useful if first open fails, for a default set of images to be available...)
00060         imageY = QVImage<uChar>(DEFAULT_IMAGE_SIZE,DEFAULT_IMAGE_SIZE,DEFAULT_IMAGE_SIZE);
00061         imageU = QVImage<uChar>(DEFAULT_IMAGE_SIZE/2,DEFAULT_IMAGE_SIZE/2,DEFAULT_IMAGE_SIZE/2);
00062         imageV = QVImage<uChar>(DEFAULT_IMAGE_SIZE/2,DEFAULT_IMAGE_SIZE/2,DEFAULT_IMAGE_SIZE/2);
00063         Set(128,imageY);
00064         Set(128,imageU);
00065         Set(128,imageV);
00066         setPropertyValue< QVImage<uChar, 1> >("Y channel image", imageY);
00067         setPropertyValue< QVImage<uChar, 1> >("U channel image", imageU);
00068         setPropertyValue< QVImage<uChar, 1> >("V channel image", imageV);
00069         QVImage<uChar, 3> imageRGB(DEFAULT_IMAGE_SIZE,DEFAULT_IMAGE_SIZE);
00070         YUV420ToRGB(imageY, imageU, imageV, imageRGB);
00071         setPropertyValue< QVImage<uChar, 3> >("RGB image", imageRGB);
00072 
00073         // Flag needed later to flush out pending images in real time cameras when resetting the camera:
00074         flush_pending_images = FALSE;
00075 
00076         }
00077 
00078 QVCameraWorker::~QVCameraWorker()
00079         {
00080         // closeCam() must be called in the destructor of the derived class, because it is virtual, and thus can't be
00081         // called by this base class destructor.
00082         }
00083 
00084 static inline int iRoundUp(int a, int b) {
00085   return (a % b == 0) ? a : b*(a / b + 1) ;
00086 }
00087 
00088 void QVCameraWorker::tryOpeningCam()
00089 {
00090         // We open the camera with the specified input properties, and update output properties accordingly:
00091         int cols, rows, fps;
00092 
00093         // Read current input properties:
00094         realTime = getPropertyValue<bool>("RealTime");
00095         noLoop = getPropertyValue<bool>("NoLoop");
00096         urlName = getPropertyValue<QString>("URL");
00097         cols =  getPropertyValue<int>("Cols");
00098         rows =  getPropertyValue<int>("Rows");
00099 
00100         // Virtual call; urlName is input parameter; cols and rows are input/output; fps is output.
00101         if(this->openCam(urlName,cols,rows,fps)) 
00102                 {
00103                 setPropertyValue<bool>("Opened",TRUE);
00104                 setPropertyValue<int>("ColsR",cols);
00105                 setPropertyValue<int>("RowsR",rows);
00106                 setPropertyValue<int>("FPS",fps);
00107                 setPropertyValue<int>("Frames",0);
00108                 setPropertyValue<double>("Pos",this->currentPos());
00109                 setPropertyValue<double>("Length",this->lengthOfVideo());
00110 
00111                 imageY = QVImage<uChar>(cols, rows, iRoundUp(cols,8));
00112                 imageU = QVImage<uChar>(cols/2, rows/2, iRoundUp(cols/2,8));
00113                 imageV = QVImage<uChar>(cols/2, rows/2, iRoundUp(cols/2,8));
00114                 setPropertyValue< QVImage<uChar, 1> >("Y channel image", imageY);
00115                 setPropertyValue< QVImage<uChar, 1> >("U channel image", imageU);
00116                 setPropertyValue< QVImage<uChar, 1> >("V channel image", imageV);
00117                 setPropertyValue< QVImage<uChar, 3> >("RGB image", QVImage<uChar,3>(cols, rows, iRoundUp(3*cols,8)));
00118 
00119                 // If the camera is in real time mode, a single shot timer will do the grabbing job (this timer will
00120                 // always be relaunched after each grab()). Otherwise, the QVCameraWorker::iterate() will simply call 
00121                 // explicitly the grabFrame slot.
00122                 if (realTime)
00123                         QTimer::singleShot(0, this, SLOT(grabFrame()));  // Schedule first grabFrame()
00124 
00125                 // Once opened, the worker is ready to run (useful when reopening stopped cameras):
00126                 unPause();
00127 
00128                 // Trick to block too fast consumer workers, if in real time:
00129                 if (realTime)
00130                         connectOrDisconnectLinkedWorkers(TRUE);
00131 
00132                 emit opened();
00133                 }
00134         else    // If something fails when opening, we reset using resetCameraWorker():
00135                 resetCameraWorker();
00136 }
00137 
00138 void QVCameraWorker::connectOrDisconnectLinkedWorkers(bool connect_disconnect)
00139         {
00140         QList<QVPropertyContainer*> list_linked_qvps = getDestinyContainers("RGB image");
00141         list_linked_qvps << getDestinyContainers("Y channel image");
00142         list_linked_qvps << getDestinyContainers("U channel image");
00143         list_linked_qvps << getDestinyContainers("V channel image");
00144         QSet<QVPropertyContainer*> set_linked_qvps = list_linked_qvps.toSet();
00145         QVWorker *worker;
00146         foreach (QVPropertyContainer *qvp, set_linked_qvps)
00147                 if((worker = dynamic_cast<QVWorker*>(qvp)) != NULL)
00148                         if(connect_disconnect)
00149                                 connect(worker,SIGNAL(endIteration(uint,int)),this,SLOT(nothing()),Qt::BlockingQueuedConnection);
00150                         else
00151                                 disconnect(worker,SIGNAL(endIteration(uint,int)),this,SLOT(nothing()));
00152         }
00153 
00154 void QVCameraWorker::resetCameraWorker()
00155         {
00156         // Disconnect consumer workers, if in real time (trick for fast consumer workers):
00157         if(realTime)
00158                 connectOrDisconnectLinkedWorkers(FALSE);
00159 
00160         // Flush any possible timer event pending:
00161         flush_pending_images = TRUE;
00162         while(qApp->hasPendingEvents ()) qApp->processEvents();
00163         flush_pending_images = FALSE;
00164 
00165         // Close camera ...
00166         this->closeCam(); // Virtual call.
00167 
00168         // ... reset output properties (except images and RowsR/ColsR, that are left untouched), ...
00169         setPropertyValue<bool>("Opened",FALSE);
00170         setPropertyValue<int>("FPS",0);
00171         setPropertyValue<int>("Frames",0);
00172         setPropertyValue<double>("Pos",0.0);
00173         setPropertyValue<double>("Length",0.0);
00174 
00175         // ...and stop worker:
00176         stop();
00177 
00178         emit closed();
00179         }
00180 
00181 
00182 bool QVCameraWorker::linkProperty(QVPropertyContainer *destinyContainer, QString destinyPropertyName)
00183         {
00184         // Asynchronous links for real time cameras, synchronous for non real time:
00185         LinkType linkType = getPropertyValue<bool>("RealTime") ? AsynchronousLink : SynchronousLink;
00186 
00187         // Link the destination image property, depending on the number of channels.
00188         if (destinyContainer->isType< QVImage<uChar, 3> >(destinyPropertyName))
00189                 return QVWorker::linkProperty("RGB image", destinyContainer, destinyPropertyName, linkType);
00190         else if (destinyContainer->isType< QVImage<uChar, 1> >(destinyPropertyName))
00191                 return QVWorker::linkProperty("Y channel image", destinyContainer, destinyPropertyName, linkType);
00192         else
00193                 {
00194                 qWarning() << "QVCameraWorker::link(): error, can't link property " << qPrintable(destinyPropertyName) << ".";
00195                 return false;
00196                 }
00197         }
00198 
00199 bool QVCameraWorker::linkProperty(QVPropertyContainer *destinyContainer, QString destinyPropertyName1, QString destinyPropertyName2, QString destinyPropertyName3)
00200         {
00201         // Asynchronous links for real time cameras, synchronous for non real time:
00202         LinkType linkType = getPropertyValue<bool>("RealTime") ? AsynchronousLink : SynchronousLink;
00203 
00204         bool ok = TRUE;
00205 
00206         // Link the destination image properties (must be one channel each)
00207         if (destinyContainer->isType< QVImage<uChar, 1> >(destinyPropertyName1))
00208                 if(not QVWorker::linkProperty("Y channel image", destinyContainer, destinyPropertyName1, linkType))
00209                         {
00210                         ok = FALSE;
00211                         qWarning() << "QVCameraWorker::linkProperty(): error, can't link Y property " << qPrintable(destinyPropertyName1) ;
00212                         }
00213         if (destinyContainer->isType< QVImage<uChar, 1> >(destinyPropertyName2))
00214                 if(not QVWorker::linkProperty("U channel image", destinyContainer, destinyPropertyName2, linkType))
00215                         {
00216                         ok = FALSE;
00217                         qWarning() << "QVCameraWorker::linkProperty(): error, can't link U property " << qPrintable(destinyPropertyName2) ;
00218                         }
00219         if (destinyContainer->isType< QVImage<uChar, 1> >(destinyPropertyName3))
00220                 if(not QVWorker::linkProperty("V channel image", destinyContainer, destinyPropertyName3, linkType))
00221                         {
00222                         ok = FALSE;
00223                         qWarning() << "QVCameraWorker::linkProperty(): error, can't link V property " << qPrintable(destinyPropertyName3) ;
00224                         }
00225 
00226         return ok;
00227         }
00228 
00229 void QVCameraWorker::grabFrame()
00230         {
00231         // Virtual call; imageY, imageU and imageV are output parameters.
00232         if(this->grab(imageY,imageU,imageV))
00233                 {
00234                 setPropertyValue<int>("Frames",getPropertyValue<int>("Frames")+1);
00235                 setPropertyValue<double>("Pos",this->currentPos());
00236                 // Needed here because mplayer does not know the length of the video until it has played a few frames:
00237                 setPropertyValue<double>("Length",this->lengthOfVideo()); 
00238 
00239                 static QTime t;
00240                 if(getPropertyValue<int>("Frames")==1)
00241                         t.start();
00242                 else
00243                         {
00244                         int ms = t.elapsed();
00245                         if(ms>0) setPropertyValue<int>("FPS",1000/ms);
00246                         t.restart();
00247                         }
00248 
00249                 if(realTime) writeOutputProperties();
00250 
00251                 emit grabbed();
00252 
00253                 // Post following timer event to schedule next grabbing (except if we are flushing images out for closing).
00254                 if(realTime and not flush_pending_images)
00255                         QTimer::singleShot(0, this, SLOT(grabFrame()));
00256                 }
00257         else
00258                 resetCameraWorker();
00259         }
00260 
00261 void QVCameraWorker::iterate()
00262         {
00263         // Try opening camera if first iteration (performed here, instead of the constructor, to assure 
00264         // that openCam() is executed always in this thread.
00265         if(getIteration()==0)
00266                 tryOpeningCam();
00267 
00268         // Needed for not try to grab from closed (stoped) cameras:
00269         if(isStoped()) return; 
00270 
00271         // If the camera is in real time mode, the timer should have called the slot for us, so we do not have to do it
00272         // explicitly. Otherwise, we call grabFrame();
00273         if (not realTime)
00274                 grabFrame();
00275 
00276         setPropertyValue< QVImage<uChar, 1> >("Y channel image", imageY);
00277         setPropertyValue< QVImage<uChar, 1> >("U channel image", imageU);
00278         setPropertyValue< QVImage<uChar, 1> >("V channel image", imageV);
00279 
00280         if (isLinkedOutput("RGB image"))
00281                 {
00282                 QVImage<uChar, 3> imageRGB(imageY.getCols(),imageY.getRows());
00283                 YUV420ToRGB(imageY, imageU, imageV, imageRGB);
00284                 setPropertyValue< QVImage<uChar, 3> >("RGB image", imageRGB);
00285                 }
00286 
00287         }
00288 
00289 void QVCameraWorker::reopen()
00290         {
00291         // Do not try to reopen again if in the middle of a reopening process (could happen if the user presses the
00292         // "Reopen" camera widget button several times very quickly). This condition avoids it:
00293         if(flush_pending_images) return;
00294 
00295         // When reopening, we must reset the camera worker ...
00296         resetCameraWorker();
00297         // ... and open the camera again using the new input properties.
00298         readInputProperties();
00299         tryOpeningCam();
00300         }



QVision framework. PARP research group, copyright 2007, 2008.