/*
- This file is one part of two, that together make
- QOpenCamWidget, a Qt 4 widget that displays video input
- from a webcam, along with an optional snapshot button.
+ This file is one part of two, that together make QOpenCamWidget,
+ a Qt 4 widget that displays video input from a webcam.
Copyright (C) 2009 Jon Langseth
* This class solves the complexity of adding a webcam view,
* by using the cross-platform available OpenCV library.
*
- * Limitations, i.e. reasons to read this code and reimplement,
- * are: saving or streaming video is not really available (unless
- * you do repeated timer-triggered connections to the startSnap slot),
- * the widget size is identical to the video source dimensions (it
- * resizes the wodget using setMinimuSize to the video dimensions,
- * and does not handle resizing to sizes above this dimension.
+ * Limitations, i.e. reasons to read this code and reimplement, are:
+ *
+ * \li saving or streaming video is not really available (unless you do
+ * repeated timer-triggered connections to the startSnap slot).
+ * \li this is a crude and simple implementation, created to solve
+ * the problem of creating a "view-and-shoot" camera app.
+ * If your needs are more complex than that, read the code,
+ * and reimplement.
+ *
+ * Note that even tough the main file here totals some ~250 lines,
+ * only about 90 of those are actual code lines, and and even those
+ * contain a lot of "air"...
*
* A brief summary of how to use this class:
* \code
* QOpenCamWidget *cw = new QOpenCamWidget(this);
* if ( cw->grabCapture(-1) ) {
- * cw->setSnapshotVisible(true);
* cw->startCapture();
* }
* connect( cw, SIGNAL(imageReady(QImage)), this, SLOT(saveImage(QImage)));
+ *
+ * QPushButton *trigger = new QPushButton(this);
+ * trigger->setText("Take picture");
+ * connect( trigger, SIGNAL(clicked()), cw, SLOT(startSnap()));
+ *
+ * \endcode
+ *
+ * Setting sizes is done during construction, and are not modifiable
+ * after the fact:
+ * \code
+ * QOpenCamWidget *cw = new QOpenCamWidget(this,
+ * new QSize(320, 240),
+ * new QSize(960, 720));
* \endcode
*
* \param *parent The parent widget containing this widget, defaults to NULL.
+ * \param *viewSize The size of the image displayed, thus the size of the widget,
+ * defaults to 640x480
+ * \param *resolution The actual resolution captured, and size of the snapshot
+ * created, defaults t0 960x720, should be larger than viewSize
*
**/
-QOpenCamWidget::QOpenCamWidget(QWidget *parent)
+QOpenCamWidget::QOpenCamWidget(QWidget *parent, QSize *viewSize, QSize *resolution)
: QWidget(parent)
{
- // private CvCapture *nextFrame from class definition
+ // Setting sane default values (i.e. NULL) for
+ // private CvCapture *nextFrame and
+ // private QTimer *frametimer from class definition
nextFrame = NULL;
- // private QTimer *frametimer from class definition
frametimer = NULL;
- // private QVBoxLayout *layout from class definition
- layout = new QVBoxLayout(this);
-
- // private QLabel *canvas from class definition
- canvas = new QLabel(this);
- canvas->setMinimumSize(200, 100);
- canvas->setAlignment(Qt::AlignCenter);
+ if ( viewSize == NULL )
+ {
+ vSize = new QSize(640, 480);
+ }
+ else vSize = viewSize;
+ if ( resolution == NULL )
+ {
+ res = new QSize(960, 720);
+ }
+ else res = resolution;
- // private QPushButton *trigger from class definition
- trigger = new QPushButton(this);
- trigger->setText("Take picture");
- trigger->setEnabled(false);
- trigger->hide();
+ this->setMinimumSize(*vSize);
+ this->setMaximumSize(*vSize);
- // private bool trigger_active from class definition
- trigger_active = false;
-
- layout->addWidget(canvas);
- this->setLayout(layout);
}
QOpenCamWidget::~QOpenCamWidget(void)
{
cvReleaseCapture( &capture );
- delete canvas;
- delete trigger;
}
// ------------------------------------------------------------- //
// Public methods (not signals/slots)
// ------------------------------------------------------------- //
-/*!
- * \brief Changes the visibility of the optional built-in "Take snapshot" button.
- *
- * The widget contains a push-button that optionally can be displayed. When
- * visible, this button is located at the bottom of the widget, and causes
- * the SLOT QOpenCamWidget::startSnap to be triggered when clicked.
- *
- * \param visible True makes the button display, and trigger,
- * false turns the feature off. False, i.e. no button, is default.
- *
- **/
-void
-QOpenCamWidget::setSnapshotVisible(bool visible)
-{
- // Checking both parameter and private "sanity" variable,
- // as a sanity- and error-control
- if ( visible && !trigger_active )
- {
- connect( trigger, SIGNAL(clicked()), this, SLOT(startSnap()));
- layout->addWidget(trigger);
- trigger->show();
- trigger_active = true;
- }
- if ( !visible && trigger_active )
- {
- layout->removeWidget(trigger);
- disconnect( trigger, SIGNAL(clicked()), 0, 0 );
- trigger->hide();
- trigger_active = false;
- }
-}
/*!
* \brief A paint event is a request to repaint all or part of a widget.
*
if ( nextFrame )
{
- // Using this objects painter to draw the image
- // causes a cute, but not desired effect..
- //paint->drawImage(event->rect(), *nextFrame);
-
- // Until I find a better solution on how to do the
- // redraw of the image, setPixmap on the canvas
- // does do the job. This may cause a performance
- // penalty though, and is considered a FIXME
- // Another drawback is that the widget does not resize..
- canvas->setPixmap( QPixmap::fromImage(*nextFrame) );
+ // To make the widget as blazingly fast as possible
+ // we output the last captured frame direclty onto
+ // the widget area.
+ // It has been slowed down a bit by doing a resize tho...
+ paint->drawImage(event->rect(), nextFrame->scaled( vSize->width(), vSize->height() ));
}
else
{
- canvas->setText("No data, check/test camera");
+ paint->drawText(event->rect(),"No data, check/test camera");
}
paint->end();
+ // Clean up..
+ delete(paint);
}
bool
QOpenCamWidget::grabCapture(int source)
{
- capture = cvCaptureFromCAM(0);
+ capture = cvCreateCameraCapture(0);
if (!capture)
{
qDebug() << "QOpenCamWidget::grabCapture(" << source << ") failed";
return false;
}
+ cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_WIDTH, res->width() );
+ cvSetCaptureProperty( capture, CV_CAP_PROP_FRAME_HEIGHT, res->height() );
+
cvGrabFrame(capture); // Grab a single frame, do resizing based on it.
IplImage *image = cvRetrieveFrame(capture);
- QSize t_size = QSize(image->width,image->height);
-
- qDebug() << "Device image format: " << image->width << "x" << image->height;
- canvas->setMinimumSize(t_size);
- canvas->setMaximumSize(t_size);
return true;
}
QOpenCamWidget::startCapture(void)
{
frametimer = new QTimer(this);
- frametimer->start(70);
+ frametimer->start(50);
connect(frametimer,SIGNAL(timeout()), this,SLOT(grabFrame()));
- trigger->setEnabled(true);
}
/*!
*
**/
QImage*
-QOpenCamWidget::Ipl2QImage(const IplImage *img)
+QOpenCamWidget::Ipl2QImage(IplImage *img)
{
- IplImage *tmp=cvCloneImage(img);
- cvConvertImage(img,tmp, CV_CVTIMG_SWAP_RB );
- QImage * qimage = new QImage(reinterpret_cast<uchar*>(tmp->imageData),
- tmp->width,
- tmp->height,
- 3* tmp->width,
- QImage::Format_RGB888);
+ cvConvertImage(img,img, CV_CVTIMG_SWAP_RB);
+ QImage * qimage = new QImage(
+ reinterpret_cast<uchar*>(img->imageData),
+ img->width, img->height,
+ 3* img->width, QImage::Format_RGB888);
return qimage;
}
**/
void QOpenCamWidget::grabFrame(void)
{
- if ( !capture )
- {
- qDebug() << "Capture device not ready!";
- return;
- }
+ if ( !capture ) { qDebug() << "Capture device not ready!"; return; }
cvGrabFrame(capture);
IplImage *iplimage = cvRetrieveFrame(capture);
-
if (iplimage)
{
nextFrame = Ipl2QImage(iplimage);
{
nextFrame = NULL;
}
+
update();
}
* stopped, and the last displayed frame is "captured", and made
* available through the emitting of the class imageReady SIGNAL.
*
- * With the "Take snapshot" button visible (setSnapshotVisible(true)),
- * this SLOT is triggered when the user clicks on the trigger button.
- * If you do not wish to use the internal trigger button, you
- * will have to add a different mechanism to trigger this SLOT.
- *
* It is possible, though I would not recommend, to use repeated
* triggering of this slot to do repeated frame-capture, and thus
* make a form of "Animation" or "Video" capture.
if (frametimer->isActive())
{
frametimer->stop();
- qDebug() << "SNAP!";
-
emit imageReady(QImage(*nextFrame));
frametimer->start();
}