I use my plot classes based on qwtplot, code can be viewed here:
https://github.com/pospiech/code/tree/master/libdev/plot/plottools/trunk/src
This code is used in another application, here I create qwtplot classes (QMatrixPlot) and add data and call replot
plot2DAmplitude->setMatrixData(QVector<double>::fromStdVector(dataAmplitude),
xaxis.size(),
QwtInterval(xaxis.front(), xaxis.back()),
QwtInterval(yaxis.front(), yaxis.back()));
plot2DAmplitude->replot();
setMatrixData is the following code:
void setMatrixData(const QVector< double > &values, int numColumns, QwtInterval xAxisRange, QwtInterval yAxisRange)
{
m_MatrixRasterData->setInterval( Qt::XAxis, xAxisRange );
m_MatrixRasterData->setInterval( Qt::YAxis, yAxisRange );
double minValue = *std::min_element( std::begin(values), std::end(values) );
double maxValue = *std::max_element( std::begin(values), std::end(values) );
m_MatrixRasterData->setInterval( Qt::ZAxis, QwtInterval(minValue, maxValue) );
m_MatrixRasterData->setValueMatrix (values, numColumns);
d_spectrogram->setData( m_MatrixRasterData );
const QwtInterval zInterval = d_spectrogram->data()->interval( Qt::ZAxis );
setAxisScale( QwtPlot::yRight, zInterval.minValue(), zInterval.maxValue() );
QwtScaleWidget *axis = axisWidget( QwtPlot::yRight );
axis->setColorMap( zInterval, QColorMap::map(d_mapType) );
}
This works - once.
The second call with different data does nothing, although the same code is called. Only If I zoom in and out the data gets an update.
Any idea? Qwt is version 6.1.3. Once I finish a minimal example I will update this post.
QwtPlotRasterItem caches the rendered image. In your code you are changing the data behind the back of the item, so the item is not aware of the cache being invalid.
With QwtPlotRasterItem::invalidateCache() you can solve problems like this one.
Related
I use VTK to display a point cloud and update it in a loop.
The windows can be interacted by mouse for the first time I display the cloud, if I want to update the cloud, I have to press 'q' and the Window will display updated cloud. However, although the cloud is updated and shown in the window correctly, I lost control of the window (i.e. cannot use mouse to move or rotate the point cloud in the window).
Following is my minimum code that recreates the issue.
I have a Visualization class to handle this, its initialization list:
points(vtkSmartPointer<vtkPoints>::New())
, vertices(vtkSmartPointer<vtkCellArray>::New())
, pointsPolyData(vtkSmartPointer<vtkPolyData>::New())
, mapper(vtkSmartPointer<vtkPolyDataMapper>::New())
, actor(vtkSmartPointer<vtkActor>::New())
, renderer(vtkSmartPointer<vtkRenderer>::New())
, renderWindow(vtkSmartPointer<vtkRenderWindow>::New())
, renderWindowInteractor(vtkSmartPointer<vtkRenderWindowInteractor>::New())
Here is the init function:
void Visualization::init(const cv::Mat &point_cloud){
noPoints = point_cloud.cols * point_cloud.rows;
// Preallocate memory
points->SetNumberOfPoints(noPoints);
// Copy
int element_stripe = sizeof(float) * point_cloud.channels();
for (unsigned i = 0; i < noPoints; i++)
{
float x = point_cloud.at<cv::Vec3f>(i)[0];
float y = point_cloud.at<cv::Vec3f>(i)[1];
float z = point_cloud.at<cv::Vec3f>(i)[2];
vtkIdType pid[1] = {i};
points->SetPoint(i, x, y, z);
vertices->InsertNextCell(1, pid);
}
// Push data to polydata
pointsPolyData->SetPoints(points);
pointsPolyData->SetVerts(vertices);
// Set visualization pipeline
mapper->SetInputData(pointsPolyData);
actor->SetMapper(mapper);
actor->GetProperty()->SetPointSize(2);
renderWindow->AddRenderer(renderer);
renderer->SetBackground(.3,.6,.3);
renderWindowInteractor->SetRenderWindow(renderWindow);
renderer->AddActor(actor);
isPipelineInit = true;
renderWindow->Render();
renderWindowInteractor->Start();
}
Here is the display function:
void Visualization::display(const cv::Mat &point_cloud){
if(!isPipelineInit)
init(point_cloud);
else
{
// Copy
int element_stripe = sizeof(float) * point_cloud.channels();
for (unsigned i = 0; i < noPoints; i++)
{
points->SetPoint(i, (float*)(point_cloud.data + i*element_stripe));
}
pointsPolyData->Modified();
mapper->Update();
renderWindowInteractor->GetRenderWindow()->Render();
}
}
I also have a function that run an infinite loop in a thread:
void Visualization::run()
{
while (1) // infinite while loop
{
// Update m_clouds and display
display(m_clouds);
}
}
UPDATE:
As mirni remind me that 'q' key essentially quit current renderWindow, what I see later on is actually from the display function rather then the init. In the display I didn't call renderWindowInteractor->Start() and therefore I cannot interact with the window. However, the renderWindowInteractor->Start() stuck my current thread and therefore I cannot continue my program and update my vtkPolyData.
I guess the question then becomes:
How shall I display and update at the same time?
Shall I do it in different thread, so one for display and another for update?
Thanks!
The answer to your question is: You just update the data and call vtkRenderWindow::Render() method. VTK rendering pipeline discovers by itself that data has changed and that it needs to update.
Your code really needs to be redesigned -- it makes more sense to update the visualization when the data changed (only once), rather than keep probing the data "has anything changed?" (every millisecond).
Also, normally you don't need a separate thread for VTK visualization pipeline. Keep it simple:
Don't make the visualization object a thread. (What if you have 100 objects? Will you create 100 separate threads one for each of then?).
Get rid of the infinite loop.
Rename the display(...) method to
void Visualization::update(const cv::Mat &point_cloud);
make it public and call it from outside whenever data changes by any means that fit your scenario (callback? Qt signal? message? ...). Make sure to keep the vtkRenderWindow::Render() call at the end.
You may want to consider copying the data from cv::Mat struct directly, using the raw pointer to the point data exposed by vtkPoints::GetVoidPointer(int) and do a memcopy instead of the for loop, but don't do this until you have things in place and your measurements show the need for optimization. (Premature optimization is the root...)
vtkRenderWindowInteractor::Start() starts an event loop and hijacks the thread as you found out. Instead of Start() just call Initialize() to enable interactor but let the control flow go on.
HTH,
Miro
I'm creating a simple configuration panel window to modify some HSV values for an OpenCV program. This is the specific code:
int HUE_MIN = 0;
int HUE_MAX = 256;
int SAT_MIN = 0;
int SAT_MAX = 256;
int VAL_MIN = 0;
int VAL_MAX = 256;
const string configPanelWindowName = "Configuration panel";
void createTrackbars()
{
namedWindow(configPanelWindowName, 0);
cv::createTrackbar( "HUE_MIN", configPanelWindowName, &HUE_MIN, HUE_MAX );
cv::createTrackbar( "HUE_MAX", configPanelWindowName, &HUE_MAX, HUE_MAX );
cv::createTrackbar( "SAT_MIN", configPanelWindowName, &SAT_MIN, SAT_MAX );
cv::createTrackbar( "SAT_MAX", configPanelWindowName, &SAT_MAX, SAT_MAX );
cv::createTrackbar( "VAL_MIN", configPanelWindowName, &VAL_MIN, VAL_MAX );
cv::createTrackbar( "VAL_MAX", configPanelWindowName, &VAL_MAX, VAL_MAX );
}
void createTrackbars();
I've followed some video demos and in most of them the trackbar shows its current value in integer numbers but in my case the numbers don't appear and I don't know why I can implement them. I've tried to update trackbar labels through the onChange parameter but it doesn't work because the label parameter should be a constant.
I don't want to use cv::putText() function because I'm not using an image into the config panel window and I'm currently using a workaround with cv::putText in a different window to show the current values of trackbars.
Finally I don't know if this is related, but all the envirements of the demos I've seen are different to mine. I'm using Mac OS X 10.10 and they instead use Ubuntu and Windows so I think that can cause this behavior.
So I want to know why this feature doesn't work for me and if there's a workaround different to use cv::putText
I am using Qt add in for Visual Studio C++ for my GUI.
And on my GUI, I have a button called the plotButton which will draw an histogram of the image when clicked. My plotting option is via the usage of QWT.
However, it does not seem to be plotting anything and closes almost immediately. Tried sleep(), but it doesn't seem to work either. Could the problem be with my code?
Here is my code for reference:
void qt5test1 ::on_plotButton_clicked()
{
//Convert to grayscale
cv::cvtColor(image, image, CV_BGR2GRAY);
int histSize[1] = {256}; // number of bins
float hranges[2] = {0.0, 255.0}; // min andax pixel value
const float* ranges[1] = {hranges};
int channels[1] = {0}; // only 1 channel used
cv::MatND hist;
// Compute histogram
calcHist(&image, 1, channels, cv::Mat(), hist, 1, histSize, ranges);
double minVal, maxVal;
cv::minMaxLoc(hist, &minVal, &maxVal);//Locate max and min values
QwtPlot plot; //Create plot widget
plot.setTitle( "Plot Demo" ); //Name the plot
plot.setCanvasBackground( Qt::black ); //Set the Background colour
plot.setAxisScale( QwtPlot::yLeft, minVal, maxVal ); //Scale the y-axis
plot.setAxisScale(QwtPlot::xBottom,0,255); //Scale the x-axis
plot.insertLegend(new QwtLegend()); //Insert a legend
QwtPlotCurve *curve = new QwtPlotCurve(); // Create a curve
curve->setTitle("Count"); //Name the curve
curve->setPen( Qt::white, 2);//Set colour and thickness for drawing the curve
//Use Antialiasing to improve plot render quality
curve->setRenderHint( QwtPlotItem::RenderAntialiased, true );
/*Insert the points that should be plotted on the graph in a
Vector of QPoints or a QPolgonF */
QPolygonF points;
for( int h = 0; h < histSize[0]; ++h) {
float bin_value = hist.at<float>(h);
points << QPointF((float)h, bin_value);
}
curve->setSamples( points ); //pass points to be drawn on the curve
curve->attach( &plot ); // Attach curve to the plot
plot.resize( 600, 400 ); //Resize the plot
plot.replot();
plot.show(); //Show plot
Sleep(100);
}
Upon clicking this button after loading the image, a window appear and disappear immediately. Under the output window, the lines can be found.
First-chance exception at 0x75d54b32 (KernelBase.dll) in qt5test1.exe: Microsoft C++ exception: cv::Exception at memory location 0x0044939c..
Unhandled exception at 0x75d54b32 (KernelBase.dll) in qt5test1.exe: Microsoft C++ exception: cv::Exception at memory location 0x0044939c..
Does anybody have any idea what could be wrong with my code? Thanks. Please note once again that the program is build, and written within Microsoft Visual Studio C++. Thanks.
The problem is that you are constructing a stack object here:
QwtPlot plot; //Create plot widget
That is true that you are trying to show the plot at the end of the method, but the show() method is not blocking with an event loop like the QDialog classes when you use the exec() call on them.
It would be processed, but you are leaving the scope right after the call either way.
There are several ways of addressing this issue, but I would strive for the Qt parent/child hierarchy where the deletion will come automatically when using pointers.
1) Qt parent/child relation
QwtPlot *plot = new QwtPlot(this);
^^^^
2) Make "plot" a class member
plot.show();
and construct it in the class constructor.
3) Use a smart pointer
QSharedPointer<QwtPlot> plot = QSharedPointer<QwtPlot>(new QwtPlot());
It depends on your further context of the class which way to pick up, so try to understand these approaches, and take your peek.
plot should be created using new. Now you create it on stack, so it will be deleted immediately when on_plotButton_clicked function finished. Sleep should not be used here, you won't get any good from it.
QwtPlot* plot = new QwtPlot();
plot->setTitle( "Plot Demo" ); //Name the plot
//...
plot->show(); //Show plot
The problem might be that your QwtPlot is just a local variable and even because the sleep is also in the main thread you won't be able to draw it in time before the function returns. Then when it finish sleeping it destroys your local QwtPlot object and returns, so you are luck enough if you get a blink of the window like that.
To make it work you will have to call it like this:
QwtPlot* plot = new QwtPlot(this);
where this is the parent window that will host your plot (if any). That way your widget will remain alive until you close it or its parent destroy it at the end of the program execution.
I'm trying to use the OpenCV stitcher class to stitch multiple frames from a stereo setup, in which neither camera moves. I'm getting poor stitching results when running across multiple frames. I've tried a few different ways, which I'll try to explain here.
Using stitcher.stitch( )
Given a stereo pair of views, I ran the following code for some frames (VideoFile is a custom wrapper for the OpenCV VideoCapture object):
VideoFile f1( ... );
VideoFile f2( ... );
cv::Mat output_frame;
cv::Stitcher stitcher = cv::Stitcher::createDefault(true);
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
stitcher.stitch( currentFrames, output_mat );
// Write output_mat, put it in a named window, etc...
f1.next_frame();
f2.next_frame();
currentFrames.clear();
}
This gave really quite good results on each frame, but since the parameters are estimated each frame put in a video you could see the small differences in stitching where the parameters were slightly different.
Using estimateTransform( ) & composePanorama( )
To get past the problem of the above method, I decided to try estimating the parameters only on the first frame, then use composePanorama( ) to stitch all subsequent frames.
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
if( ! have_transform ) {
status = stitcher.estimateTransform( currentFrames );
}
status = stitcher.composePanorama(currentFrames, output_frame );
// ... as above
}
Sadly there appears to be a bug (documented here) causing the two views to move apart in a very odd way, as in the images below:
Frame 1:
Frame 2:
...
Frame 8:
Clearly this is useless, but I thought it may be just because of the bug, which basically keeps multiplying the intrinsic parameter matrix by a constant each time composePanorama() is called. So I did a minor patch on the bug, stopping this from happening, but then the stitching results were poor. Patch below (modules/stitching/src/stitcher.cpp), results afterwards:
243 for (size_t i = 0; i < imgs_.size(); ++i)
244 {
245 // Update intrinsics
246 // change following to *=1 to prevent scaling error, but messes up stitching.
247 cameras_[i].focal *= compose_work_aspect;
248 cameras_[i].ppx *= compose_work_aspect;
249 cameras_[i].ppy *= compose_work_aspect;
Results:
Does anyone have a clue how I can fix this problem? Basically I need to work out the transformation once, then use it on the remaining frames (we're talking 30mins of video).
I'm ideally looking for some advice on patching the stitcher class, but I would be willing to try handcoding a different solution. An earlier attempt which involved finding SURF points, correlating them and finding the homography gave fairly poor results compared to the stitcher class, so I'd rather use it if possible.
So in the end, I hacked about with the stitcher.cpp code and got something close to a solution (but not perfect as the stitching seam still moves about a lot so your mileage may vary).
Changes to stitcher.hpp
Added a new function setCameras() at line 136:
void setCameras( std::vector<detail::CameraParams> c ) {
this->cameras_ = c;
}`
Added a new private member variable to keep track of whether this is our first estimation:
bool _not_first;
Changes to stitcher.cpp
In estimateTransform() (line ~100):
this->not_first = 0;
images.getMatVector(imgs_);
// ...
In composePanorama() (line ~227):
// ...
compose_work_aspect = compose_scale / work_scale_;
// Update warped image scale
if( !this->not_first ) {
warped_image_scale_ *= static_cast<float>(compose_work_aspect);
this->not_first = 1;
}
w = warper_->create((float)warped_image_scale_);
// ...
Code calling stitcher object:
So basically, we create a stitcher object, then get the transform on the first frame (storing the camera matrices outside the stitcher class). The stitcher will then break the Intrinsic Matrix somewhere along the line causing the next frame to mess up. So before we process it, we just reset the cameras using the ones we extracted from the class.
Be warned, I had to have some error checking in case the stitcher couldn't produce an estimation with the default settings - you may need to iteratively decrease the confidence threshold using setPanoConfidenceThresh(...) before you get a result.
cv::Stitcher stitcher = cv::Stitcher::createDefault(true);
std::vector<cv::detail::CameraParams> cams;
bool have_transform = false;
for( int i = 0; i < num_frames; i++ ) {
currentFrames.push_back(f1.frame( ));
currentFrames.push_back(f2.frame( ));
if( ! have_transform ) {
status = stitcher.estimateTransform( currentFrames );
have_transform = true;
cams = stitcher.cameras();
// some code to check the status of the stitch and handle errors...
}
stitcher.setCameras( cams );
status = stitcher.composePanorama(currentFrames, output_frame );
// ... Doing stuff with the panorama
}
Please be aware that this is very much a hack of the OpenCV code, which is going to make updating to a newer version a pain. Unfortunately I was short of time so a nasty hack was all I could get round to!
Is there any way to set text labels for each item of a pie diagram, created using KDChart lib in Qt?
To be more specific, I'm not using the Model/View architecture in this particular case. I create it though KDChart::Widget and merely fill the chart using Widget::setDataCell().
Seemingly there are several ways to set text labels for axis, but I haven't encountered something similar for a pie diagram. Anyway it's not the thing I need. I want to set labels for certain points rather than for its axis. In apply to a pie diagram it would be something like titled sectors.
I thought that maybe with using KDChart::Legend with filled values I can achieve required behavior, but it haven't worked.
Here is a code sample, maybe it will help somewhat. But keep in mind that it's changed (cleared of cluttering lines) and I haven't tested its correctness:
KDChart::Widget* newChart = new KDChart::Widget;
newChart->setType( KDChart::Widget::Pie );
int curColNo = 0; // it's not a size_t 'coz setDataCell requires an int
for( QVector::const_iterator curValueIt = response.begin(); curValueIt != response.end(); ++curValueIt )
{
newChart->setDataCell( 0, curColNo, *curValueIt );
newChart->diagram()->setBrush( curColNo++, QBrush( m_responsesColors[curValueIt] ) );
m_legend->addDiagram( newChart->diagram() );
}
m_mainLayout.addWidget( newChart, m_curLayoutRowNo, m_curLayoutColNo );
One more thing - I tried to fill it with inconsistent column numbers (0,2,5,9,etc) and pie chart was drawn incorrectly - some sectors overlapped others. In other types of charts (bar chart, for example) all data was visualized correctly.
Do you have any ideas about item labels?
P.S. I've figured out what's wrong with filling Pie chart's columns with skipping some of them. If you fill columns inconsistently (skipping some of them), then just set those skipped columns' values to zero explicitly. It will fix problems with wrong pie chart's visualizing.
Probably KDChart should figure out about skipped columns by itself and set it to null automatically, but it won't. So do it yourself.
Hope, this will help someone.
I have found a solution by my own. Considering a small amount of info on KDChart library, I'm posting it here in hope it will help someone with similar problem.
The solution lies quite deeply in the KDChart hierarchy. You need to manually turn on labels display. I've created a separated function for it.
void setValuesVisible( KDChart::AbstractDiagram* diagram, bool visible ) throw()
{
const QFont font( QFont( "Comic", 10 ) ); // the font for all labels
const int colCount = diagram->model()->columnCount();
for ( int iColumn = 0; iColumn < colCount; ++iColumn )
{
//QBrush brush( diagram->brush( iColumn ) ); // here you can get a color of the specified column
KDChart::DataValueAttributes a( diagram->dataValueAttributes( iColumn ) );
KDChart::TextAttributes ta( a.textAttributes() );
ta.setRotation( 0 );
ta.setFont( font );
ta.setAutoRotate( true );
//ta.setPen( QPen( brush.color() ) ); // here you can change a color of the current label's text
ta.setVisible( visible ); // this line turns on labels display
a.setTextAttributes( ta );
a.setVisible( true );
diagram->setDataValueAttributes( iColumn, a);
}
diagram->update();
}
Keep in mind, that there is shorter solution - just set TextAttributes of the "global" DataValueAttributes (there is a method for it in the KDChart::AbstractDiagram class - AbstractDiagram::dataValueAttributes() without any params) if you don't need unique text parameters for each label.