UI Window to show an image with drawings
As any image processing software we should have some sort of basic (separate) UI structure which serves as an independent instrument to showing results at first stage. Such tool must be supported for all OSes.
Not sure if I understand correctly the issue but isn't it solved with the qimage_display example?
Not really. We need our own simpler API (wrapper) for this as QT file compilation is different but it's pretty close to what I did in the example.
I understand. I had love to come up with a design by the end of this week.
Will work on this one.
Thinking about the structure for future UI basement. There is an idea of file structure: Base class:
src/ui/ui.h
src/ui/ui.cpp
QT class
src/ui/qt/ui.h
src/ui/qt/ui.cpp
We need this because we could assume that not everybody has QT installed on their machines. Additionally in future we could come out with an idea to support OpenGL (or DirectX on Windows) so we just add extra classes inherited from base class.
Talking about the context of base class. We might need to have some class called UiWindow which should have possible functions:
show();
setImage( const PenguinV::Image & image );
drawPoint( Point2d point );
drawLine( Point2d start, Point2d end );
In an application we might want to have something like this:
...
// some image processing code
UiWindow window ( image );
window.show();
@ihhub I mostly agree with you about the architecture. Nevertheless I'm not sure if for a basic view functionality, we really need native OpenGL support as Qt already provide it. DirectX is also supported by Qt through the Angle library. Further, there is also a Vulkan support since Qt 5.10.
Do we also currently need to provide functions to draw primitives although the library is currently targeting image manipulation?
Drawing primitives is a good visual tool to verify that your computations are correct. For example you want visually see where found edge of an object locates at image. That's what drawPoint useful for.
Drawing primitives is a good visual tool to verify that your computations are correct. For example you want visually see where found edge of an object locates at image. That's what drawPoint useful for.
Fair point. I'm still studying the library :).
I agree that QT supports a lot of technologies but we have to look to our problem from different angle of view: let's assume that a developer only has Visual Studio on it's machine. Installing QT for UI display procedures would be a big step backwards in terms of development as he/she needs to integrate QT into Visual Studio. Another example: you develop an iOS application on XCode. This framework has it's own engine for UI code so installing QT is not a good way to do.
As a conclusion: that's why I proposed to have base class and derived class (for QT) as of now so for future we might add OpenGL or DirectX or something else (assuming that it's easy to implement). The library would choose needed technology (assuming it's done by CMake) and use it internally. For a developer it would be hidden.
I see. My argument was, if a user needs GUI support, he should pay the price of installing an additional library as he would do for other stuff. Anyway I'm completely fine with your suggestion.
Edge detection code is here so now UI to show points on image is required :)
Edge detection code is here so now UI to show points on image is required :)
Nice. I will continue work on this later this week. I'm at the Meeting C++ conference :).
Congratulations! I have never been at any C++ conference :)
My vision of UI base class. src/ui/ui.h file:
#pragma once
#include <vector>
#include "../image_buffer.h"
#include "../math/math_base.h"
class UiWindow
{
public:
UiWindow( const PenguinV_Image::Image & image = PenguinV_Image::Image() );
virtual ~UiWindow();
void show(); // show window at the screen
void setImage( const PenguinV_Image::Image & image ); // replaces existing shown image by new image
void drawPoint( const Point2d & point );
void drawLine( const Point2d & start, const Point2d & end );
protected:
virtual void draw();
PenguinV_Image::Image _image; // we store a copy of image
bool _shown;
std::vector< Point2d > _point;
std::vector< Point2d > _line; // we store 2 points per line
};
src/ui/ui.cpp file:
#include "ui.h"
UiWindow::UiWindow( const PenguinV_Image::Image & image)
: _image( image )
, _shown( false)
{
}
UiWindow::~UiWindow()
{
}
void UiWindow::show()
{
_shown = true;
draw();
}
void UiWindow::setImage( const PenguinV_Image::Image & image )
{
_image = image;
draw();
}
void UiWindow::drawPoint( const Point2d & point )
{
_point.push_back( point );
draw();
}
void UiWindow::drawLine( const Point2d & start, const Point2d & end )
{
_line.push_back( start );
_line.push_back( end );
draw();
}
void UiWindow::draw()
{
}
src/ui/qt/qt_ui.h file:
#pragma once
#include "../ui.h"
#include <QLabel>
#include <QPixmap>
class UiWindowQt: public UiWindow
{
public:
UiWindowQt( const PenguinV_Image::Image & image = PenguinV_Image::Image() );
virtual ~UiWindowQt();
protected:
virtual void draw();
private:
QPixmap _pixmap;
QLabel _window;
};
src/ui/qt/qt_ui.cpp file:
#include "qt_ui.h"
#include <QImage>
UiWindowQt::UiWindowQt( const PenguinV_Image::Image & image )
: UiWindow( image )
{
const QImage imageQt( image.data(), image.width(), image.height(), image.rowSize(), image.colorCount() == 1u ? QImage::Format_Grayscale8 : QImage::Format_RGB888 );
_pixmap = QPixmap::fromImage( imageQt );
_window.setPixmap( _pixmap );
}
UiWindowQt::~UiWindowQt()
{
}
void UiWindowQt::draw()
{
if( !_shown ) // we don't need to draw anything
return;
_window.show();
}
Of course it's just a code written in web editor with some problems but I think you got an idea :)
Haha. You took all the fun away :). I'm a little bit busy at work currently. I might push code by the end of the week.
I modified my previous comment. Now it works except drawing points and lines :)
Our QT example would be like this:
#include <iostream>
#include <QApplication>
#include <QFileDialog>
#include <QLabel>
#include "../../../src/blob_detection.h"
#include "../../../src/image_buffer.h"
#include "../../../src/image_function.h"
#include "../../../src/FileOperation/bitmap.h"
#include "../../../src/ui/qt/qt_ui.h"
int main( int argc, char *argv[] )
{
try
{
// First of all we create QT application
QApplication app( argc, argv );
// Then we retrieve a path for bitmap file
const QString & fileName = QFileDialog::getOpenFileName( NULL,
QObject::tr("Open Bitmap image"), "",
QObject::tr("Bitmap (*.bmp);;All Files (*)") );
// Load a color image from storage
PenguinV_Image::Image original = Bitmap_Operation::Load( fileName.toUtf8().constData() );
if( original.colorCount() != PenguinV_Image::RGB || original.empty() ) {
std::cout << "Looks like no image or it is not a color image" << std::endl;
return 0;
}
// Display image in separate window
UiWindowQt window1( original );
window1.show();
// Because our logo is green-white so we extract red channel to make green areas be as black on gray-scale image
PenguinV_Image::Image red = Image_Function::ExtractChannel( original, 0 );
// Convert image into QImage format
UiWindowQt window2( red );
window2.show();
// We theshold image for proper blob detection
// Remark: actually we can pass the threshold to blob detection function so we can skip this step
// But we made this just to show how it works
PenguinV_Image::Image thresholded = Image_Function::Threshold( red, Image_Function::GetThreshold( Image_Function::Histogram( red ) ) );
// Convert image into QImage format
UiWindowQt window3( thresholded );
window3.show();
// Perform blob detection
Blob_Detection::BlobDetection detection;
detection.find( thresholded );
// Create an ouput image
PenguinV_Image::Image output( thresholded.width(), thresholded.height() );
output.fill( 0 );
// Sort blobs by size and do NOT draw an edge of biggest blob what is actually white backgroud
detection.sort( Blob_Detection::BlobDetection::CRITERION_SIZE );
for( std::vector <Blob_Detection::BlobInfo>::const_iterator blob = detection().begin() + 1; blob != detection().end(); ++blob )
Image_Function::SetPixel( output, blob->edgeX(), blob->edgeY(), 255 );
// Convert image into QImage format
UiWindowQt window4( output );
window4.show();
return app.exec();
}
catch( const std::exception & ex ) { // uh-oh, something went wrong!
std::cout << "Exception " << ex.what() << " raised. Closing the application..." << std::endl;
return 1;
}
catch( ... ) { // uh-oh, something terrible happen!
std::cout << "Generic exception raised. Closing the application..." << std::endl;
return 2;
}
}
Wow. you're quick. Would you mind to create a branch and push everything there? I could also easily review ;)
My code doesn't have point and line implementation :)
@yannlabou could you please add the code for point and line drawing and add a pull request? It should be not hard after all ;)
Otherwise I can do it myself but in 7-9 hours time only.
Otherwise I can do it myself but in 7-9 hours time only.
That's exactly what I meant. Go ahead :). I unfortunately won't have much time this week.
Sure. I'll create a pull request when it's done. Sorry that I made it faster than you :)
I'm working on Windows based UI as well.
@yannlabou could you please verify that QT_CORE_LIB macro exist for QT projects? I want to make a common code for UI but I need some extra data for this.
My idea is to hide platform/IDE specific code under macros so in ui.cpp file so we would have something like this:
#ifdef QT_CORE_LIB
// do something to setup QT Ui Window or include header file
#elseif WIN32
// do something to setup Windows Ui Window or include header file
#elseif MACOS
// do something to setup MacOS Ui Window or include header file
#elseif Linux
// do something to setup Linux Ui Window or include header file
#else
// do nothing
#endif
At another hand we might not need to merge the code as UI is not strictly speaking a part of the library. We could just add an example. What are your thoughts?
@yannlabou could you please verify that
QT_CORE_LIBmacro exist for QT projects? I want to make a common code for UI but I need some extra data for this.
QT_CORE_LIB, QT_GUI_LIB and so on are undocumented and likely to be removed or deprecated any time. I had suggest something like
find_package(Qt5Core)
find_package(Qt5Gui)
find_package(Qt5Widgets)
if(Qt5Core_FOUND AND Qt5Gui_FOUND AND Qt5Widgets_FOUND)
set(PENGUINV_HAS_QT5 ON)
endif()
Later in the lib code
#ifdef PENGUINV_HAS_QT5
// do something to setup QT Ui Window or include header file
#elseif WIN32
// do something to setup Windows Ui Window or include header file
#elseif MACOS
// do something to setup MacOS Ui Window or include header file
#elseif Linux
// do something to setup Linux Ui Window or include header file
#else
// do nothing
#endif
At another hand we might not need to merge the code as UI is not strictly speaking a part of the library. We could just add an example. What are your thoughts?
I strongly agree with you on this one. UI brings a lot of platform specific code that is hard to maintain. I had prefer to opt-in for Qt as it's platform independent and not merge the rest. Provide it as examples is a great idea.
Cool. I'll add an example for Windows little later. Probably as a separate patch. I'll create new issues to add more OS specific UI like for Linux, MacOS or Android (?). This issue would become a main issue holder for them. Next step would be to add a support of text, rectangles and circles (ovals).