vendor: OpenCV 5.0.0 snapshot at 755e50675d97db9b7d449d8bd6b09888646f6c6e
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
---
|
||||
AccessModifierOffset: -2
|
||||
ConstructorInitializerIndentWidth: 4
|
||||
AlignEscapedNewlinesLeft: false
|
||||
AlignTrailingComments: true
|
||||
AllowAllParametersOfDeclarationOnNextLine: true
|
||||
AllowShortIfStatementsOnASingleLine: false
|
||||
AllowShortLoopsOnASingleLine: false
|
||||
AlwaysBreakTemplateDeclarations: false
|
||||
AlwaysBreakBeforeMultilineStrings: false
|
||||
BreakBeforeBinaryOperators: false
|
||||
BreakBeforeTernaryOperators: true
|
||||
BreakConstructorInitializersBeforeComma: false
|
||||
BinPackParameters: true
|
||||
ColumnLimit: 80
|
||||
ConstructorInitializerAllOnOneLineOrOnePerLine: false
|
||||
DerivePointerBinding: false
|
||||
ExperimentalAutoDetectBinPacking: false
|
||||
IndentCaseLabels: false
|
||||
MaxEmptyLinesToKeep: 1
|
||||
NamespaceIndentation: None
|
||||
ObjCSpaceBeforeProtocolList: true
|
||||
PenaltyBreakBeforeFirstCallParameter: 19
|
||||
PenaltyBreakComment: 60
|
||||
PenaltyBreakString: 1000
|
||||
PenaltyBreakFirstLessLess: 120
|
||||
PenaltyExcessCharacter: 1000000
|
||||
PenaltyReturnTypeOnItsOwnLine: 60
|
||||
PointerBindsToType: false
|
||||
SpacesBeforeTrailingComments: 1
|
||||
Cpp11BracedListStyle: false
|
||||
Standard: Cpp11
|
||||
IndentWidth: 8
|
||||
TabWidth: 8
|
||||
UseTab: ForIndentation
|
||||
BreakBeforeBraces: Allman
|
||||
IndentFunctionDeclarationAfterType: false
|
||||
SpacesInParentheses: false
|
||||
SpacesInAngles: false
|
||||
SpaceInEmptyParentheses: false
|
||||
SpacesInCStyleCastParentheses: false
|
||||
SpaceAfterControlStatementKeyword: true
|
||||
SpaceBeforeAssignmentOperators: true
|
||||
ContinuationIndentWidth: 4
|
||||
...
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
build/
|
||||
CMakeLists.txt.user
|
||||
.ycm_extra_conf.py
|
||||
.ycm_extra_conf.pyc
|
||||
test.sh
|
||||
release.sh
|
||||
*.swp
|
||||
*.swo
|
||||
src/dbg/dbg.hpp
|
||||
*~
|
||||
@@ -0,0 +1,41 @@
|
||||
if(NOT HAVE_QT OR NOT HAVE_CXX11 OR QT_VERSION_MAJOR LESS 5)
|
||||
ocv_module_disable(cvv)
|
||||
return()
|
||||
endif()
|
||||
|
||||
set(the_description "Debug visualization framework")
|
||||
ocv_add_module(cvv opencv_core opencv_imgproc opencv_features WRAP python)
|
||||
ocv_warnings_disable(CMAKE_CXX_FLAGS -Wshadow -Wmissing-declarations)
|
||||
|
||||
# Qt
|
||||
set(CVV_QT_MODULES Core Gui Widgets)
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
list(APPEND CVV_QT_MODULES Core5Compat)
|
||||
endif()
|
||||
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS ${CVV_QT_MODULES})
|
||||
|
||||
set(CMAKE_INCLUDE_CURRENT_DIR ON)
|
||||
|
||||
# Automoc doesn't work properly with opencv_world build
|
||||
# Use QT<ver>_WRAP_CPP() directly instead
|
||||
file(GLOB_RECURSE CVV_MOC_HEADERS
|
||||
"${CMAKE_CURRENT_LIST_DIR}/src/*.hpp"
|
||||
"${CMAKE_CURRENT_LIST_DIR}/src/*.h"
|
||||
)
|
||||
if(QT_VERSION_MAJOR EQUAL 6)
|
||||
QT6_WRAP_CPP(_MOC_OUTFILES ${CVV_MOC_HEADERS})
|
||||
elseif(QT_VERSION_MAJOR EQUAL 5)
|
||||
QT5_WRAP_CPP(_MOC_OUTFILES ${CVV_MOC_HEADERS})
|
||||
endif()
|
||||
|
||||
foreach(module ${CVV_QT_MODULES})
|
||||
list(APPEND CVV_LIBRARIES ${Qt${QT_VERSION_MAJOR}${module}_LIBRARIES})
|
||||
endforeach()
|
||||
|
||||
ocv_module_include_directories()
|
||||
ocv_glob_module_sources(SOURCES ${_MOC_OUTFILES})
|
||||
ocv_create_module(${CVV_LIBRARIES})
|
||||
ocv_add_accuracy_tests()
|
||||
ocv_add_perf_tests()
|
||||
ocv_add_samples()
|
||||
@@ -0,0 +1,37 @@
|
||||
Copyright (c) 2013/2014 Johannes Bechberger
|
||||
Copyright (c) 2013/2014 Erich Bretnütz
|
||||
Copyright (c) 2013/2014 Nikolai Gaßner
|
||||
Copyright (c) 2013/2014 Raphael Grimm
|
||||
Copyright (c) 2013/2014 Clara Scherer
|
||||
Copyright (c) 2013/2014 Florian Weber
|
||||
Copyright (c) 2013/2014 Andreas Bihlmaier
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
* Neither the name CVVisual nor the names of its contributors may be used to
|
||||
endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
GUI for Interactive Visual Debugging of Computer Vision Programs
|
||||
================================================================
|
||||
|
||||
Simple code that you can add to your program that pops up a GUI allowing you to interactively and visually debug computer vision programs.
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef __OPENCV_CVV_HPP__
|
||||
#define __OPENCV_CVV_HPP__
|
||||
|
||||
/**
|
||||
@defgroup cvv GUI for Interactive Visual Debugging of Computer Vision Programs
|
||||
|
||||
Namespace for all functions is **cvv**, i.e. *cvv::showImage()*.
|
||||
|
||||
Compilation:
|
||||
|
||||
- For development, i.e. for cvv GUI to show up, compile your code using cvv with
|
||||
*g++ -DCVVISUAL_DEBUGMODE*.
|
||||
- For release, i.e. cvv calls doing nothing, compile your code without above flag.
|
||||
|
||||
See cvv tutorial for a commented example application using cvv.
|
||||
|
||||
*/
|
||||
|
||||
#include <opencv2/cvv/call_meta_data.hpp>
|
||||
#include <opencv2/cvv/debug_mode.hpp>
|
||||
#include <opencv2/cvv/dmatch.hpp>
|
||||
#include <opencv2/cvv/filter.hpp>
|
||||
#include <opencv2/cvv/final_show.hpp>
|
||||
#include <opencv2/cvv/show_image.hpp>
|
||||
|
||||
#endif //__OPENCV_CVV_HPP__
|
||||
@@ -0,0 +1,67 @@
|
||||
#ifndef CVVISUAL_CALL_DATA_HPP
|
||||
#define CVVISUAL_CALL_DATA_HPP
|
||||
|
||||
#include <string>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
//! @addtogroup cvv
|
||||
//! @{
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Optional information about a location in Code.
|
||||
*/
|
||||
struct CallMetaData
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates an unknown location.
|
||||
*/
|
||||
CallMetaData()
|
||||
: file(nullptr), line(0), function(nullptr), isKnown(false)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates the provided location.
|
||||
*
|
||||
* Argument should be self-explaining.
|
||||
*/
|
||||
CallMetaData(const char *file, size_t line, const char *function)
|
||||
: file(file), line(line), function(function), isKnown(true)
|
||||
{
|
||||
}
|
||||
operator bool()
|
||||
{
|
||||
return isKnown;
|
||||
}
|
||||
|
||||
// self-explaining:
|
||||
const char *file;
|
||||
const size_t line;
|
||||
const char *function;
|
||||
|
||||
/**
|
||||
* @brief Whether *this holds actual data.
|
||||
*/
|
||||
const bool isKnown;
|
||||
};
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespaces
|
||||
|
||||
/**
|
||||
* @brief Creates an instance of CallMetaData with the location of the macro as
|
||||
* value.
|
||||
*/
|
||||
#define CVVISUAL_LOCATION ::cvv::impl::CallMetaData(__FILE__, __LINE__, CV_Func)
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,5 @@
|
||||
#ifdef __OPENCV_BUILD
|
||||
#error this is a compatibility header which should not be used inside the OpenCV library
|
||||
#endif
|
||||
|
||||
#include "opencv2/cvv.hpp"
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef CVVISUAL_DEBUG_MODE_HPP
|
||||
#define CVVISUAL_DEBUG_MODE_HPP
|
||||
|
||||
#if __cplusplus >= 201103L && defined CVVISUAL_USE_THREAD_LOCAL
|
||||
#define CVVISUAL_THREAD_LOCAL thread_local
|
||||
#else
|
||||
#define CVVISUAL_THREAD_LOCAL
|
||||
#endif
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
//! @addtogroup cvv
|
||||
//! @{
|
||||
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* The debug-flag-singleton
|
||||
*/
|
||||
static inline bool &getDebugFlag()
|
||||
{
|
||||
CVVISUAL_THREAD_LOCAL static bool flag = true;
|
||||
return flag;
|
||||
}
|
||||
|
||||
} // namespace impl
|
||||
|
||||
/** @brief Returns whether debug-mode is active for this TU and thread.
|
||||
*/
|
||||
static inline bool debugMode()
|
||||
{
|
||||
return impl::getDebugFlag();
|
||||
}
|
||||
|
||||
/** @brief Enable or disable cvv for current translation unit and thread
|
||||
|
||||
(disabled this way has higher - but still low - overhead compared to using the compile flags).
|
||||
@param active
|
||||
*/
|
||||
static inline void setDebugFlag(bool active)
|
||||
{
|
||||
impl::getDebugFlag() = active;
|
||||
}
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace cvv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef CVVISUAL_DEBUG_DMATCH_HPP
|
||||
#define CVVISUAL_DEBUG_DMATCH_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "call_meta_data.hpp"
|
||||
#include "debug_mode.hpp"
|
||||
|
||||
#ifdef CV_DOXYGEN
|
||||
#define CVVISUAL_DEBUGMODE
|
||||
#endif
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
//! @addtogroup cvv
|
||||
//! @{
|
||||
|
||||
namespace impl
|
||||
{
|
||||
CV_EXPORTS void debugDMatch(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, const CallMetaData &data,
|
||||
const char *description, const char *view,
|
||||
bool useTrainDescriptor);
|
||||
} // namespace impl
|
||||
|
||||
#ifdef CVVISUAL_DEBUGMODE
|
||||
/** @brief Add a filled in DMatch \<dmatch\> to debug GUI.
|
||||
|
||||
The matches can are visualized for interactive inspection in different GUI views (one similar to an
|
||||
interactive :draw_matches:drawMatches\<\>).
|
||||
|
||||
@param img1 First image used in DMatch \<dmatch\>.
|
||||
@param keypoints1 Keypoints of first image.
|
||||
@param img2 Second image used in DMatch.
|
||||
@param keypoints2 Keypoints of second image.
|
||||
@param matches
|
||||
@param data See showImage
|
||||
@param description See showImage
|
||||
@param view See showImage
|
||||
@param useTrainDescriptor Use DMatch \<dmatch\>'s train descriptor index instead of query
|
||||
descriptor index.
|
||||
*/
|
||||
static inline void
|
||||
debugDMatch(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, const impl::CallMetaData &data,
|
||||
const char *description = nullptr, const char *view = nullptr,
|
||||
bool useTrainDescriptor = true)
|
||||
{
|
||||
if (debugMode())
|
||||
{
|
||||
impl::debugDMatch(img1, std::move(keypoints1), img2,
|
||||
std::move(keypoints2), std::move(matches),
|
||||
data, description, view, useTrainDescriptor);
|
||||
}
|
||||
}
|
||||
/** @overload */
|
||||
static inline void
|
||||
debugDMatch(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, const impl::CallMetaData &data,
|
||||
const std::string &description, const std::string &view,
|
||||
bool useTrainDescriptor = true)
|
||||
{
|
||||
if (debugMode())
|
||||
{
|
||||
impl::debugDMatch(img1, std::move(keypoints1), img2,
|
||||
std::move(keypoints2), std::move(matches),
|
||||
data, description.c_str(), view.c_str(),
|
||||
useTrainDescriptor);
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void debugDMatch(cv::InputArray, std::vector<cv::KeyPoint>,
|
||||
cv::InputArray, std::vector<cv::KeyPoint>,
|
||||
std::vector<cv::DMatch>,
|
||||
const impl::CallMetaData &,
|
||||
const char * = nullptr, const char * = nullptr,
|
||||
bool = true)
|
||||
{
|
||||
}
|
||||
static inline void debugDMatch(cv::InputArray, std::vector<cv::KeyPoint>,
|
||||
cv::InputArray, std::vector<cv::KeyPoint>,
|
||||
std::vector<cv::DMatch>,
|
||||
const impl::CallMetaData &, const std::string &,
|
||||
const std::string &, bool = true)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace cvv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,76 @@
|
||||
#ifndef CVVISUAL_DEBUG_FILTER_HPP
|
||||
#define CVVISUAL_DEBUG_FILTER_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include "call_meta_data.hpp"
|
||||
#include "debug_mode.hpp"
|
||||
|
||||
#ifdef CV_DOXYGEN
|
||||
#define CVVISUAL_DEBUGMODE
|
||||
#endif
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
//! @addtogroup cvv
|
||||
//! @{
|
||||
|
||||
namespace impl
|
||||
{
|
||||
// implementation outside API
|
||||
CV_EXPORTS void debugFilter(cv::InputArray original, cv::InputArray result,
|
||||
const CallMetaData &data, const char *description,
|
||||
const char *view);
|
||||
} // namespace impl
|
||||
|
||||
#ifdef CVVISUAL_DEBUGMODE
|
||||
/**
|
||||
* @brief Use the debug-framework to compare two images (from which the second
|
||||
* is intended to be the result of
|
||||
* a filter applied to the first).
|
||||
*/
|
||||
static inline void
|
||||
debugFilter(cv::InputArray original, cv::InputArray result,
|
||||
impl::CallMetaData metaData = impl::CallMetaData(),
|
||||
const char *description = nullptr, const char *view = nullptr)
|
||||
{
|
||||
if (debugMode())
|
||||
{
|
||||
impl::debugFilter(original, result, metaData, description,
|
||||
view);
|
||||
}
|
||||
}
|
||||
/** @overload */
|
||||
static inline void debugFilter(cv::InputArray original, cv::InputArray result,
|
||||
impl::CallMetaData metaData,
|
||||
const ::std::string &description,
|
||||
const ::std::string &view = "")
|
||||
{
|
||||
if (debugMode())
|
||||
{
|
||||
impl::debugFilter(original, result, metaData,
|
||||
description.c_str(), view.c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void debugFilter(cv::InputArray, cv::InputArray,
|
||||
impl::CallMetaData = impl::CallMetaData(),
|
||||
const char * = nullptr, const char * = nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
static inline void debugFilter(cv::InputArray, cv::InputArray,
|
||||
impl::CallMetaData, const ::std::string &,
|
||||
const ::std::string &)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace cvv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,52 @@
|
||||
#ifndef CVVISUAL_FINAL_SHOW_HPP
|
||||
#define CVVISUAL_FINAL_SHOW_HPP
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "debug_mode.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
//! @addtogroup cvv
|
||||
//! @{
|
||||
|
||||
namespace impl
|
||||
{
|
||||
CV_EXPORTS void finalShow();
|
||||
}
|
||||
|
||||
/** @brief Passes the control to the debug-window for a last time.
|
||||
|
||||
This function **must** be called *once* *after* all cvv calls if any. As an alternative create an
|
||||
instance of FinalShowCaller, which calls finalShow() in its destructor (RAII-style).
|
||||
*/
|
||||
inline void finalShow()
|
||||
{
|
||||
#ifdef CVVISUAL_DEBUGMODE
|
||||
if (debugMode())
|
||||
{
|
||||
impl::finalShow();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief RAII-class to call finalShow() in it's dtor.
|
||||
*/
|
||||
class FinalShowCaller
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Calls finalShow().
|
||||
*/
|
||||
~FinalShowCaller()
|
||||
{
|
||||
finalShow();
|
||||
}
|
||||
};
|
||||
|
||||
//! @}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
#ifndef CVVISUAL_DEBUG_SHOW_IMAGE_HPP
|
||||
#define CVVISUAL_DEBUG_SHOW_IMAGE_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include "call_meta_data.hpp"
|
||||
#include "debug_mode.hpp"
|
||||
|
||||
#ifdef CV_DOXYGEN
|
||||
#define CVVISUAL_DEBUGMODE
|
||||
#endif
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
//! @addtogroup cvv
|
||||
//! @{
|
||||
|
||||
namespace impl
|
||||
{
|
||||
// implementation outside API
|
||||
CV_EXPORTS void showImage(cv::InputArray img, const CallMetaData &data,
|
||||
const char *description, const char *view);
|
||||
} // namespace impl
|
||||
|
||||
#ifdef CVVISUAL_DEBUGMODE
|
||||
/** @brief Add a single image to debug GUI (similar to imshow \<\>).
|
||||
|
||||
@param img Image to show in debug GUI.
|
||||
@param metaData Properly initialized CallMetaData struct, i.e. information about file, line and
|
||||
function name for GUI. Use CVVISUAL_LOCATION macro.
|
||||
@param description Human readable description to provide context to image.
|
||||
@param view Preselect view that will be used to visualize this image in GUI. Other views can still
|
||||
be selected in GUI later on.
|
||||
*/
|
||||
static inline void showImage(cv::InputArray img,
|
||||
impl::CallMetaData metaData = impl::CallMetaData(),
|
||||
const char *description = nullptr,
|
||||
const char *view = nullptr)
|
||||
{
|
||||
if (debugMode())
|
||||
{
|
||||
impl::showImage(img, metaData, description, view);
|
||||
}
|
||||
}
|
||||
/** @overload */
|
||||
static inline void showImage(cv::InputArray img, impl::CallMetaData metaData,
|
||||
const ::std::string &description,
|
||||
const ::std::string &view = "")
|
||||
{
|
||||
if (debugMode())
|
||||
{
|
||||
impl::showImage(img, metaData, description.c_str(),
|
||||
view.c_str());
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void showImage(cv::InputArray,
|
||||
impl::CallMetaData = impl::CallMetaData(),
|
||||
const char * = nullptr, const char * = nullptr)
|
||||
{
|
||||
}
|
||||
static inline void showImage(cv::InputArray, impl::CallMetaData,
|
||||
const ::std::string &, const ::std::string &)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
//! @}
|
||||
|
||||
} // namespace cvv
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,119 @@
|
||||
// system includes
|
||||
#include <iostream>
|
||||
|
||||
// library includes
|
||||
#include <opencv2/imgproc.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
#include <opencv2/videoio.hpp>
|
||||
|
||||
#define CVVISUAL_DEBUGMODE
|
||||
#include <opencv2/cvv/debug_mode.hpp>
|
||||
#include <opencv2/cvv/show_image.hpp>
|
||||
#include <opencv2/cvv/filter.hpp>
|
||||
#include <opencv2/cvv/dmatch.hpp>
|
||||
#include <opencv2/cvv/final_show.hpp>
|
||||
|
||||
using namespace std;
|
||||
using namespace cv;
|
||||
|
||||
template<class T> std::string toString(const T& p_arg)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
ss << p_arg;
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
// parser keys
|
||||
const char *keys =
|
||||
"{ help h usage ? | | show this message }"
|
||||
"{ width W | 0| camera resolution width. leave at 0 to use defaults }"
|
||||
"{ height H | 0| camera resolution height. leave at 0 to use defaults }";
|
||||
|
||||
CommandLineParser parser(argc, argv, keys);
|
||||
if (parser.has("help")) {
|
||||
parser.printMessage();
|
||||
return 0;
|
||||
}
|
||||
int res_w = parser.get<int>("width");
|
||||
int res_h = parser.get<int>("height");
|
||||
|
||||
// setup video capture
|
||||
cv::VideoCapture capture(0);
|
||||
if (!capture.isOpened()) {
|
||||
std::cout << "Could not open VideoCapture" << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (res_w>0 && res_h>0) {
|
||||
printf("Setting resolution to %dx%d\n", res_w, res_h);
|
||||
capture.set(cv::CAP_PROP_FRAME_WIDTH, res_w);
|
||||
capture.set(cv::CAP_PROP_FRAME_HEIGHT, res_h);
|
||||
}
|
||||
|
||||
|
||||
cv::Mat prevImgGray;
|
||||
std::vector<cv::KeyPoint> prevKeypoints;
|
||||
cv::Mat prevDescriptors;
|
||||
|
||||
int maxFeatureCount = 500;
|
||||
Ptr<ORB> detector = ORB::create(maxFeatureCount);
|
||||
|
||||
cv::BFMatcher matcher(cv::NORM_HAMMING);
|
||||
|
||||
for (int imgId = 0; imgId < 10; imgId++) {
|
||||
// capture a frame
|
||||
cv::Mat imgRead;
|
||||
capture >> imgRead;
|
||||
printf("%d: image captured\n", imgId);
|
||||
|
||||
std::string imgIdString{"imgRead"};
|
||||
imgIdString += toString(imgId);
|
||||
cvv::showImage(imgRead, CVVISUAL_LOCATION, imgIdString.c_str());
|
||||
|
||||
// convert to grayscale
|
||||
cv::Mat imgGray;
|
||||
cv::cvtColor(imgRead, imgGray, COLOR_BGR2GRAY);
|
||||
cvv::debugFilter(imgRead, imgGray, CVVISUAL_LOCATION, "to gray");
|
||||
|
||||
// detect ORB features
|
||||
std::vector<cv::KeyPoint> keypoints;
|
||||
cv::Mat descriptors;
|
||||
detector->detectAndCompute(imgGray, cv::noArray(), keypoints, descriptors);
|
||||
printf("%d: detected %zd keypoints\n", imgId, keypoints.size());
|
||||
|
||||
// match them to previous image (if available)
|
||||
if (!prevImgGray.empty()) {
|
||||
std::vector<cv::DMatch> matches;
|
||||
matcher.match(prevDescriptors, descriptors, matches);
|
||||
printf("%d: all matches size=%zd\n", imgId, matches.size());
|
||||
std::string allMatchIdString{"all matches "};
|
||||
allMatchIdString += toString(imgId-1) + "<->" + toString(imgId);
|
||||
cvv::debugDMatch(prevImgGray, prevKeypoints, imgGray, keypoints, matches, CVVISUAL_LOCATION, allMatchIdString.c_str());
|
||||
|
||||
// remove worst (as defined by match distance) bestRatio quantile
|
||||
double bestRatio = 0.8;
|
||||
std::sort(matches.begin(), matches.end());
|
||||
matches.resize(int(bestRatio * matches.size()));
|
||||
printf("%d: best matches size=%zd\n", imgId, matches.size());
|
||||
std::string bestMatchIdString{"best " + toString(bestRatio) + " matches "};
|
||||
bestMatchIdString += toString(imgId-1) + "<->" + toString(imgId);
|
||||
cvv::debugDMatch(prevImgGray, prevKeypoints, imgGray, keypoints, matches, CVVISUAL_LOCATION, bestMatchIdString.c_str());
|
||||
}
|
||||
|
||||
prevImgGray = imgGray;
|
||||
prevKeypoints = keypoints;
|
||||
prevDescriptors = descriptors;
|
||||
}
|
||||
|
||||
cvv::finalShow();
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,384 @@
|
||||
#include "view_controller.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <iostream>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QDesktopServices>
|
||||
#include <QUrl>
|
||||
|
||||
#include "../gui/call_tab.hpp"
|
||||
#include "../gui/call_window.hpp"
|
||||
#include "../gui/overview_panel.hpp"
|
||||
#include "../gui/main_call_window.hpp"
|
||||
#include "../gui/filter_call_tab.hpp"
|
||||
#include "../gui/match_call_tab.hpp"
|
||||
#include "../gui/image_call_tab.hpp"
|
||||
#include "../impl/init.hpp"
|
||||
#include "../impl/filter_call.hpp"
|
||||
#include "../impl/match_call.hpp"
|
||||
#include "../impl/single_image_call.hpp"
|
||||
#include "../impl/data_controller.hpp"
|
||||
#include "../qtutil/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace controller
|
||||
{
|
||||
|
||||
// It's only used for instatiating a QApplication.
|
||||
// static char *emptyArray[] = {""};
|
||||
static char *parameterSystemV[] = { new char[1]{ 0 }, nullptr };
|
||||
static int parameterSystemC = 1;
|
||||
|
||||
ViewController::ViewController()
|
||||
{
|
||||
impl::initializeFilterAndViews();
|
||||
if (!QApplication::instance())
|
||||
{
|
||||
auto tmp =
|
||||
new QApplication{ parameterSystemC, parameterSystemV };
|
||||
ownsQApplication = true;
|
||||
(void)tmp;
|
||||
}
|
||||
ovPanel = new gui::OverviewPanel{ util::makeRef(*this) };
|
||||
mainWindow = new gui::MainCallWindow(util::makeRef(*this), 0, ovPanel);
|
||||
windowMap[0] = std::unique_ptr<gui::CallWindow>(mainWindow);
|
||||
max_window_id = 0;
|
||||
mainWindow->show();
|
||||
}
|
||||
|
||||
ViewController::~ViewController()
|
||||
{
|
||||
callTabMap.clear();
|
||||
windowMap.clear();
|
||||
windowMap.clear();
|
||||
if (ownsQApplication)
|
||||
{
|
||||
delete QApplication::instance();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewController::addCallType(const QString typeName, TabFactory constr)
|
||||
{
|
||||
ViewController::callTabType[typeName] = constr;
|
||||
}
|
||||
|
||||
static std::unique_ptr<cvv::gui::FilterCallTab>
|
||||
makeFilterCallTab(cvv::util::Reference<cvv::impl::Call> call)
|
||||
{
|
||||
return cvv::util::make_unique<cvv::gui::FilterCallTab>(
|
||||
*call.castTo<cvv::impl::FilterCall>());
|
||||
}
|
||||
|
||||
static std::unique_ptr<cvv::gui::MatchCallTab>
|
||||
makeMatchCallTab(cvv::util::Reference<cvv::impl::Call> call)
|
||||
{
|
||||
return cvv::util::make_unique<cvv::gui::MatchCallTab>(
|
||||
*call.castTo<cvv::impl::MatchCall>());
|
||||
}
|
||||
|
||||
static std::unique_ptr<cvv::gui::ImageCallTab>
|
||||
makeImageCallTab(cvv::util::Reference<cvv::impl::Call> call)
|
||||
{
|
||||
return cvv::util::make_unique<cvv::gui::ImageCallTab>(
|
||||
*call.castTo<cvv::impl::SingleImageCall>());
|
||||
}
|
||||
|
||||
std::map<QString, TabFactory> ViewController::callTabType{
|
||||
{ "filter", makeFilterCallTab }, { "match", makeMatchCallTab },
|
||||
{ "singleImage", makeImageCallTab }
|
||||
};
|
||||
|
||||
void ViewController::addCall(util::Reference<impl::Call> data)
|
||||
{
|
||||
updateMode();
|
||||
if (mode == Mode::NORMAL)
|
||||
{
|
||||
ovPanel->addElement(*data);
|
||||
mainWindow->showOverviewTab();
|
||||
}
|
||||
else if (mode == Mode::FAST_FORWARD)
|
||||
{
|
||||
ovPanel->addElementBuffered(*data);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewController::exec()
|
||||
{
|
||||
updateMode();
|
||||
if (mode == Mode::NORMAL)
|
||||
{
|
||||
QApplication::instance()->exec();
|
||||
}
|
||||
}
|
||||
|
||||
impl::Call &ViewController::getCall(size_t id)
|
||||
{
|
||||
return impl::dataController().getCall(id);
|
||||
}
|
||||
|
||||
QString ViewController::getSetting(const QString &scope, const QString &key)
|
||||
{
|
||||
return qtutil::getSetting(scope, key);
|
||||
}
|
||||
|
||||
std::vector<util::Reference<gui::CallWindow>> ViewController::getTabWindows()
|
||||
{
|
||||
std::vector<util::Reference<gui::CallWindow>> windows{};
|
||||
for (auto &it : windowMap)
|
||||
{
|
||||
windows.push_back(util::makeRef(*(it.second)));
|
||||
}
|
||||
return windows;
|
||||
}
|
||||
|
||||
util::Reference<gui::MainCallWindow> ViewController::getMainWindow()
|
||||
{
|
||||
return util::makeRef(*mainWindow);
|
||||
}
|
||||
|
||||
void ViewController::moveCallTabToNewWindow(size_t tabId)
|
||||
{
|
||||
if (!hasCall(tabId))
|
||||
return;
|
||||
auto newWindow = util::make_unique<gui::CallWindow>(
|
||||
util::makeRef<ViewController>(*this), ++max_window_id);
|
||||
removeCallTab(tabId);
|
||||
newWindow->addTab(getCallTab(tabId));
|
||||
newWindow->show();
|
||||
if (doesShowExitProgramButton)
|
||||
{
|
||||
newWindow->showExitProgramButton();
|
||||
}
|
||||
windowMap[max_window_id] = std::move(newWindow);
|
||||
removeEmptyWindowsWithDelay();
|
||||
}
|
||||
|
||||
void ViewController::moveCallTabToWindow(size_t tabId, size_t windowId)
|
||||
{
|
||||
if (!hasCall(tabId))
|
||||
return;
|
||||
removeCallTab(tabId);
|
||||
auto tab = getCallTab(tabId);
|
||||
windowMap[windowId]->addTab(tab);
|
||||
removeEmptyWindowsWithDelay();
|
||||
}
|
||||
|
||||
void ViewController::removeCallTab(size_t tabId, bool deleteIt, bool deleteCall, bool updateUI)
|
||||
{
|
||||
auto *curWindow = getCurrentWindowOfTab(tabId);
|
||||
if (curWindow->hasTab(tabId))
|
||||
{
|
||||
getCurrentWindowOfTab(tabId)->removeTab(tabId);
|
||||
if (deleteIt)
|
||||
{
|
||||
callTabMap.erase(tabId);
|
||||
}
|
||||
}
|
||||
if (deleteCall && hasCall(tabId))
|
||||
{
|
||||
if (updateUI)
|
||||
{
|
||||
ovPanel->removeElement(tabId);
|
||||
}
|
||||
impl::dataController().removeCall(tabId);
|
||||
}
|
||||
removeEmptyWindowsWithDelay();
|
||||
}
|
||||
|
||||
void ViewController::openHelpBrowser(const QString &topic)
|
||||
{
|
||||
qtutil::openHelpBrowser(topic);
|
||||
}
|
||||
|
||||
void ViewController::resumeProgramExecution()
|
||||
{
|
||||
QApplication::instance()->exit();
|
||||
}
|
||||
|
||||
void ViewController::setDefaultSetting(const QString &scope, const QString &key,
|
||||
const QString &value)
|
||||
{
|
||||
qtutil::setDefaultSetting(scope, key, value);
|
||||
}
|
||||
|
||||
void ViewController::setSetting(const QString &scope, const QString &key,
|
||||
const QString &value)
|
||||
{
|
||||
qtutil::setSetting(scope, key, value);
|
||||
}
|
||||
|
||||
void ViewController::showCallTab(size_t tabId)
|
||||
{
|
||||
auto *window = getCurrentWindowOfTab(tabId);
|
||||
window->showTab(tabId);
|
||||
window->setWindowState((window->windowState() & ~Qt::WindowMinimized) |
|
||||
Qt::WindowActive);
|
||||
window->raise();
|
||||
}
|
||||
|
||||
void ViewController::showAndOpenCallTab(size_t tabId)
|
||||
{
|
||||
auto curWindow = getCurrentWindowOfTab(tabId);
|
||||
if (!curWindow->hasTab(tabId))
|
||||
{
|
||||
moveCallTabToWindow(tabId, 0);
|
||||
curWindow = mainWindow;
|
||||
}
|
||||
curWindow->showTab(tabId);
|
||||
}
|
||||
|
||||
void ViewController::openCallTab(size_t tabId)
|
||||
{
|
||||
auto curWindow = getCurrentWindowOfTab(tabId);
|
||||
if (!curWindow->hasTab(tabId))
|
||||
{
|
||||
moveCallTabToWindow(tabId, 0);
|
||||
curWindow = mainWindow;
|
||||
}
|
||||
}
|
||||
|
||||
void ViewController::showOverview()
|
||||
{
|
||||
mainWindow->setWindowState(
|
||||
(mainWindow->windowState() & ~Qt::WindowMinimized) |
|
||||
Qt::WindowActive);
|
||||
mainWindow->raise();
|
||||
mainWindow->showOverviewTab();
|
||||
}
|
||||
|
||||
gui::CallWindow *ViewController::getCurrentWindowOfTab(size_t tabId)
|
||||
{
|
||||
for (auto &elem : windowMap)
|
||||
{
|
||||
if (elem.second->hasTab(tabId))
|
||||
{
|
||||
return elem.second.get();
|
||||
}
|
||||
}
|
||||
return mainWindow;
|
||||
}
|
||||
|
||||
gui::CallTab *ViewController::getCallTab(size_t tabId)
|
||||
{
|
||||
if (callTabMap.count(tabId) == 0)
|
||||
{
|
||||
auto *call = &(getCall(tabId));
|
||||
if (callTabType.count(call->type()) == 0)
|
||||
{
|
||||
throw std::invalid_argument{
|
||||
"no such type '" + call->type().toStdString() +
|
||||
"'"
|
||||
};
|
||||
}
|
||||
callTabMap[tabId] =
|
||||
callTabType[call->type()](util::makeRef(*call));
|
||||
}
|
||||
return callTabMap[tabId].get();
|
||||
}
|
||||
|
||||
void ViewController::removeWindowFromMaps(size_t windowId)
|
||||
{
|
||||
if (windowMap.count(windowId) > 0)
|
||||
{
|
||||
windowMap[windowId].release();
|
||||
windowMap.erase(windowId);
|
||||
}
|
||||
}
|
||||
|
||||
void ViewController::removeEmptyWindows()
|
||||
{
|
||||
std::vector<size_t> remIds{};
|
||||
for (auto &elem : windowMap)
|
||||
{
|
||||
if (elem.second->tabCount() == 0 && elem.second->getId() != 0)
|
||||
{
|
||||
remIds.push_back(elem.first);
|
||||
}
|
||||
}
|
||||
for (auto windowId : remIds)
|
||||
{
|
||||
auto window = windowMap[windowId].release();
|
||||
windowMap.erase(windowId);
|
||||
window->deleteLater();
|
||||
}
|
||||
shouldRunRemoveEmptyWindows_ = false;
|
||||
}
|
||||
|
||||
void ViewController::removeEmptyWindowsWithDelay()
|
||||
{
|
||||
shouldRunRemoveEmptyWindows_ = true;
|
||||
}
|
||||
|
||||
bool ViewController::shouldRunRemoveEmptyWindows()
|
||||
{
|
||||
return shouldRunRemoveEmptyWindows_;
|
||||
}
|
||||
|
||||
void ViewController::showExitProgramButton()
|
||||
{
|
||||
for (auto &elem : windowMap)
|
||||
{
|
||||
elem.second->showExitProgramButton();
|
||||
}
|
||||
doesShowExitProgramButton = true;
|
||||
}
|
||||
|
||||
bool ViewController::hasCall(size_t id)
|
||||
{
|
||||
return impl::dataController().hasCall(id);
|
||||
}
|
||||
|
||||
void ViewController::setMode(Mode newMode)
|
||||
{
|
||||
mode = newMode;
|
||||
switch (newMode)
|
||||
{
|
||||
case Mode::NORMAL:
|
||||
break;
|
||||
case Mode::HIDE:
|
||||
hideAll();
|
||||
QApplication::instance()->exit();
|
||||
break;
|
||||
case Mode::FAST_FORWARD:
|
||||
if (!doesShowExitProgramButton)
|
||||
{
|
||||
QApplication::instance()->exit();
|
||||
}
|
||||
else
|
||||
{
|
||||
mode = Mode::NORMAL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Mode ViewController::getMode()
|
||||
{
|
||||
return mode;
|
||||
}
|
||||
|
||||
void ViewController::updateMode()
|
||||
{
|
||||
if (mode == Mode::FAST_FORWARD && hasFinalCall())
|
||||
{
|
||||
mode = Mode::NORMAL;
|
||||
ovPanel->flushElementBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
void ViewController::hideAll()
|
||||
{
|
||||
for (auto &window : windowMap)
|
||||
{
|
||||
window.second->hide();
|
||||
}
|
||||
}
|
||||
|
||||
bool ViewController::hasFinalCall()
|
||||
{
|
||||
return doesShowExitProgramButton;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
#ifndef CVVISUAL_VIEWCONTROLLER_HPP
|
||||
#define CVVISUAL_VIEWCONTROLLER_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <QString>
|
||||
|
||||
#include "../util/util.hpp"
|
||||
#include "../impl/call.hpp"
|
||||
#include "../gui/call_window.hpp"
|
||||
#include "../gui/call_tab.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
namespace gui
|
||||
{
|
||||
class CallTab;
|
||||
class CallWindow;
|
||||
class MainCallWindow;
|
||||
class OverviewPanel;
|
||||
}
|
||||
|
||||
namespace controller
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Modes that this cvv application can be running in.
|
||||
*/
|
||||
enum class Mode
|
||||
{
|
||||
/**
|
||||
* @brief The normal mode.
|
||||
*/
|
||||
NORMAL = 0,
|
||||
/**
|
||||
* @brief The cvv UI is hidden.
|
||||
*/
|
||||
HIDE = 1,
|
||||
/**
|
||||
* @brief The cvv UI stops only at the final call
|
||||
* The final call is the call which is called after `cvv::finalShow()`)
|
||||
*/
|
||||
FAST_FORWARD = 2
|
||||
};
|
||||
|
||||
class ViewController;
|
||||
|
||||
/**
|
||||
* @brief Typedef for a function that creates a CallTab from a impl::Call.
|
||||
*/
|
||||
using TabFactory =
|
||||
std::function<std::unique_ptr<gui::CallTab>(util::Reference<impl::Call>)>;
|
||||
|
||||
/**
|
||||
* @brief Controlls the windows, call tabs and the event fetch loop.
|
||||
* Its the layer between the low level model (aka DataController) an the high
|
||||
* level GUI (aka CallTab, OverviewPanel, ...).
|
||||
*/
|
||||
class ViewController
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The default contructor for this class.
|
||||
*/
|
||||
ViewController();
|
||||
|
||||
/**
|
||||
* @brief Clean up.
|
||||
*/
|
||||
~ViewController();
|
||||
|
||||
/**
|
||||
* @brief Adds the new call tab type.
|
||||
* @param typeName name of the new type
|
||||
* @param constr function constructing an instance of this call tab
|
||||
* type
|
||||
* @return an instance of the new call tab type
|
||||
*/
|
||||
static void addCallType(const QString typeName, TabFactory constr);
|
||||
|
||||
/**
|
||||
* @brief Adds a new call and shows it in the overview table.
|
||||
* @param data new call (data)
|
||||
*/
|
||||
void addCall(util::Reference<impl::Call> data);
|
||||
|
||||
/**
|
||||
* @brief Execute the Qt event loop.
|
||||
*/
|
||||
void exec();
|
||||
|
||||
/**
|
||||
* @brief Get the call with the given id.
|
||||
* @param id given id
|
||||
* @return call with the given id
|
||||
*/
|
||||
impl::Call &getCall(size_t id);
|
||||
|
||||
/**
|
||||
* @brief Get the current setting [key] in the given scope.
|
||||
* Please use `setDefaultSetting` to set a default value that's other
|
||||
* than
|
||||
* an empty QString.
|
||||
* @param scope given scope (e.g. 'Overview')
|
||||
* @param key settings key (e.g. 'autoOpenTabs')
|
||||
* @return settings string
|
||||
*/
|
||||
QString getSetting(const QString &scope, const QString &key);
|
||||
|
||||
/**
|
||||
* @brief Get the inherited call windows with tabs.
|
||||
* @return the inherited CallWindows
|
||||
*/
|
||||
std::vector<util::Reference<gui::CallWindow>> getTabWindows();
|
||||
|
||||
/**
|
||||
* @brief Get the inherited main window.
|
||||
* @return the inherited main window
|
||||
*/
|
||||
util::Reference<gui::MainCallWindow> getMainWindow();
|
||||
|
||||
/**
|
||||
* @brief Move the call tab with the given id to a new window.
|
||||
* @param tabId given call tab id
|
||||
*/
|
||||
void moveCallTabToNewWindow(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Move the given call tab to the given window.
|
||||
* @param tabId id of the given call tab
|
||||
* @param windowId id of the given window (0 is the main window)
|
||||
*/
|
||||
void moveCallTabToWindow(size_t tabId, size_t windowId);
|
||||
|
||||
/**
|
||||
* @brief Removes the call tab with the given id.
|
||||
* @param tabId given id
|
||||
* @param deleteCall if deleteCall and deleteIt are true, it also
|
||||
* deletes the proper Call
|
||||
*/
|
||||
void removeCallTab(size_t tabId, bool deleteIt = true,
|
||||
bool deleteCall = false, bool updateUI = true);
|
||||
|
||||
/**
|
||||
* @brief Opens the users default browser with the topic help page.
|
||||
* Current URL: cvv.mostlynerdless.de/help.php?topic=[topic]
|
||||
*
|
||||
* Topics can be added via appending the doc/topics.yml file.
|
||||
*
|
||||
* @param topic help topic
|
||||
*/
|
||||
void openHelpBrowser(const QString &topic);
|
||||
|
||||
/**
|
||||
* @brief Resume the execution of the calling program.
|
||||
*/
|
||||
void resumeProgramExecution();
|
||||
|
||||
/**
|
||||
* @brief Set the default setting for a given stettings key and scope.
|
||||
* It doesn't override existing settings.
|
||||
* @param scope given settings scope
|
||||
* @param key given settings key
|
||||
* @param value default value of the setting
|
||||
*/
|
||||
void setDefaultSetting(const QString &scope, const QString &key,
|
||||
const QString &value);
|
||||
|
||||
/**
|
||||
* @brief Set the setting for a given stettings key and scope.
|
||||
* @param scope given settings scope
|
||||
* @param key given settings key
|
||||
* @param value new value of the setting
|
||||
*/
|
||||
void setSetting(const QString &scope, const QString &key,
|
||||
const QString &value);
|
||||
|
||||
/**
|
||||
* @brief Show the given call tab and bring it's window to the front.
|
||||
* @note It's not guaranteed that it really brings the tabs' window to the front.
|
||||
* @param tabId id of the given call tab
|
||||
*/
|
||||
void showCallTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Shows the tab and opens it if necessary.
|
||||
* @param tabId id of the tab
|
||||
*/
|
||||
void showAndOpenCallTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Opens the tab if necessary.
|
||||
* @param tabId id of the tab
|
||||
*/
|
||||
void openCallTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Show the overview tab (and table) and bring it's window to the
|
||||
* front.
|
||||
* @note The latter is not guaranteed.
|
||||
*/
|
||||
void showOverview();
|
||||
|
||||
/**
|
||||
* @brief Get the window in which the given tab lays currently.
|
||||
* @param tabId id of the given call tab
|
||||
* @return current window
|
||||
*/
|
||||
gui::CallWindow *getCurrentWindowOfTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Returns the call tab with the given id and constructs it if
|
||||
* doesn't exit.
|
||||
* @param tabId given id
|
||||
* @return call tab with given id
|
||||
*/
|
||||
gui::CallTab *getCallTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Remove the window from the internal data structures.
|
||||
* @param windowId id of the window
|
||||
* @note Only call this method if you now the implacations of deleting
|
||||
* the window.
|
||||
*/
|
||||
void removeWindowFromMaps(size_t windowId);
|
||||
|
||||
/**
|
||||
* @brief Shows an "Exit program" button on each window.
|
||||
*/
|
||||
void showExitProgramButton();
|
||||
|
||||
/**
|
||||
* @brief Removes the empty windows.
|
||||
* @note It's safer to call the removeEmptyWindowsWithDelay method
|
||||
* instead.
|
||||
*/
|
||||
void removeEmptyWindows();
|
||||
|
||||
/**
|
||||
* @brief Removes the empty windows with a small delay.
|
||||
*/
|
||||
void removeEmptyWindowsWithDelay();
|
||||
|
||||
/**
|
||||
* @brief Checks whether or not is useful to call the
|
||||
* removeEmptyWindows() method.
|
||||
* @return Is is useful to call the removeEmptyWindows() method?
|
||||
* @note Please don't call this method outside a periodcally called
|
||||
* method.
|
||||
*/
|
||||
bool shouldRunRemoveEmptyWindows();
|
||||
|
||||
/**
|
||||
* @brief Set the mode that this application is running in.
|
||||
* @param newMode mode to be set
|
||||
*/
|
||||
void setMode(Mode newMode);
|
||||
|
||||
/**
|
||||
* @brief Returns the mode this program is running in.
|
||||
* @return the current mode, NROMAL, HIDE or FAST_FORWARD
|
||||
*/
|
||||
Mode getMode();
|
||||
|
||||
/**
|
||||
* @brief Checks whether or not the `cvv::finalCall()` method has been
|
||||
* called?
|
||||
* @return Has the `cvv::finalCall()` method been called?
|
||||
*/
|
||||
bool hasFinalCall();
|
||||
|
||||
private:
|
||||
static std::map<QString, TabFactory> callTabType;
|
||||
|
||||
std::map<size_t, std::unique_ptr<gui::CallWindow>> windowMap{};
|
||||
gui::MainCallWindow *mainWindow;
|
||||
|
||||
std::map<size_t, std::unique_ptr<gui::CallTab>> callTabMap{};
|
||||
gui::OverviewPanel *ovPanel;
|
||||
bool doesShowExitProgramButton = false;
|
||||
/**
|
||||
* @brief Counter == 0 <=> you should run `removeEmptyWindows()`.
|
||||
*/
|
||||
bool shouldRunRemoveEmptyWindows_ = true;
|
||||
|
||||
Mode mode = Mode::NORMAL;
|
||||
|
||||
bool ownsQApplication = false;
|
||||
|
||||
size_t max_window_id = 0;
|
||||
|
||||
bool hasCall(size_t id);
|
||||
|
||||
void updateMode();
|
||||
|
||||
void hideAll();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "api.hpp"
|
||||
|
||||
#include "../gui/filter_call_tab.hpp"
|
||||
#include "../gui/match_call_tab.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace extend
|
||||
{
|
||||
|
||||
void addCallType(const QString name, TabFactory factory)
|
||||
{
|
||||
controller::ViewController::addCallType(name, factory);
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::extend
|
||||
@@ -0,0 +1,63 @@
|
||||
#ifndef CVVISUAL_EXTENSION_API_HPP
|
||||
#define CVVISUAL_EXTENSION_API_HPP
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "../impl/call.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "../view/filter_view.hpp"
|
||||
#include "../gui/match_call_tab.hpp"
|
||||
#include "../gui/filter_call_tab.hpp"
|
||||
#include "../qtutil/filterselectorwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace extend
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Introduces a new filter-view.
|
||||
* @param name of the new FilterView.
|
||||
* @tparam FView A FilterView. Needs to have a constructor of the form
|
||||
* FView(const cvv::impl::FilterCall&, QWidget*).
|
||||
*/
|
||||
template <class FView> void addFilterView(const QString name)
|
||||
{
|
||||
cvv::gui::FilterCallTab::registerFilterView<FView>(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Introduces a new match-view.
|
||||
* @param name of the new MatchView.
|
||||
* @tparam MView A MatchView. Needs to have a constructor of the form
|
||||
* MView(const cvv::impl::MatchCall&, QWidget*).
|
||||
*/
|
||||
template <class MView> void addMatchView(const QString name)
|
||||
{
|
||||
cvv::gui::MatchCallTab::registerMatchView<MView>(name);
|
||||
}
|
||||
|
||||
using TabFactory = controller::TabFactory;
|
||||
/**
|
||||
* @brief Introduces a new call-type.
|
||||
* @param factory A function that receives a reference to a call and should
|
||||
* return the appropriate
|
||||
* window.
|
||||
*/
|
||||
void addCallType(const QString name, TabFactory factory);
|
||||
|
||||
template <std::size_t In, std::size_t Out, class Filter>
|
||||
/**
|
||||
* @brief Introduces a new filter for the filter-selector-widget.
|
||||
*/
|
||||
bool registerFilter(const QString &name)
|
||||
{
|
||||
return cvv::qtutil::registerFilter<In, Out, Filter>(name);
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::extend
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,58 @@
|
||||
#ifndef CVVISUAL_CALL_TAB_HPP
|
||||
#define CVVISUAL_CALL_TAB_HPP
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Super class of the inner part of a tab or window.
|
||||
* A call tab.
|
||||
* The inner part of a tab or a window.
|
||||
* Super class for actual call tabs containing views.
|
||||
*/
|
||||
class CallTab : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Returns the name of this tab.
|
||||
* @return current name
|
||||
*/
|
||||
const QString getName() const
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the name of this tab.
|
||||
* @param name new name
|
||||
*/
|
||||
void setName(const QString &newName)
|
||||
{
|
||||
name = newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the of this CallTab.
|
||||
* @return the ID of the CallTab
|
||||
* (ID is equal to the ID of the associated call in derived classes)
|
||||
*/
|
||||
virtual size_t getId() const
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
QString name;
|
||||
};
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,285 @@
|
||||
#include "call_window.hpp"
|
||||
|
||||
#include <QMenu>
|
||||
#include <QStatusBar>
|
||||
#include <QPushButton>
|
||||
#include <QHBoxLayout>
|
||||
#include <QVariant>
|
||||
|
||||
#include "../stfl/stringutils.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
namespace controller
|
||||
{
|
||||
class ViewController;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
CallWindow::CallWindow(util::Reference<controller::ViewController> controller,
|
||||
size_t id)
|
||||
: id{ id }, controller{ controller }
|
||||
{
|
||||
initTabs();
|
||||
initFooter();
|
||||
setWindowTitle(QString("CVVisual | window no. %1").arg(id));
|
||||
setMinimumWidth(600);
|
||||
setMinimumHeight(600);
|
||||
}
|
||||
|
||||
void CallWindow::initTabs()
|
||||
{
|
||||
tabWidget = new TabWidget(this);
|
||||
tabWidget->setTabsClosable(true);
|
||||
tabWidget->setMovable(true);
|
||||
setCentralWidget(tabWidget);
|
||||
|
||||
auto *flowButtons = new QHBoxLayout();
|
||||
auto *flowButtonsWidget = new QWidget(this);
|
||||
tabWidget->setCornerWidget(flowButtonsWidget, Qt::TopLeftCorner);
|
||||
flowButtonsWidget->setLayout(flowButtons);
|
||||
flowButtons->setAlignment(Qt::AlignLeft | Qt::AlignTop);
|
||||
closeButton = new QPushButton("Close", this);
|
||||
flowButtons->addWidget(closeButton);
|
||||
closeButton->setStyleSheet(
|
||||
"QPushButton {background-color: red; color: white;}");
|
||||
closeButton->setToolTip("Close this debugging application.");
|
||||
connect(closeButton, SIGNAL(clicked()), this, SLOT(closeApp()));
|
||||
fastForwardButton = new QPushButton(">>", this);
|
||||
flowButtons->addWidget(fastForwardButton);
|
||||
fastForwardButton->setStyleSheet(
|
||||
"QPushButton {background-color: yellow; color: blue;}");
|
||||
fastForwardButton->setToolTip(
|
||||
"Fast forward until cvv::finalCall() gets called.");
|
||||
connect(fastForwardButton, SIGNAL(clicked()), this,
|
||||
SLOT(fastForward()));
|
||||
stepButton = new QPushButton("Step", this);
|
||||
flowButtons->addWidget(stepButton);
|
||||
stepButton->setStyleSheet(
|
||||
"QPushButton {background-color: green; color: white;}");
|
||||
stepButton->setToolTip(
|
||||
"Resume program execution for a next debugging step.");
|
||||
connect(stepButton, SIGNAL(clicked()), this, SLOT(step()));
|
||||
flowButtons->setContentsMargins(0, 0, 0, 0);
|
||||
flowButtons->setSpacing(0);
|
||||
|
||||
auto *tabBar = tabWidget->getTabBar();
|
||||
tabBar->setElideMode(Qt::ElideRight);
|
||||
tabBar->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(tabBar, SIGNAL(customContextMenuRequested(QPoint)), this,
|
||||
SLOT(contextMenuRequested(QPoint)));
|
||||
connect(tabBar, SIGNAL(tabCloseRequested(int)), this,
|
||||
SLOT(tabCloseRequested(int)));
|
||||
}
|
||||
|
||||
void CallWindow::initFooter()
|
||||
{
|
||||
leftFooter = new QLabel();
|
||||
rightFooter = new QLabel();
|
||||
QStatusBar *bar = statusBar();
|
||||
bar->addPermanentWidget(leftFooter, 2);
|
||||
bar->addPermanentWidget(rightFooter, 2);
|
||||
}
|
||||
|
||||
void CallWindow::showExitProgramButton()
|
||||
{
|
||||
stepButton->setVisible(false);
|
||||
fastForwardButton->setVisible(false);
|
||||
}
|
||||
|
||||
void CallWindow::addTab(CallTab *tab)
|
||||
{
|
||||
tabMap[tab->getId()] = tab;
|
||||
QString name = QString("[%1] %2").arg(tab->getId()).arg(tab->getName());
|
||||
int index =
|
||||
tabWidget->addTab(tab, stfl::shortenString(name, 20, true, true));
|
||||
tabWidget->getTabBar()->setTabData(index, QVariant((int)tab->getId()));
|
||||
}
|
||||
|
||||
size_t CallWindow::getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
void CallWindow::removeTab(CallTab *tab)
|
||||
{
|
||||
tabMap.erase(tabMap.find(tab->getId()));
|
||||
int index = tabWidget->indexOf(tab);
|
||||
tabWidget->removeTab(index);
|
||||
}
|
||||
|
||||
void CallWindow::removeTab(size_t tabId)
|
||||
{
|
||||
if (hasTab(tabId))
|
||||
{
|
||||
removeTab(tabMap[tabId]);
|
||||
}
|
||||
}
|
||||
|
||||
void CallWindow::showTab(CallTab *tab)
|
||||
{
|
||||
tabWidget->setCurrentWidget(tab);
|
||||
}
|
||||
|
||||
void CallWindow::showTab(size_t tabId)
|
||||
{
|
||||
if (hasTab(tabId))
|
||||
{
|
||||
showTab(tabMap[tabId]);
|
||||
}
|
||||
}
|
||||
|
||||
void CallWindow::updateLeftFooter(QString newText)
|
||||
{
|
||||
leftFooter->setText(newText);
|
||||
}
|
||||
|
||||
void CallWindow::updateRightFooter(QString newText)
|
||||
{
|
||||
rightFooter->setText(newText);
|
||||
}
|
||||
|
||||
void CallWindow::step()
|
||||
{
|
||||
controller->resumeProgramExecution();
|
||||
}
|
||||
|
||||
void CallWindow::fastForward()
|
||||
{
|
||||
controller->setMode(controller::Mode::FAST_FORWARD);
|
||||
}
|
||||
|
||||
void CallWindow::closeApp()
|
||||
{
|
||||
controller->setMode(controller::Mode::HIDE);
|
||||
}
|
||||
|
||||
bool CallWindow::hasTab(size_t tabId)
|
||||
{
|
||||
return tabMap.count(tabId);
|
||||
}
|
||||
|
||||
void CallWindow::contextMenuRequested(const QPoint &location)
|
||||
{
|
||||
controller->removeEmptyWindows();
|
||||
auto tabBar = tabWidget->getTabBar();
|
||||
int tabIndex = tabBar->tabAt(location);
|
||||
if (tabIndex == tabOffset - 1)
|
||||
return;
|
||||
QMenu *menu = new QMenu(this);
|
||||
connect(menu, SIGNAL(triggered(QAction *)), this,
|
||||
SLOT(contextMenuAction(QAction *)));
|
||||
auto windows = controller->getTabWindows();
|
||||
menu->addAction(new QAction("Remove call", this));
|
||||
menu->addAction(new QAction("Close tab", this));
|
||||
menu->addAction(new QAction("Open in new window", this));
|
||||
for (auto window : windows)
|
||||
{
|
||||
if (window->getId() != id)
|
||||
{
|
||||
menu->addAction(new QAction(
|
||||
QString("Open in '%1'").arg(window->windowTitle()),
|
||||
this));
|
||||
}
|
||||
}
|
||||
currentContextMenuTabId = getCallTabIdByTabIndex(tabIndex);
|
||||
menu->popup(tabBar->mapToGlobal(location));
|
||||
}
|
||||
|
||||
void CallWindow::contextMenuAction(QAction *action)
|
||||
{
|
||||
if (currentContextMenuTabId == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
auto text = action->text();
|
||||
if (text == "Open in new window")
|
||||
{
|
||||
controller->moveCallTabToNewWindow(currentContextMenuTabId);
|
||||
}
|
||||
else if (text == "Remove call")
|
||||
{
|
||||
controller->removeCallTab(currentContextMenuTabId, true, true);
|
||||
}
|
||||
else if (text == "Close tab")
|
||||
{
|
||||
controller->removeCallTab(currentContextMenuTabId);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto windows = controller->getTabWindows();
|
||||
for (auto window : windows)
|
||||
{
|
||||
if (text ==
|
||||
QString("Open in '%1'").arg(window->windowTitle()))
|
||||
{
|
||||
controller->moveCallTabToWindow(
|
||||
currentContextMenuTabId, window->getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
currentContextMenuTabId = -1;
|
||||
}
|
||||
|
||||
size_t CallWindow::tabCount()
|
||||
{
|
||||
return tabMap.size();
|
||||
}
|
||||
|
||||
std::vector<size_t> CallWindow::getCallTabIds()
|
||||
{
|
||||
std::vector<size_t> ids{};
|
||||
for (auto &elem : tabMap)
|
||||
{
|
||||
ids.push_back(elem.first);
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
void CallWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
controller->removeWindowFromMaps(id);
|
||||
// FIXME: tabWidget is already freed sometimes: Use-after-free Bug
|
||||
tabWidget->clear();
|
||||
for (auto &elem : tabMap)
|
||||
{
|
||||
controller->removeCallTab(elem.first, true);
|
||||
}
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void CallWindow::tabCloseRequested(int index)
|
||||
{
|
||||
if (hasTabAtIndex(index))
|
||||
{
|
||||
controller->removeCallTab(getCallTabIdByTabIndex(index));
|
||||
}
|
||||
controller->removeEmptyWindows();
|
||||
}
|
||||
|
||||
size_t CallWindow::getCallTabIdByTabIndex(int index)
|
||||
{
|
||||
if (hasTabAtIndex(index))
|
||||
{
|
||||
auto tabData = tabWidget->getTabBar()->tabData(index);
|
||||
bool ok = true;
|
||||
size_t callTabId = tabData.toInt(&ok);
|
||||
if (ok && tabMap.count(callTabId) > 0)
|
||||
{
|
||||
return callTabId;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool CallWindow::hasTabAtIndex(int index)
|
||||
{
|
||||
auto tabData = tabWidget->getTabBar()->tabData(index);
|
||||
return tabData != 0 && !tabData.isNull() && tabData.isValid();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
#ifndef CVVISUAL_CALLWINDOW_HPP
|
||||
#define CVVISUAL_CALLWINDOW_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QMainWindow>
|
||||
#include <QString>
|
||||
#include <vector>
|
||||
#include <QLabel>
|
||||
#include <QKeyEvent>
|
||||
#include <QPoint>
|
||||
#include <QCloseEvent>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "call_tab.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "tabwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
namespace controller
|
||||
{
|
||||
class ViewController;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Window inheriting some call tabs with in a tab widget.
|
||||
*/
|
||||
class CallWindow : public QMainWindow
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Contructs a new call window.
|
||||
* @param controller view controller that this window belongs to
|
||||
* @param id id of the window
|
||||
*/
|
||||
CallWindow(util::Reference<controller::ViewController> controller,
|
||||
size_t id);
|
||||
|
||||
/**
|
||||
* @brief Shows an "Exit program" button.
|
||||
*/
|
||||
void showExitProgramButton();
|
||||
|
||||
/**
|
||||
* @brief Add a new tab to the inherited tab widget.
|
||||
* @param tab new tab
|
||||
*/
|
||||
void addTab(CallTab *tab);
|
||||
|
||||
/**
|
||||
* @brief Get the id of this window.
|
||||
* @return id of this window.
|
||||
*/
|
||||
size_t getId();
|
||||
|
||||
/**
|
||||
* @brief Remove the given tab from this window.
|
||||
* @param given tab to remove
|
||||
*/
|
||||
void removeTab(CallTab *tab);
|
||||
|
||||
/**
|
||||
* @brief Remove the given tab from this window.
|
||||
* @param id of the given tab
|
||||
*/
|
||||
void removeTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Show the given tab.
|
||||
* @param given tab
|
||||
*/
|
||||
void showTab(CallTab *tab);
|
||||
|
||||
/**
|
||||
* @brief Show the given tab.
|
||||
* @param id of the given tab
|
||||
*/
|
||||
void showTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Examines whether or not the given is inherited in this window.
|
||||
* @param id of the given tab
|
||||
*/
|
||||
bool hasTab(size_t tabId);
|
||||
|
||||
/**
|
||||
* @brief Returns the number of tabs shown in this window.
|
||||
* @return number of tabs
|
||||
*/
|
||||
size_t tabCount();
|
||||
|
||||
/**
|
||||
* @brief Returns the ids of the available call tabs.
|
||||
* @return available call tabs' ids
|
||||
*/
|
||||
std::vector<size_t> getCallTabIds();
|
||||
|
||||
public slots:
|
||||
|
||||
/**
|
||||
* @brief Update the left footer with the given text.
|
||||
* @param newText given text
|
||||
*/
|
||||
void updateLeftFooter(QString newText);
|
||||
|
||||
/**
|
||||
* @brief Update the right footer with the given text.
|
||||
* @param newText given text
|
||||
*/
|
||||
void updateRightFooter(QString newText);
|
||||
|
||||
private slots:
|
||||
void contextMenuRequested(const QPoint &location);
|
||||
|
||||
void contextMenuAction(QAction *action);
|
||||
|
||||
void tabCloseRequested(int index);
|
||||
|
||||
void step();
|
||||
|
||||
void fastForward();
|
||||
|
||||
void closeApp();
|
||||
|
||||
protected:
|
||||
size_t id;
|
||||
util::Reference<controller::ViewController> controller;
|
||||
TabWidget *tabWidget;
|
||||
QMainWindow *window;
|
||||
QPushButton *closeButton;
|
||||
QPushButton *stepButton;
|
||||
QPushButton *fastForwardButton;
|
||||
std::map<size_t, CallTab *> tabMap;
|
||||
QLabel *leftFooter;
|
||||
QLabel *rightFooter;
|
||||
int currentContextMenuTabId = -1;
|
||||
int tabOffset = 0;
|
||||
|
||||
void initMenu();
|
||||
|
||||
void initTabs();
|
||||
|
||||
void initFooter();
|
||||
|
||||
void closeEvent(QCloseEvent *event) CV_OVERRIDE;
|
||||
|
||||
size_t getCallTabIdByTabIndex(int index);
|
||||
|
||||
bool hasTabAtIndex(int index);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,80 @@
|
||||
#ifndef CVVISUAL_FILTER_CALL_TAB_HPP
|
||||
#define CVVISUAL_FILTER_CALL_TAB_HPP
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "multiview_call_tab.hpp"
|
||||
#include "../view/filter_view.hpp"
|
||||
#include "../impl/filter_call.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/** Filter Call Tab.
|
||||
* @brief Inner part of a tab, contains a FilterView.
|
||||
* The inner part of a tab or window
|
||||
* containing a FilterView.
|
||||
* Allows to switch views and to access the help.
|
||||
*/
|
||||
class FilterCallTab
|
||||
: public MultiViewCallTab<cvv::view::FilterView, cvv::impl::FilterCall>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Short constructor named after the Call, using the requested View
|
||||
* from the Call or, if no or invalid request, default view.
|
||||
* Initializes the FilterCallTab with the requested or default view and names it
|
||||
* after the associated FilterCall.
|
||||
* @param filterCall - the FilterCall containing the information to be
|
||||
* visualized.
|
||||
*/
|
||||
FilterCallTab(const cvv::impl::FilterCall &filterCall)
|
||||
: FilterCallTab{
|
||||
filterCall, filterCall.requestedView()
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor with possibility to select view.
|
||||
* Note that the default view is still created first.
|
||||
* @param call - the MatchCall containing the information to be
|
||||
* visualized.
|
||||
* @param filterViewId - ID of the View to be set up. If a view of this name does
|
||||
* not exist, the default view will be used.
|
||||
*/
|
||||
FilterCallTab(const cvv::impl::FilterCall &filterCall, const QString& filterViewId)
|
||||
: MultiViewCallTab<cvv::view::FilterView, cvv::impl::FilterCall>{
|
||||
filterCall, filterViewId, QString{ "default_filter_view" }, QString{ "DefaultFilterView" }
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
~FilterCallTab()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register the template class to the map of FilterViews.
|
||||
* View needs to offer a constructor of the form View(const
|
||||
* cvv::impl::FilterCall&, QWidget*).
|
||||
* @param name to register the class under.
|
||||
* @tparam View - Class to register.
|
||||
* @return true when the view was registered and false when the name was
|
||||
* already taken.
|
||||
*/
|
||||
template <class View>
|
||||
static bool registerFilterView(const QString &name)
|
||||
{
|
||||
return registerView<View>(name);
|
||||
}
|
||||
};
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,75 @@
|
||||
#include <QString>
|
||||
#include <QPushButton>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "image_call_tab.hpp"
|
||||
#include "../view/image_view.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "../impl/single_image_call.hpp"
|
||||
#include "../qtutil/util.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
ImageCallTab::ImageCallTab(const cvv::impl::SingleImageCall &call)
|
||||
: imageCall_{ call }
|
||||
{
|
||||
setName(imageCall_->description());
|
||||
|
||||
createGui();
|
||||
}
|
||||
|
||||
ImageCallTab::ImageCallTab(const QString &tabName,
|
||||
const cvv::impl::SingleImageCall &call)
|
||||
: imageCall_{ call }
|
||||
{
|
||||
setName(tabName);
|
||||
|
||||
createGui();
|
||||
}
|
||||
|
||||
void ImageCallTab::helpButtonClicked() const
|
||||
{
|
||||
cvv::qtutil::openHelpBrowser("SingleImageView");
|
||||
}
|
||||
|
||||
size_t ImageCallTab::getId() const
|
||||
{
|
||||
return imageCall_->getId();
|
||||
}
|
||||
|
||||
void ImageCallTab::createGui()
|
||||
{
|
||||
hlayout_ = new QHBoxLayout{ this };
|
||||
hlayout_->setAlignment(Qt::AlignTop);
|
||||
hlayout_->addWidget(new QLabel{ "Single Image View" });
|
||||
helpButton_ = new QPushButton{ "Help", this };
|
||||
hlayout_->addWidget(helpButton_);
|
||||
connect(helpButton_, SIGNAL(clicked()), this,
|
||||
SLOT(helpButtonClicked()));
|
||||
|
||||
upperBar_ = new QWidget{ this };
|
||||
upperBar_->setLayout(hlayout_);
|
||||
|
||||
vlayout_ = new QVBoxLayout{ this };
|
||||
|
||||
vlayout_->addWidget(upperBar_);
|
||||
setView();
|
||||
|
||||
setLayout(vlayout_);
|
||||
imageView_->showFullImage();
|
||||
}
|
||||
|
||||
void ImageCallTab::setView()
|
||||
{
|
||||
imageView_ = new cvv::view::ImageView{ imageCall_->mat(), this };
|
||||
vlayout_->addWidget(imageView_);
|
||||
}
|
||||
}
|
||||
} // namespaces
|
||||
@@ -0,0 +1,93 @@
|
||||
#ifndef CVVISUAL_IMAGE_CALL_TAB_HPP
|
||||
#define CVVISUAL_IMAGE_CALL_TAB_HPP
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QString>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
|
||||
#include "call_tab.hpp"
|
||||
#include "../view/image_view.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "../impl/single_image_call.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/** Single Image Call Tab.
|
||||
* @brief Inner part of a tab, contains an IageView.
|
||||
* The inner part of a tab or window
|
||||
* containing an ImageView.
|
||||
* Allows to access the help.
|
||||
*/
|
||||
class ImageCallTab : public CallTab
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Short constructor named after the Call.
|
||||
* Initializes the ImageCallTab and names it after the associated
|
||||
* FilterCall.
|
||||
* @param call the SingleImageCall containing the information to be
|
||||
* visualized.
|
||||
*/
|
||||
ImageCallTab(const cvv::impl::SingleImageCall &call);
|
||||
|
||||
/**
|
||||
* @brief Constructor using default view.
|
||||
* Short constructor..
|
||||
* @param tabName.
|
||||
* @param call the SingleImageCall containing the information to be
|
||||
* visualized.
|
||||
* @attention might be deleted.
|
||||
*/
|
||||
ImageCallTab(const QString &tabName,
|
||||
const cvv::impl::SingleImageCall &call);
|
||||
|
||||
/**
|
||||
* @brief get ID.
|
||||
* @return the ID of the CallTab.
|
||||
* (ID is equal to the ID of the associated call).
|
||||
* Overrides CallTab's getId.
|
||||
*/
|
||||
size_t getId() const override;
|
||||
|
||||
private
|
||||
slots:
|
||||
|
||||
/**
|
||||
* @brief Help Button clicked.
|
||||
* Called when the help button is clicked.
|
||||
*/
|
||||
void helpButtonClicked() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Sets up the visible parts.
|
||||
* Called by the constructors.
|
||||
*/
|
||||
void createGui();
|
||||
|
||||
/**
|
||||
* @brief sets up View referred to by viewId.
|
||||
* @param viewId ID of the view to be set.
|
||||
* @throw std::out_of_range if no view named viewId was registered.
|
||||
*/
|
||||
void setView();
|
||||
|
||||
util::Reference<const cvv::impl::SingleImageCall> imageCall_;
|
||||
cvv::view::ImageView *imageView_;
|
||||
|
||||
QPushButton *helpButton_;
|
||||
QHBoxLayout *hlayout_;
|
||||
QVBoxLayout *vlayout_;
|
||||
QWidget *upperBar_;
|
||||
};
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "main_call_window.hpp"
|
||||
|
||||
#include <QApplication>
|
||||
#include <QPoint>
|
||||
|
||||
#include "../util/util.hpp"
|
||||
#include "../stfl/stringutils.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
MainCallWindow::MainCallWindow(
|
||||
util::Reference<controller::ViewController> controller, size_t id,
|
||||
OverviewPanel *ovPanel)
|
||||
: CallWindow(controller, id), ovPanel{ ovPanel }
|
||||
{
|
||||
tabOffset = 1;
|
||||
QString name = "Overview";
|
||||
tabWidget->insertTab(0, ovPanel, name);
|
||||
auto *tabBar = tabWidget->getTabBar();
|
||||
tabBar->tabButton(0, QTabBar::RightSide)->hide();
|
||||
setWindowTitle(QString("CVVisual | main window"));
|
||||
}
|
||||
|
||||
void MainCallWindow::showOverviewTab()
|
||||
{
|
||||
tabWidget->setCurrentWidget(ovPanel);
|
||||
}
|
||||
|
||||
void MainCallWindow::closeEvent(QCloseEvent *event)
|
||||
{
|
||||
(void)event;
|
||||
controller->setMode(controller::Mode::HIDE);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef CVVISUAL_MAINCALLWINDOW_HPP
|
||||
#define CVVISUAL_MAINCALLWINDOW_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QCloseEvent>
|
||||
|
||||
#include "call_window.hpp"
|
||||
#include "overview_panel.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
namespace controller
|
||||
{
|
||||
class ViewController;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class OverviewPanel;
|
||||
|
||||
/**
|
||||
* @brief A call window also inheriting the overview panel.
|
||||
*/
|
||||
class MainCallWindow : public CallWindow
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new main call window.
|
||||
* @param controller view controller inheriting this main window
|
||||
* @param id id of this main window
|
||||
* @param ovPanel inherited overview panel
|
||||
*/
|
||||
MainCallWindow(util::Reference<controller::ViewController> controller,
|
||||
size_t id, OverviewPanel *ovPanel);
|
||||
|
||||
~MainCallWindow()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Show the overview tab.
|
||||
*/
|
||||
void showOverviewTab();
|
||||
|
||||
/**
|
||||
* @brief Hides the close window.
|
||||
*/
|
||||
void hideCloseWindow();
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent *event) CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
OverviewPanel *ovPanel;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,107 @@
|
||||
#ifndef CVVISUAL_MATCH_CALL_TAB_HPP
|
||||
#define CVVISUAL_MATCH_CALL_TAB_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
|
||||
#include "multiview_call_tab.hpp"
|
||||
#include "../view/match_view.hpp"
|
||||
#include "../impl/match_call.hpp"
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/** Match Call Tab.
|
||||
* @brief Inner part of a tab, contains a MatchView.
|
||||
* The inner part of a tab or window
|
||||
* containing a MatchView.
|
||||
* Allows to switch views and to access the help.
|
||||
*/
|
||||
class MatchCallTab
|
||||
: public MultiViewCallTab<cvv::view::MatchView, cvv::impl::MatchCall>
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Short constructor named after Call and using the requested View
|
||||
* from the Call or, if no or invalid request, default view.
|
||||
* Initializes the MatchCallTab with the requested or default view and names it after
|
||||
* the associated MatchCall.
|
||||
* @param matchCall - the MatchCall containing the information to be
|
||||
* visualized.
|
||||
*/
|
||||
MatchCallTab(const cvv::impl::MatchCall &matchCall)
|
||||
: MatchCallTab{
|
||||
matchCall, matchCall.requestedView()
|
||||
}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor with possibility to select view.
|
||||
* Note that the default view is still created first.
|
||||
* @param matchCall - the MatchCall containing the information to be
|
||||
* visualized.
|
||||
* @param matchViewId - ID of the View to be set up. If a view of this name does
|
||||
* not exist, the default view will be used.
|
||||
*/
|
||||
MatchCallTab(const cvv::impl::MatchCall& matchCall, const QString& matchViewId)
|
||||
: MultiViewCallTab<cvv::view::MatchView, cvv::impl::MatchCall>{
|
||||
matchCall, matchViewId, QString{ "default_match_view" }, QString{ "LineMatchView" }
|
||||
}
|
||||
{
|
||||
oldView_ = view_;
|
||||
connect(&this->viewSet, SIGNAL(signal()), this, SLOT(viewChanged()));
|
||||
}
|
||||
|
||||
~MatchCallTab()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register the template class to the map of MatchViews.
|
||||
* View needs to offer a constructor of the form View(const
|
||||
* cvv::impl::MatchCall&, QWidget*).
|
||||
* @param name to register the class under.
|
||||
* @tparam View - Class to register.
|
||||
* @return true when the view was registered and false when the name was
|
||||
* already taken.
|
||||
*/
|
||||
template <class View> static bool registerMatchView(const QString &name)
|
||||
{
|
||||
return registerView<View>(name);
|
||||
}
|
||||
|
||||
private slots:
|
||||
|
||||
/**
|
||||
* @brief Slot called when the view has completely changed.
|
||||
*/
|
||||
void viewChanged()
|
||||
{
|
||||
if(oldView_ != nullptr)
|
||||
{
|
||||
view_->setKeyPointSelection(oldView_->getKeyPointSelection());
|
||||
view_->setMatchSelection(oldView_->getMatchSelection());
|
||||
}
|
||||
oldView_ = view_;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* @brief usually equal to view_, but not immediately changed when view_ is changed.
|
||||
*/
|
||||
cvv::view::MatchView* oldView_;
|
||||
|
||||
};
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,260 @@
|
||||
#ifndef CVVISUAL_MULTIVIEW_CALL_TAB_HPP
|
||||
#define CVVISUAL_MULTIVIEW_CALL_TAB_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QMap>
|
||||
#include <QPushButton>
|
||||
#include <QComboBox>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "call_tab.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../qtutil/registerhelper.hpp"
|
||||
#include "../qtutil/signalslot.hpp"
|
||||
#include "../qtutil/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/** Call Tab for multiple views.
|
||||
* @brief Inner part of a tab, contains a View.
|
||||
* The inner part of a tab or window
|
||||
* containing a View.
|
||||
* Allows to switch between different views and to access the help.
|
||||
* @tparam ViewType A type of View.
|
||||
* @tparam CallType A type of Call.
|
||||
*/
|
||||
template <class ViewType, class CallType>
|
||||
class MultiViewCallTab
|
||||
: public CallTab,
|
||||
public cvv::qtutil::RegisterHelper<ViewType, const CallType &, QWidget *>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Short constructor named after Call and using the default view.
|
||||
* Initializes the MultiViewCallTab with the default view and names it after
|
||||
* the associated Call.
|
||||
* @param call - the Call containing the information to be
|
||||
* visualized.
|
||||
* @param default_key - Key under which the default view is to be saved.
|
||||
* @param standard_default - Standard default view.
|
||||
*/
|
||||
MultiViewCallTab(const CallType &call, const QString& default_key, const QString& standard_default)
|
||||
: MultiViewCallTab{ call.description(), call, default_key, standard_default }
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor using the default view.
|
||||
* Initializes the MultiViewCallTab with the default view.
|
||||
* @param name - Name to give the CallTab.
|
||||
* @param call - the Call containing the information to be
|
||||
* visualized.
|
||||
* @param default_key - Key under which the default view is to be saved.
|
||||
* @param standard_default - Standard default view.
|
||||
*/
|
||||
MultiViewCallTab(const QString &tabName, const CallType &call, const QString& default_key, const QString& standard_default)
|
||||
: call_{ call }, currentIndexChanged{ [&]()
|
||||
{
|
||||
vlayout_->removeWidget(view_);
|
||||
view_->setVisible(false);
|
||||
setView();
|
||||
} },
|
||||
helpButtonClicked{ [&]()
|
||||
{ qtutil::openHelpBrowser(viewId_); } },
|
||||
setAsDefaultButtonClicked{ [&]()
|
||||
{ qtutil::setSetting(default_scope_, default_key_, viewId_); } }
|
||||
{
|
||||
setName(tabName);
|
||||
default_scope_ = QString{ "default_views" };
|
||||
default_key_ = default_key;
|
||||
standard_default_ = standard_default;
|
||||
// Sets standard_default_ as default in case no other default is
|
||||
// set:
|
||||
qtutil::setDefaultSetting(default_scope_, default_key_,
|
||||
standard_default_);
|
||||
viewId_ = qtutil::getSetting(default_scope_, default_key_);
|
||||
createGui();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Constructor with possibility to select view.
|
||||
* Note that the default view is still created first.
|
||||
* @param call - the Call containing the information to be
|
||||
* visualized.
|
||||
* @param viewId - ID of the View to be set up. If a view of this name does
|
||||
* not exist, the default view will be used.
|
||||
* @param default_key - Key under which the default view is to be saved.
|
||||
* @param standard_default - Standard default view.
|
||||
*/
|
||||
MultiViewCallTab(const CallType& call, const QString& viewId, const QString& default_key, const QString& standard_default)
|
||||
: MultiViewCallTab{call, default_key, standard_default}
|
||||
{
|
||||
this->select(viewId);
|
||||
}
|
||||
|
||||
~MultiViewCallTab()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief get ID.
|
||||
* @return the ID of the CallTab.
|
||||
* (ID is equal to the ID of the associated call).
|
||||
* Overrides CallTab's getId.
|
||||
*/
|
||||
size_t getId() const override
|
||||
{
|
||||
return call_->getId();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Register the template class to the map of Views.
|
||||
* View needs to offer a constructor of the form View(const
|
||||
* cvv::impl::CallType&, QWidget*).
|
||||
* @param name to register the class under.
|
||||
* @tparam View - Class to register.
|
||||
* @return true when the view was registered and false when the name was
|
||||
* already taken.
|
||||
*/
|
||||
template <class View> static bool registerView(const QString &name)
|
||||
{
|
||||
return MultiViewCallTab<ViewType, CallType>::registerElement(
|
||||
name, [](const CallType &call, QWidget *parent)
|
||||
{
|
||||
return cvv::util::make_unique<View>(call, parent);
|
||||
});
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Scope to search the default view in.
|
||||
*/
|
||||
QString default_scope_;
|
||||
/**
|
||||
* @brief Key under which the default view is saved.
|
||||
*/
|
||||
QString default_key_;
|
||||
/**
|
||||
* @brief standard default view.
|
||||
*/
|
||||
QString standard_default_;
|
||||
|
||||
/**
|
||||
* @brief Sets up the visible parts.
|
||||
* Called by the constructors.
|
||||
*/
|
||||
void createGui()
|
||||
{
|
||||
if (!this->select(viewId_))
|
||||
{
|
||||
this->select(standard_default_);
|
||||
viewId_ = this->selection();
|
||||
setAsDefaultButtonClicked.slot(); // Set as default.
|
||||
/* If viewId_ does not name a valid View, it will be
|
||||
* attempted to set standard_default_.
|
||||
* If that was not registered either, the current
|
||||
* selection of the ComboBox will be used automatically.
|
||||
* Whichever was chosen will be set as the new default.
|
||||
*/
|
||||
}
|
||||
hlayout_ = new QHBoxLayout{};
|
||||
hlayout_->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
||||
hlayout_->addWidget(new QLabel{ "View:" });
|
||||
hlayout_->addWidget(this->comboBox_);
|
||||
setAsDefaultButton_ = new QPushButton{ "Set as default", this };
|
||||
hlayout_->addWidget(setAsDefaultButton_);
|
||||
helpButton_ = new QPushButton{ "Help", this };
|
||||
hlayout_->addWidget(helpButton_);
|
||||
|
||||
upperBar_ = new QWidget{ this };
|
||||
upperBar_->setLayout(hlayout_);
|
||||
|
||||
vlayout_ = new QVBoxLayout{};
|
||||
|
||||
vlayout_->addWidget(upperBar_);
|
||||
setView();
|
||||
|
||||
setLayout(vlayout_);
|
||||
|
||||
QObject::connect(setAsDefaultButton_, SIGNAL(clicked()),
|
||||
&setAsDefaultButtonClicked, SLOT(slot()));
|
||||
QObject::connect(helpButton_, SIGNAL(clicked()),
|
||||
&helpButtonClicked, SLOT(slot()));
|
||||
QObject::connect(&this->signalElementSelected(),
|
||||
SIGNAL(signal(QString)), ¤tIndexChanged,
|
||||
SLOT(slot()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief sets up the View currently selected in the ComboBox inherited
|
||||
* from RegisterHelper.
|
||||
*/
|
||||
void setView()
|
||||
{
|
||||
viewId_ = this->selection();
|
||||
if (viewHistory_.count(this->selection()))
|
||||
{
|
||||
view_ = viewHistory_.at(this->selection());
|
||||
vlayout_->addWidget(view_);
|
||||
view_->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
viewHistory_.emplace(
|
||||
this->selection(),
|
||||
((*this)()(*call_, this).release()));
|
||||
view_ = viewHistory_.at(this->selection());
|
||||
vlayout_->addWidget(view_);
|
||||
}
|
||||
viewSet.emitSignal();
|
||||
}
|
||||
|
||||
util::Reference<const CallType> call_;
|
||||
QString viewId_;
|
||||
ViewType *view_;
|
||||
std::map<QString, ViewType *> viewHistory_;
|
||||
|
||||
QPushButton *helpButton_;
|
||||
QPushButton *setAsDefaultButton_;
|
||||
QHBoxLayout *hlayout_;
|
||||
QVBoxLayout *vlayout_;
|
||||
QWidget *upperBar_;
|
||||
|
||||
//signals:
|
||||
/**
|
||||
* @brief signal emitted whem view is completely set up.
|
||||
*/
|
||||
qtutil::Signal viewSet;
|
||||
|
||||
// slots:
|
||||
/**
|
||||
* @brief View selection change.
|
||||
* Slot called when the index of the view selection changes.
|
||||
*/
|
||||
qtutil::Slot currentIndexChanged;
|
||||
/**
|
||||
* @brief Help Button clicked.
|
||||
* Called when the help button is clicked.
|
||||
*/
|
||||
const qtutil::Slot helpButtonClicked;
|
||||
/**
|
||||
* @brief setAsDefaultButton clicked.
|
||||
* Called when the setAsDefaultButton,which sets the current view as
|
||||
* default, is clicked.
|
||||
*/
|
||||
qtutil::Slot setAsDefaultButtonClicked;
|
||||
};
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,318 @@
|
||||
#include "overview_group_subtable.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QStringList>
|
||||
#include <QModelIndex>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QHeaderView>
|
||||
#include <QList>
|
||||
#include <QSize>
|
||||
|
||||
#include "call_window.hpp"
|
||||
#include "overview_table.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
OverviewGroupSubtable::OverviewGroupSubtable(
|
||||
util::Reference<controller::ViewController> controller,
|
||||
OverviewTable *parent, stfl::ElementGroup<OverviewTableRow> group)
|
||||
: controller{ controller }, parent{ parent }, group{ std::move(group) }
|
||||
{
|
||||
controller->setDefaultSetting("overview", "imgsize",
|
||||
QString::number(100));
|
||||
initUI();
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::initUI()
|
||||
{
|
||||
controller->setDefaultSetting("overview", "imgzoom", "30");
|
||||
qTable = new QTableWidget(this);
|
||||
qTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
qTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||
qTable->setTextElideMode(Qt::ElideNone);
|
||||
auto verticalHeader = qTable->verticalHeader();
|
||||
verticalHeader->setVisible(false);
|
||||
auto horizontalHeader = qTable->horizontalHeader();
|
||||
horizontalHeader->setSectionResizeMode(QHeaderView::ResizeToContents);
|
||||
horizontalHeader->setStretchLastSection(false);
|
||||
connect(qTable, SIGNAL(cellDoubleClicked(int, int)), this,
|
||||
SLOT(rowClicked(int, int)));
|
||||
qTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(qTable, SIGNAL(customContextMenuRequested(QPoint)), this,
|
||||
SLOT(customMenuRequested(QPoint)));
|
||||
auto *layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(qTable);
|
||||
setLayout(layout);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::updateUI()
|
||||
{
|
||||
imgSize = controller->getSetting("overview", "imgzoom").toInt() *
|
||||
width() / 400;
|
||||
QStringList list{};
|
||||
list << "ID";
|
||||
maxImages = 0;
|
||||
for (auto element : group.getElements())
|
||||
{
|
||||
if (maxImages < element.call()->matrixCount())
|
||||
{
|
||||
maxImages = element.call()->matrixCount();
|
||||
}
|
||||
}
|
||||
if (parent->isShowingImages())
|
||||
{
|
||||
for (auto element : group.getElements())
|
||||
{
|
||||
if (maxImages < element.call()->matrixCount())
|
||||
{
|
||||
maxImages = element.call()->matrixCount();
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < maxImages; i++)
|
||||
{
|
||||
list << QString("Image ") + QString::number(i + 1);
|
||||
}
|
||||
}
|
||||
list << "Description"
|
||||
<< "Function"
|
||||
<< "File"
|
||||
<< "Line"
|
||||
<< "Type";
|
||||
qTable->setRowCount(group.size());
|
||||
qTable->setColumnCount(list.size());
|
||||
qTable->setHorizontalHeaderLabels(list);
|
||||
int textRowHeight = qTable->fontMetrics().height() + 5;
|
||||
if (textRowHeight >= imgSize)
|
||||
{
|
||||
qTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
qTable->setVerticalScrollMode(
|
||||
QAbstractItemView::ScrollPerPixel);
|
||||
}
|
||||
rowHeight = std::max(imgSize, textRowHeight);
|
||||
for (size_t i = 0; i < group.size(); i++)
|
||||
{
|
||||
group.get(i).addToTable(qTable, i, parent->isShowingImages(),
|
||||
maxImages, imgSize, imgSize);
|
||||
qTable->setRowHeight(i, rowHeight);
|
||||
}
|
||||
auto header = qTable->horizontalHeader();
|
||||
header->setSectionResizeMode(0, QHeaderView::ResizeToContents);
|
||||
for (size_t i = 1; i < maxImages + 1; i++)
|
||||
{
|
||||
header->setSectionResizeMode(i, QHeaderView::ResizeToContents);
|
||||
}
|
||||
for (size_t i = maxImages + 1; i < maxImages + 4; i++)
|
||||
{
|
||||
header->setSectionResizeMode(i, QHeaderView::Stretch);
|
||||
}
|
||||
header->setSectionResizeMode(maxImages + 4,
|
||||
QHeaderView::ResizeToContents);
|
||||
header->setSectionResizeMode(maxImages + 5,
|
||||
QHeaderView::ResizeToContents);
|
||||
updateMinimumSize();
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::rowClicked(int row, int collumn)
|
||||
{
|
||||
(void)collumn;
|
||||
size_t tabId = group.get(row).id();
|
||||
controller->showAndOpenCallTab(tabId);
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::customMenuRequested(QPoint location)
|
||||
{
|
||||
if (qTable->rowCount() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
controller->removeEmptyWindows();
|
||||
QMenu *menu = new QMenu(this);
|
||||
auto windows = controller->getTabWindows();
|
||||
menu->addAction(new QAction("Open in new window", this));
|
||||
for (auto window : windows)
|
||||
{
|
||||
menu->addAction(new QAction(
|
||||
QString("Open in '%1'").arg(window->windowTitle()), this));
|
||||
}
|
||||
menu->addAction(new QAction("Remove call", this));
|
||||
QModelIndex index = qTable->indexAt(location);
|
||||
if (!index.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
int row = index.row();
|
||||
QString idStr = qTable->item(row, 0)->text();
|
||||
connect(menu, SIGNAL(triggered(QAction *)), this,
|
||||
SLOT(customMenuAction(QAction *)));
|
||||
std::stringstream{ idStr.toStdString() } >> currentCustomMenuCallTabId;
|
||||
currentCustomMenuCallTabIdValid = true;
|
||||
menu->popup(mapToGlobal(location));
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::customMenuAction(QAction *action)
|
||||
{
|
||||
if (!currentCustomMenuCallTabIdValid)
|
||||
{
|
||||
return;
|
||||
}
|
||||
QString actionText = action->text();
|
||||
if (actionText == "Open in new window")
|
||||
{
|
||||
controller->moveCallTabToNewWindow(currentCustomMenuCallTabId);
|
||||
currentCustomMenuCallTabId = -1;
|
||||
return;
|
||||
}
|
||||
else if (actionText == "Remove call")
|
||||
{
|
||||
controller->removeCallTab(currentCustomMenuCallTabId, true,
|
||||
true);
|
||||
currentCustomMenuCallTabId = -1;
|
||||
return;
|
||||
}
|
||||
auto windows = controller->getTabWindows();
|
||||
for (auto window : windows)
|
||||
{
|
||||
if (actionText ==
|
||||
QString("Open in '%1'").arg(window->windowTitle()))
|
||||
{
|
||||
controller->moveCallTabToWindow(
|
||||
currentCustomMenuCallTabId, window->getId());
|
||||
break;
|
||||
}
|
||||
}
|
||||
currentCustomMenuCallTabId = -1;
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::resizeEvent(QResizeEvent *event)
|
||||
{
|
||||
(void)event;
|
||||
imgSize = controller->getSetting("overview", "imgzoom").toInt() *
|
||||
width() / 400;
|
||||
rowHeight = std::max(imgSize, qTable->fontMetrics().height() + 5);
|
||||
for (size_t row = 0; row < group.size(); row++)
|
||||
{
|
||||
group.get(row).resizeInTable(qTable, row,
|
||||
parent->isShowingImages(), maxImages,
|
||||
imgSize, imgSize);
|
||||
qTable->setRowHeight(row, rowHeight);
|
||||
}
|
||||
updateMinimumSize();
|
||||
event->accept();
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::removeRow(size_t id)
|
||||
{
|
||||
for (size_t i = 0; i < group.size(); i++)
|
||||
{
|
||||
if (group.get(i).id() == id)
|
||||
{
|
||||
group.removeElement(i);
|
||||
updateUI();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool OverviewGroupSubtable::hasRow(size_t id)
|
||||
{
|
||||
for (size_t i = 0; i < group.size(); i++)
|
||||
{
|
||||
if (group.get(i).id() == id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::setRowGroup(
|
||||
stfl::ElementGroup<OverviewTableRow> &newGroup)
|
||||
{
|
||||
auto compFunc = [](const OverviewTableRow &first,
|
||||
const OverviewTableRow &second)
|
||||
{ return first.id() == second.id(); };
|
||||
if (group.hasSameElementList(newGroup, compFunc))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Now both groups aren't the same
|
||||
size_t newMax = 0;
|
||||
for (auto row : newGroup.getElements())
|
||||
{
|
||||
if (row.call()->matrixCount() > newMax)
|
||||
{
|
||||
newMax = row.call()->matrixCount();
|
||||
}
|
||||
}
|
||||
if (newMax == maxImages)
|
||||
{
|
||||
group = newGroup;
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
// Now both groups have the same maximum number of images within their
|
||||
// elements
|
||||
size_t minLength = std::min(group.size(), newGroup.size());
|
||||
for (size_t i = 0; i < minLength; i++)
|
||||
{
|
||||
if (group.get(i).id() != newGroup.get(i).id())
|
||||
{
|
||||
group = newGroup;
|
||||
updateUI();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Now the bigger group's element lists starts with the smaller group's
|
||||
// one
|
||||
if (group.size() < newGroup.size())
|
||||
{
|
||||
// Now the new group appends the current group
|
||||
for (size_t row = group.size(); row < newGroup.size(); row++)
|
||||
{
|
||||
newGroup.get(row)
|
||||
.addToTable(qTable, row, parent->isShowingImages(),
|
||||
maxImages, imgSize, imgSize);
|
||||
qTable->setRowHeight(row, rowHeight);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Now the new group deletes elements from the current group
|
||||
for (size_t row = group.size() - 1; row >= newGroup.size();
|
||||
row--)
|
||||
{
|
||||
qTable->removeRow(row);
|
||||
}
|
||||
}
|
||||
group = newGroup;
|
||||
updateMinimumSize();
|
||||
}
|
||||
|
||||
void OverviewGroupSubtable::updateMinimumSize()
|
||||
{
|
||||
int width = qTable->sizeHint().width();
|
||||
setMinimumWidth(width);
|
||||
int height = qTable->horizontalHeader()->height();
|
||||
for (int a = 0; a < qTable->rowCount(); ++a)
|
||||
{
|
||||
height += qTable->rowHeight(a);
|
||||
}
|
||||
setMinimumHeight(height + (qTable->rowCount() * 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
#ifndef CVVISUAL_OVERVIEW_GROUP_SUBTABLE_HPP
|
||||
#define CVVISUAL_OVERVIEW_GROUP_SUBTABLE_HPP
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QAction>
|
||||
#include <QResizeEvent>
|
||||
|
||||
#include "../stfl/element_group.hpp"
|
||||
#include "overview_table_row.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace controller
|
||||
{
|
||||
class ViewController;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class OverviewTable;
|
||||
|
||||
/**
|
||||
* @brief A table for the a group of overview data sets.
|
||||
*/
|
||||
class OverviewGroupSubtable : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an over group subtable.
|
||||
* @param controller view controller
|
||||
* @param parent parent table
|
||||
* @param group the displayed group of overview data sets
|
||||
*/
|
||||
OverviewGroupSubtable(
|
||||
util::Reference<controller::ViewController> controller,
|
||||
OverviewTable *parent, stfl::ElementGroup<OverviewTableRow> group);
|
||||
|
||||
~OverviewGroupSubtable()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Updates the displayed table UI.
|
||||
*/
|
||||
void updateUI();
|
||||
|
||||
/**
|
||||
* @brief Remove the row with the given id.
|
||||
* @param given table row id
|
||||
*/
|
||||
void removeRow(size_t id);
|
||||
|
||||
/**
|
||||
* @brief Checks whether or not the table shows the row with the given
|
||||
* id.
|
||||
* @param id given row id
|
||||
* @return Does the table show the row with the given id?
|
||||
*/
|
||||
bool hasRow(size_t id);
|
||||
|
||||
/**
|
||||
* @brief Set the displayed rows.
|
||||
* @note This method does some optimisations to only fully rebuild all
|
||||
* rows if necessary.
|
||||
* @param newGroup new group of rows that will be displayed
|
||||
*/
|
||||
void setRowGroup(stfl::ElementGroup<OverviewTableRow> &newGroup);
|
||||
|
||||
protected:
|
||||
void resizeEvent(QResizeEvent *event) CV_OVERRIDE;
|
||||
|
||||
private slots:
|
||||
void rowClicked(int row, int collumn);
|
||||
void customMenuRequested(QPoint location);
|
||||
void customMenuAction(QAction *action);
|
||||
|
||||
private:
|
||||
util::Reference<controller::ViewController> controller;
|
||||
OverviewTable *parent;
|
||||
stfl::ElementGroup<OverviewTableRow> group;
|
||||
QTableWidget *qTable;
|
||||
size_t currentCustomMenuCallTabId = 0;
|
||||
bool currentCustomMenuCallTabIdValid = false;
|
||||
size_t maxImages = 0;
|
||||
int imgSize = 0;
|
||||
int rowHeight = 0;
|
||||
|
||||
void initUI();
|
||||
|
||||
void updateMinimumSize();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,214 @@
|
||||
#include "overview_panel.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <math.h>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
#include <QMap>
|
||||
#include <QSet>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
#include <QWidget>
|
||||
#include <QScrollArea>
|
||||
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "../qtutil/stfl_query_widget.hpp"
|
||||
#include "../qtutil/util.hpp"
|
||||
#include "../stfl/element_group.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
OverviewPanel::OverviewPanel(
|
||||
util::Reference<controller::ViewController> controller)
|
||||
: controller{ controller }
|
||||
{
|
||||
qtutil::setDefaultSetting("overview", "imgzoom", "20");
|
||||
QVBoxLayout *layout = new QVBoxLayout{};
|
||||
setLayout(layout);
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
queryWidget = new qtutil::STFLQueryWidget();
|
||||
layout->addWidget(queryWidget);
|
||||
table = new OverviewTable(controller);
|
||||
layout->addWidget(table);
|
||||
|
||||
auto bottomArea = new QWidget{ this };
|
||||
auto bottomLayout = new QHBoxLayout;
|
||||
imgSizeSliderLabel = new QLabel{ "Zoom", bottomArea };
|
||||
imgSizeSliderLabel->setMaximumWidth(50);
|
||||
imgSizeSliderLabel->setAlignment(Qt::AlignRight);
|
||||
bottomLayout->addWidget(imgSizeSliderLabel);
|
||||
imgSizeSlider = new QSlider{ Qt::Horizontal, bottomArea };
|
||||
imgSizeSlider->setMinimumWidth(50);
|
||||
imgSizeSlider->setMaximumWidth(200);
|
||||
imgSizeSlider->setMinimum(0);
|
||||
imgSizeSlider->setMaximum(100);
|
||||
imgSizeSlider->setSliderPosition(
|
||||
qtutil::getSetting("overview", "imgzoom").toInt());
|
||||
connect(imgSizeSlider, SIGNAL(valueChanged(int)), this,
|
||||
SLOT(imgSizeSliderAction()));
|
||||
bottomLayout->addWidget(imgSizeSlider);
|
||||
bottomArea->setLayout(bottomLayout);
|
||||
layout->addWidget(bottomArea);
|
||||
|
||||
initEngine();
|
||||
connect(queryWidget, SIGNAL(showHelp(QString)), this,
|
||||
SLOT(showHelp(QString)));
|
||||
// connect(queryWidget, SIGNAL(userInputUpdate(QString)), this,
|
||||
// SLOT(updateQuery(QString)));
|
||||
connect(queryWidget, SIGNAL(filterSignal(QString)), this,
|
||||
SLOT(filterQuery(QString)));
|
||||
connect(queryWidget, SIGNAL(requestSuggestions(QString)), this,
|
||||
SLOT(requestSuggestions(QString)));
|
||||
}
|
||||
|
||||
void OverviewPanel::initEngine()
|
||||
{
|
||||
// raw and description filter
|
||||
auto rawFilter = [](const OverviewTableRow &elem)
|
||||
{
|
||||
return elem.description();
|
||||
};
|
||||
queryEngine.addStringCmdFunc("raw", rawFilter, false);
|
||||
queryEngine.addStringCmdFunc("description", rawFilter, false);
|
||||
|
||||
// file filter
|
||||
queryEngine.addStringCmdFunc("file", [](const OverviewTableRow &elem)
|
||||
{
|
||||
return elem.file();
|
||||
});
|
||||
|
||||
// function filter
|
||||
queryEngine.addStringCmdFunc("function",
|
||||
[](const OverviewTableRow &elem)
|
||||
{
|
||||
return elem.function();
|
||||
});
|
||||
|
||||
// line filter
|
||||
queryEngine.addIntegerCmdFunc("line", [](const OverviewTableRow &elem)
|
||||
{ return elem.line(); });
|
||||
|
||||
// id filter
|
||||
queryEngine.addIntegerCmdFunc("id", [](const OverviewTableRow &elem)
|
||||
{
|
||||
return elem.id();
|
||||
});
|
||||
|
||||
// type filter
|
||||
queryEngine.addStringCmdFunc("type", [](const OverviewTableRow &elem)
|
||||
{
|
||||
return elem.type();
|
||||
});
|
||||
|
||||
//"number of images" filter
|
||||
queryEngine.addIntegerCmdFunc("image_count",
|
||||
[](const OverviewTableRow &elem)
|
||||
{
|
||||
return elem.call()->matrixCount();
|
||||
});
|
||||
|
||||
//additional commands
|
||||
|
||||
//open call command
|
||||
queryEngine.addAdditionalCommand("open",
|
||||
[&](QStringList args, std::vector<stfl::ElementGroup<OverviewTableRow>>& groups)
|
||||
{
|
||||
openCommand(args, groups);
|
||||
}, {"first_of_group", "last_of_group", "shown"});
|
||||
}
|
||||
|
||||
void OverviewPanel::openCommand(QStringList args,
|
||||
std::vector<stfl::ElementGroup<OverviewTableRow>>& groups)
|
||||
{
|
||||
if (args.contains("shown"))
|
||||
{
|
||||
for (auto &group : groups)
|
||||
{
|
||||
for (auto &elem : group.getElements())
|
||||
{
|
||||
controller->openCallTab(elem.id());
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (args.contains("first_of_group") || args.contains("last_of_group"))
|
||||
{
|
||||
bool first = args.contains("first_of_group") ;
|
||||
for (auto &group : groups)
|
||||
{
|
||||
if (group.size() > 0)
|
||||
{
|
||||
size_t index = first ? 0 : group.size() - 1;
|
||||
size_t id = group.get(index).id();
|
||||
controller->openCallTab(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void OverviewPanel::addElement(const util::Reference<const impl::Call> newCall)
|
||||
{
|
||||
OverviewTableRow row(newCall);
|
||||
queryEngine.addNewElement(row);
|
||||
table->updateRowGroups(queryEngine.reexecuteLastQuery());
|
||||
}
|
||||
|
||||
void OverviewPanel::addElementBuffered(const util::Reference<const impl::Call> newCall)
|
||||
{
|
||||
elementBuffer.push_back(newCall);
|
||||
}
|
||||
|
||||
void OverviewPanel::flushElementBuffer()
|
||||
{
|
||||
std::vector<OverviewTableRow> rows;
|
||||
for (const util::Reference<const impl::Call> call : elementBuffer)
|
||||
{
|
||||
rows.push_back(OverviewTableRow(call));
|
||||
}
|
||||
queryEngine.addElements(std::move(rows));
|
||||
table->updateRowGroups(queryEngine.reexecuteLastQuery());
|
||||
elementBuffer.clear();
|
||||
}
|
||||
|
||||
void OverviewPanel::removeElement(size_t id)
|
||||
{
|
||||
queryEngine.removeElements([id](OverviewTableRow elem)
|
||||
{
|
||||
return elem.id() == id;
|
||||
});
|
||||
table->removeElement(id);
|
||||
}
|
||||
|
||||
void OverviewPanel::filterQuery(QString query)
|
||||
{
|
||||
table->updateRowGroups(queryEngine.query(query));
|
||||
}
|
||||
|
||||
void OverviewPanel::updateQuery(QString query)
|
||||
{
|
||||
filterQuery(query);
|
||||
}
|
||||
|
||||
void OverviewPanel::requestSuggestions(QString query)
|
||||
{
|
||||
queryWidget->showSuggestions(queryEngine.getSuggestions(query));
|
||||
}
|
||||
|
||||
void OverviewPanel::imgSizeSliderAction()
|
||||
{
|
||||
controller->setSetting("overview", "imgzoom",
|
||||
QString::number(imgSizeSlider->value()));
|
||||
table->updateUI();
|
||||
}
|
||||
|
||||
void OverviewPanel::showHelp(QString topic)
|
||||
{
|
||||
controller->openHelpBrowser(topic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef CVVISUAL_OVERVIEWPANEL_HPP
|
||||
#define CVVISUAL_OVERVIEWPANEL_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QSlider>
|
||||
#include <QLabel>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "../stfl/stfl_engine.hpp"
|
||||
#include "../impl/call.hpp"
|
||||
#include "overview_table.hpp"
|
||||
#include "overview_table_row.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
namespace controller
|
||||
{
|
||||
class ViewController;
|
||||
}
|
||||
|
||||
namespace qtutil
|
||||
{
|
||||
class STFLQueryWidget;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class OverviewTable;
|
||||
class OverviewTableRow;
|
||||
|
||||
/**
|
||||
* @brief The overview showing a filterable table displaying the different
|
||||
* calls.
|
||||
*/
|
||||
class OverviewPanel : public QWidget
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Contructs an OverviewPanel.
|
||||
* @param controller ViewController that inherits this overview
|
||||
*/
|
||||
OverviewPanel(util::Reference<controller::ViewController> controller);
|
||||
|
||||
/**
|
||||
* @brief Adds the given call to the shown overview table.
|
||||
* @param newCall given call
|
||||
*/
|
||||
void addElement(const util::Reference<const impl::Call> newCall);
|
||||
|
||||
/**
|
||||
* @brief Changes the "Resume program execution" button label to "Exit
|
||||
* Application."
|
||||
*/
|
||||
void showExitApplicationButton();
|
||||
|
||||
/**
|
||||
* @brief Adds the given call buffered to the shown overview table.
|
||||
* @note Be sure to flush the buffer via flushElementBuffer() later.
|
||||
* @param newCall given call
|
||||
*/
|
||||
void addElementBuffered(const util::Reference<const impl::Call> newCall);
|
||||
|
||||
/**
|
||||
* @brief Flushes the element buffer and shows its elements in the overview table.
|
||||
*/
|
||||
void flushElementBuffer();
|
||||
|
||||
/**
|
||||
* @brief Removes and deletes the element with the given id.
|
||||
* @param id given element id
|
||||
*/
|
||||
void removeElement(size_t id);
|
||||
|
||||
private slots:
|
||||
|
||||
void filterQuery(QString query);
|
||||
|
||||
void updateQuery(QString query);
|
||||
|
||||
void requestSuggestions(QString query);
|
||||
|
||||
void imgSizeSliderAction();
|
||||
|
||||
void showHelp(QString topic);
|
||||
|
||||
private:
|
||||
stfl::STFLEngine<OverviewTableRow> queryEngine{"Overview"};
|
||||
qtutil::STFLQueryWidget *queryWidget;
|
||||
OverviewTable *table;
|
||||
util::Reference<controller::ViewController> controller;
|
||||
QLabel *imgSizeSliderLabel;
|
||||
QSlider *imgSizeSlider;
|
||||
std::vector<util::Reference<const impl::Call>> elementBuffer;
|
||||
|
||||
void initEngine();
|
||||
|
||||
void openCommand(QStringList args,
|
||||
std::vector<stfl::ElementGroup<OverviewTableRow>>& groups);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,125 @@
|
||||
#include "overview_table.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QStringList>
|
||||
|
||||
#include "../stfl/element_group.hpp"
|
||||
#include "overview_table_row.hpp"
|
||||
#include "overview_group_subtable.hpp"
|
||||
#include "../qtutil/accordion.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
OverviewTable::OverviewTable(
|
||||
util::Reference<controller::ViewController> controller)
|
||||
: controller{ controller }
|
||||
{
|
||||
subtableAccordion = new qtutil::Accordion{};
|
||||
auto *layout = new QVBoxLayout{};
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(subtableAccordion);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void OverviewTable::updateRowGroups(
|
||||
std::vector<stfl::ElementGroup<OverviewTableRow>> newGroups)
|
||||
{
|
||||
bool startTheSame = true;
|
||||
for (size_t i = 0; i < std::min(groups.size(), newGroups.size()); i++)
|
||||
{
|
||||
if (!newGroups.at(i).hasSameTitles(groups.at(i)))
|
||||
{
|
||||
startTheSame = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (startTheSame && groups.size() <= newGroups.size())
|
||||
{
|
||||
for (size_t i = 0;
|
||||
i < std::min(groups.size(), newGroups.size()); i++)
|
||||
{
|
||||
subTables.at(i)->setRowGroup(newGroups.at(i));
|
||||
subTables.at(i)->updateUI();
|
||||
}
|
||||
for (size_t i = groups.size(); i < newGroups.size(); i++)
|
||||
{
|
||||
appendRowGroupToTable(newGroups.at(i));
|
||||
subTables.at(i)->setRowGroup(newGroups.at(i));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
subtableAccordion->clear();
|
||||
subTables.clear();
|
||||
for (auto &group : newGroups)
|
||||
{
|
||||
appendRowGroupToTable(group);
|
||||
}
|
||||
}
|
||||
groups = newGroups;
|
||||
}
|
||||
|
||||
void OverviewTable::hideImages()
|
||||
{
|
||||
doesShowImages = false;
|
||||
updateUI();
|
||||
}
|
||||
|
||||
void OverviewTable::showImages()
|
||||
{
|
||||
doesShowImages = true;
|
||||
updateUI();
|
||||
}
|
||||
|
||||
bool OverviewTable::isShowingImages()
|
||||
{
|
||||
return doesShowImages;
|
||||
}
|
||||
|
||||
void OverviewTable::updateUI()
|
||||
{
|
||||
for (auto *subTable : subTables)
|
||||
{
|
||||
subTable->updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
void OverviewTable::removeElement(size_t id)
|
||||
{
|
||||
for (auto *subTable : subTables)
|
||||
{
|
||||
if (subTable->hasRow(id))
|
||||
{
|
||||
subTable->removeRow(id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
OverviewTable::appendRowGroupToTable(stfl::ElementGroup<OverviewTableRow> group)
|
||||
{
|
||||
if (group.size() > 0)
|
||||
{
|
||||
auto subtable = util::make_unique<OverviewGroupSubtable>(
|
||||
controller, this, std::move(group));
|
||||
auto subtablePtr = subtable.get();
|
||||
auto titles = group.getTitles();
|
||||
QString title =
|
||||
"No grouping specified, use #group to specify one";
|
||||
if (titles.size() != 0)
|
||||
{
|
||||
title = titles.join(", ");
|
||||
}
|
||||
subtableAccordion->push_back(title, std::move(subtable), false);
|
||||
subTables.push_back(subtablePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
#ifndef CVVISUAL_OVERVIEWTABLE_HPP
|
||||
#define CVVISUAL_OVERVIEWTABLE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
|
||||
#include "overview_panel.hpp"
|
||||
#include "overview_table_row.hpp"
|
||||
#include "../stfl/element_group.hpp"
|
||||
#include "../qtutil/accordion.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
#include "overview_group_subtable.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class OverviewPanel;
|
||||
class OverviewTableRow;
|
||||
|
||||
/**
|
||||
* @brief A table displaying the different calls in the overview.
|
||||
* It's actually an accordion of subtables to support grouping.
|
||||
*/
|
||||
class OverviewTable : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a new OverviewTable.
|
||||
* @param controller it's ViewController
|
||||
*/
|
||||
OverviewTable(util::Reference<controller::ViewController> controller);
|
||||
|
||||
~OverviewTable()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Update the inherited groups of rows and rebuild the UI fully.
|
||||
* @param newGroups new groups for this table
|
||||
*/
|
||||
void updateRowGroups(
|
||||
const std::vector<stfl::ElementGroup<OverviewTableRow>> newGroups);
|
||||
|
||||
/**
|
||||
* @brief Hide the thumbnail images in the tables.
|
||||
*/
|
||||
void hideImages();
|
||||
|
||||
/**
|
||||
* @brief Show thumbnail images in the tables.
|
||||
*/
|
||||
void showImages();
|
||||
|
||||
/**
|
||||
* @brief Does this the tables show thumbnail images?
|
||||
*/
|
||||
bool isShowingImages();
|
||||
|
||||
/**
|
||||
* @brief Updates the UI.
|
||||
* Updates all subtables.
|
||||
*/
|
||||
void updateUI();
|
||||
|
||||
/**
|
||||
* @brief Removes the table element with the given id.
|
||||
* @param id given element id
|
||||
*/
|
||||
void removeElement(size_t id);
|
||||
|
||||
private:
|
||||
util::Reference<controller::ViewController> controller;
|
||||
bool doesShowImages = true;
|
||||
qtutil::Accordion *subtableAccordion;
|
||||
std::vector<OverviewGroupSubtable *> subTables{};
|
||||
std::vector<stfl::ElementGroup<OverviewTableRow>> groups;
|
||||
|
||||
void appendRowGroupToTable(stfl::ElementGroup<OverviewTableRow> group);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,104 @@
|
||||
#include "overview_table_row.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
#include <QImage>
|
||||
|
||||
#include "../qtutil/util.hpp"
|
||||
#include "../stfl/stringutils.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
OverviewTableRow::OverviewTableRow(util::Reference<const impl::Call> call)
|
||||
: call_{ call }
|
||||
{
|
||||
id_ = call_->getId();
|
||||
idStr = QString::number(call_->getId());
|
||||
for (size_t i = 0; i < 2 && i < call->matrixCount(); i++)
|
||||
{
|
||||
QPixmap img;
|
||||
std::tie(std::ignore, img) =
|
||||
qtutil::convertMatToQPixmap(call->matrixAt(i));
|
||||
imgs.push_back(std::move(img));
|
||||
}
|
||||
description_ = QString(call_->description());
|
||||
if (call_->metaData().isKnown)
|
||||
{
|
||||
const auto &data = call_->metaData();
|
||||
line_ = data.line;
|
||||
lineStr = QString::number(data.line);
|
||||
fileStr = data.file;
|
||||
functionStr = data.function;
|
||||
}
|
||||
typeStr = QString(call_->type());
|
||||
}
|
||||
|
||||
void OverviewTableRow::addToTable(QTableWidget *table, size_t row,
|
||||
bool showImages, size_t maxImages,
|
||||
int imgHeight, int imgWidth)
|
||||
{
|
||||
std::vector<std::unique_ptr<QTableWidgetItem>> items{};
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(idStr));
|
||||
if (showImages)
|
||||
{
|
||||
for (size_t i = 0; i < imgs.size() && i < maxImages; i++)
|
||||
{
|
||||
auto imgWidget = util::make_unique<QTableWidgetItem>("");
|
||||
imgWidget->setData(
|
||||
Qt::DecorationRole,
|
||||
imgs.at(i).scaled(imgHeight, imgWidth,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
imgWidget->setTextAlignment(Qt::AlignHCenter);
|
||||
items.push_back(std::move(imgWidget));
|
||||
}
|
||||
}
|
||||
|
||||
size_t emptyImagesToAdd =
|
||||
showImages ? maxImages - std::min(maxImages, imgs.size())
|
||||
: maxImages;
|
||||
|
||||
for (size_t i = 0; i < emptyImagesToAdd; i++)
|
||||
{
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(""));
|
||||
}
|
||||
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(description_));
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(functionStr, 30));
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(fileStr));
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(lineStr));
|
||||
items.push_back(util::make_unique<QTableWidgetItem>(typeStr));
|
||||
for (size_t i = 0; i < items.size(); i++)
|
||||
{
|
||||
items[i]->setFlags(items[i]->flags() ^ Qt::ItemIsEditable);
|
||||
table->setItem(row, i, items[i].release());
|
||||
}
|
||||
}
|
||||
|
||||
void OverviewTableRow::resizeInTable(QTableWidget *table, size_t row,
|
||||
bool showImages, size_t maxImages,
|
||||
int imgHeight, int imgWidth)
|
||||
{
|
||||
if (showImages)
|
||||
{
|
||||
for (size_t i = 0; i < imgs.size() && i < maxImages; i++)
|
||||
{
|
||||
auto imgWidget = util::make_unique<QTableWidgetItem>("");
|
||||
imgWidget->setData(
|
||||
Qt::DecorationRole,
|
||||
imgs.at(i).scaled(imgHeight, imgWidth,
|
||||
Qt::KeepAspectRatio,
|
||||
Qt::SmoothTransformation));
|
||||
imgWidget->setTextAlignment(Qt::AlignHCenter);
|
||||
imgWidget->setFlags(imgWidget->flags() ^ Qt::ItemIsEditable);
|
||||
table->setItem(row, i + 1, imgWidget.release());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
#ifndef CVVISUAL_OVERVIEWTABLEROW_HPP
|
||||
#define CVVISUAL_OVERVIEWTABLEROW_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QTableWidget>
|
||||
#include <QString>
|
||||
#include <QPixmap>
|
||||
|
||||
#include "../impl/call.hpp"
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A UI wrapper for an impl::Call, providing utility and UI functions.
|
||||
*
|
||||
* Also allowing it to add its data to a table.
|
||||
*/
|
||||
class OverviewTableRow
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor of this class.
|
||||
* @param call call this row is based on
|
||||
*/
|
||||
OverviewTableRow(util::Reference<const impl::Call> call);
|
||||
|
||||
~OverviewTableRow()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds the inherited data set to the given table.
|
||||
* @param table given table
|
||||
* @param row row index at which the data will be shown
|
||||
* @param showImages does the table show images?
|
||||
* @param maxImages the maximum number of images the table shows
|
||||
* @param imgHeight height of the shown images
|
||||
* @param imgWidth width of the shown images
|
||||
*/
|
||||
void addToTable(QTableWidget *table, size_t row, bool showImages,
|
||||
size_t maxImages, int imgHeight = 100,
|
||||
int imgWidth = 100);
|
||||
|
||||
/**
|
||||
* @brief Resizes the images in the given row.
|
||||
* Make sure to call this after (!) you called addToTable() with the
|
||||
* same row parameter on this object some time.
|
||||
* @param table given table
|
||||
* @param row row index at which the data will be shown
|
||||
* @param showImages does the table show images?
|
||||
* @param maxImages the maximum number of images the table shows
|
||||
* @param imgHeight height of the shown images
|
||||
* @param imgWidth width of the shown images
|
||||
*/
|
||||
void resizeInTable(QTableWidget *table, size_t row, bool showImages,
|
||||
size_t maxImages, int imgHeight = 100,
|
||||
int imgWidth = 100);
|
||||
|
||||
/**
|
||||
* @brief Get the inherited call.
|
||||
* @return the inherited call
|
||||
*/
|
||||
util::Reference<const impl::Call> call() const
|
||||
{
|
||||
return call_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the description of the inherited call.
|
||||
* @return description of the inherited call.
|
||||
*/
|
||||
QString description() const
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the id of the inherited call.
|
||||
* @return id of the inherited call.
|
||||
*/
|
||||
size_t id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the function name property of the inherited call.
|
||||
* @return function name property of the inherited call.
|
||||
*/
|
||||
QString function() const
|
||||
{
|
||||
return functionStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the file name of the inherited call.
|
||||
* @return file name of the inherited call.
|
||||
*/
|
||||
QString file() const
|
||||
{
|
||||
return fileStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the line property of the inherited call.
|
||||
* @return line property of the inherited call.
|
||||
*/
|
||||
size_t line() const
|
||||
{
|
||||
return line_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the type of the inherited call.
|
||||
* @return type of the inherited call.
|
||||
*/
|
||||
QString type() const
|
||||
{
|
||||
return typeStr;
|
||||
}
|
||||
|
||||
private:
|
||||
util::Reference<const impl::Call> call_;
|
||||
size_t id_ = 0;
|
||||
size_t line_ = 0;
|
||||
QString idStr = "";
|
||||
QString description_ = "";
|
||||
std::vector<QPixmap> imgs{};
|
||||
QString functionStr = "";
|
||||
QString fileStr = "";
|
||||
QString lineStr = "";
|
||||
QString typeStr = "";
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,301 @@
|
||||
#include "rawview_group_subtable.hpp"
|
||||
|
||||
#include <utility>
|
||||
#include <algorithm>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QStringList>
|
||||
#include <QModelIndexList>
|
||||
#include <QModelIndex>
|
||||
#include <QMenu>
|
||||
#include <QAction>
|
||||
#include <QHeaderView>
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QTableWidgetSelectionRange>
|
||||
|
||||
#include "call_window.hpp"
|
||||
#include "../view/rawview.hpp"
|
||||
#include "rawview_table.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
RawviewGroupSubtable::RawviewGroupSubtable(
|
||||
RawviewTable *parent, stfl::ElementGroup<RawviewTableRow> group)
|
||||
: parent{ parent }, group{ std::move(group) }
|
||||
{
|
||||
qTable = new QTableWidget(this);
|
||||
qTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||
qTable->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||
auto horizontalHeader = qTable->horizontalHeader();
|
||||
horizontalHeader->setSectionResizeMode(QHeaderView::Stretch);
|
||||
horizontalHeader->setStretchLastSection(false);
|
||||
qTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
connect(qTable, SIGNAL(customContextMenuRequested(QPoint)), this,
|
||||
SLOT(customMenuRequested(QPoint)));
|
||||
connect(qTable, SIGNAL(itemSelectionChanged()), this,
|
||||
SLOT(selectionChanged()));
|
||||
|
||||
auto *layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(qTable);
|
||||
setLayout(layout);
|
||||
|
||||
QStringList list{};
|
||||
if (group.getTitles().contains("single key point"))
|
||||
{
|
||||
list << "x"
|
||||
<< "y"
|
||||
<< "size"
|
||||
<< "angle"
|
||||
<< "response"
|
||||
<< "octave"
|
||||
<< "class id"
|
||||
<< "img number";
|
||||
}
|
||||
else
|
||||
{
|
||||
list << "match distance"
|
||||
<< "img idx"
|
||||
<< "query idx"
|
||||
<< "train idx";
|
||||
list << "key point 1 x"
|
||||
<< "y 1"
|
||||
<< "size 1"
|
||||
<< "angle 1"
|
||||
<< "response 1"
|
||||
<< "octave 1"
|
||||
<< "class id 1";
|
||||
list << "key point 2 x"
|
||||
<< "y 2"
|
||||
<< "size 2"
|
||||
<< "angle 2"
|
||||
<< "response 2"
|
||||
<< "octave 2"
|
||||
<< "class id 2";
|
||||
}
|
||||
qTable->setColumnCount(list.size());
|
||||
qTable->setHorizontalHeaderLabels(list);
|
||||
updateUI();
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::updateUI()
|
||||
{
|
||||
qTable->setRowCount(group.size());
|
||||
for (size_t i = 0; i < group.size(); i++)
|
||||
{
|
||||
group.get(i).addToTable(qTable, i);
|
||||
}
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::selectionChanged()
|
||||
{
|
||||
QModelIndexList indexList = qTable->selectionModel()->selectedIndexes();
|
||||
currentRowIndexes.clear();
|
||||
for (QModelIndex index : indexList)
|
||||
{
|
||||
if (index.isValid())
|
||||
{
|
||||
auto row = index.row();
|
||||
if (row < qTable->rowCount() && row >= 0)
|
||||
{
|
||||
currentRowIndexes.insert(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::customMenuRequested(QPoint location)
|
||||
{
|
||||
QModelIndex index = qTable->indexAt(location);
|
||||
if (!index.isValid())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QMenu *menu = new QMenu(this);
|
||||
connect(menu, SIGNAL(triggered(QAction *)), this,
|
||||
SLOT(customMenuAction(QAction *)));
|
||||
|
||||
if (parent->getParent()->doesShowShowInViewMenu())
|
||||
{
|
||||
menu->addAction(new QAction("Show selected rows in view", this));
|
||||
}
|
||||
|
||||
auto formats = RawviewTableRow::getAvailableTextFormats();
|
||||
for (auto format : formats)
|
||||
{
|
||||
menu->addAction(
|
||||
new QAction(QString("Copy as %1").arg(format), this));
|
||||
}
|
||||
int row = index.row();
|
||||
if (currentRowIndexes.size() == 0)
|
||||
{
|
||||
currentRowIndexes = { row };
|
||||
}
|
||||
menu->popup(qTable->viewport()->mapToGlobal(location));
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::customMenuAction(QAction *action)
|
||||
{
|
||||
bool single = group.getTitles().contains("single key point");
|
||||
if (currentRowIndexes.size() > 0)
|
||||
{
|
||||
auto rows = getSelectedRows();
|
||||
QString text = action->text();
|
||||
if (text == "Show selected rows in view")
|
||||
{
|
||||
if (single)
|
||||
{
|
||||
std::vector<cv::KeyPoint> keyPoints;
|
||||
for (auto row : rows)
|
||||
{
|
||||
keyPoints.push_back(row.getKeyPoint1());
|
||||
}
|
||||
parent->getParent()->keyPointsSelected(
|
||||
keyPoints);
|
||||
std::vector<cv::DMatch> emptyVec{};
|
||||
parent->getParent()->matchesSelected(emptyVec); //unselect matches
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<cv::DMatch> matches;
|
||||
for (auto row : rows)
|
||||
{
|
||||
matches.push_back(row.getMatch());
|
||||
}
|
||||
parent->getParent()->matchesKeyPointsSelected(matches);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto formats =
|
||||
RawviewTableRow::getAvailableTextFormats();
|
||||
for (auto format : formats)
|
||||
{
|
||||
if (text == QString("Copy as %1").arg(format))
|
||||
{
|
||||
QString formattedRows;
|
||||
formattedRows =
|
||||
RawviewTableRow::rowsToText(
|
||||
rows, format, single);
|
||||
QApplication::clipboard()->setText(
|
||||
formattedRows);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cv::DMatch> RawviewGroupSubtable::getMatchSelection()
|
||||
{
|
||||
std::vector<cv::DMatch> matches;
|
||||
for (RawviewTableRow row : getSelectedRows())
|
||||
{
|
||||
if (!row.hasSingleKeyPoint())
|
||||
{
|
||||
matches.push_back(row.getMatch());
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
std::vector<cv::KeyPoint> RawviewGroupSubtable::getKeyPointSelection()
|
||||
{
|
||||
std::vector<cv::KeyPoint> keyPoints;
|
||||
for (RawviewTableRow row : getSelectedRows())
|
||||
{
|
||||
if (row.hasSingleKeyPoint())
|
||||
{
|
||||
keyPoints.push_back(row.getKeyPoint1());
|
||||
}
|
||||
}
|
||||
return keyPoints;
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::setMatchSelection(std::vector<cv::DMatch> matches)
|
||||
{
|
||||
std::set<int> indexes;
|
||||
for (size_t i = 0; i < group.size(); i++)
|
||||
{
|
||||
RawviewTableRow elem = group.get(i);
|
||||
if (!elem.hasSingleKeyPoint())
|
||||
{
|
||||
for (auto &match : matches)
|
||||
{
|
||||
if (match.distance == elem.matchDistance() &&
|
||||
match.imgIdx == elem.matchImgIdx() &&
|
||||
match.queryIdx == elem.matchQueryIdx() &&
|
||||
match.trainIdx == elem.matchTrainIdx())
|
||||
{
|
||||
indexes.insert(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setSelectedRows(indexes);
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::setKeyPointSelection(std::vector<cv::KeyPoint> keyPoints)
|
||||
{
|
||||
std::set<int> indexes;
|
||||
for (size_t i = 0; i < group.size(); i++)
|
||||
{
|
||||
RawviewTableRow elem = group.get(i);
|
||||
if (elem.hasSingleKeyPoint())
|
||||
{
|
||||
for (auto &keyPoint : keyPoints)
|
||||
{
|
||||
if (keyPoint.pt.x == elem.keyPoint1XCoord() &&
|
||||
keyPoint.pt.y == elem.keyPoint1YCoord() &&
|
||||
keyPoint.size == elem.keyPoint1Size() &&
|
||||
keyPoint.angle == elem.keyPoint1Angle() &&
|
||||
keyPoint.response == elem.keyPoint1Response() &&
|
||||
keyPoint.octave == elem.keyPoint1Octave() &&
|
||||
keyPoint.class_id == elem.keyPoint1ClassId())
|
||||
{
|
||||
indexes.insert(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
setSelectedRows(indexes);
|
||||
}
|
||||
|
||||
std::vector<RawviewTableRow> RawviewGroupSubtable::getSelectedRows()
|
||||
{
|
||||
std::vector<RawviewTableRow> rows;
|
||||
for (auto index : currentRowIndexes)
|
||||
{
|
||||
if (index < qTable->rowCount() && index >= 0)
|
||||
{
|
||||
rows.push_back(group.get(index));
|
||||
}
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
void RawviewGroupSubtable::setSelectedRows(std::set<int> rowIndexes)
|
||||
{
|
||||
currentRowIndexes = rowIndexes;
|
||||
QTableWidgetSelectionRange clearSelectionRange(0, 0, qTable->rowCount(), qTable->columnCount());
|
||||
qTable->setRangeSelected(clearSelectionRange, false);
|
||||
for (int i : rowIndexes)
|
||||
{
|
||||
QTableWidgetSelectionRange range(i, 0, i + 1, qTable->columnCount());
|
||||
qTable->setRangeSelected(range, true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef CVVISUAL_RAWVIEW_GROUP_SUBTABLE_HPP
|
||||
#define CVVISUAL_RAWVIEW_GROUP_SUBTABLE_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QTableWidget>
|
||||
#include <QAction>
|
||||
#include <QItemSelection>
|
||||
|
||||
#include "../stfl/element_group.hpp"
|
||||
#include "rawview_table_row.hpp"
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace controller
|
||||
{
|
||||
class ViewController;
|
||||
}
|
||||
}
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class RawviewTable;
|
||||
|
||||
/**
|
||||
* @brief A table for the a group of overview data sets.
|
||||
*/
|
||||
class RawviewGroupSubtable : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs an over group subtable.
|
||||
* @param controller view controller
|
||||
* @param parent parent table
|
||||
* @param group the displayed group of overview data sets
|
||||
*/
|
||||
RawviewGroupSubtable(RawviewTable *parent,
|
||||
stfl::ElementGroup<RawviewTableRow> group);
|
||||
|
||||
/**
|
||||
* @brief Updates the displayed table UI.
|
||||
*/
|
||||
void updateUI();
|
||||
|
||||
std::vector<cv::DMatch> getMatchSelection();
|
||||
|
||||
std::vector<cv::KeyPoint> getKeyPointSelection();
|
||||
|
||||
public slots:
|
||||
|
||||
void setMatchSelection(std::vector<cv::DMatch> matches);
|
||||
|
||||
void setKeyPointSelection(std::vector<cv::KeyPoint> keyPoints);
|
||||
|
||||
private slots:
|
||||
|
||||
void customMenuRequested(QPoint location);
|
||||
void customMenuAction(QAction *action);
|
||||
void selectionChanged();
|
||||
|
||||
private:
|
||||
RawviewTable *parent;
|
||||
stfl::ElementGroup<RawviewTableRow> group;
|
||||
QTableWidget *qTable;
|
||||
std::set<int> currentRowIndexes;
|
||||
|
||||
std::vector<RawviewTableRow> getSelectedRows();
|
||||
|
||||
void setSelectedRows(std::set<int> rowIndexes);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,99 @@
|
||||
#include "rawview_table.hpp"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QStringList>
|
||||
|
||||
#include "../stfl/element_group.hpp"
|
||||
#include "rawview_table_row.hpp"
|
||||
#include "rawview_group_subtable.hpp"
|
||||
#include "../qtutil/accordion.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
RawviewTable::RawviewTable(view::Rawview *parent) : parent{ parent }
|
||||
{
|
||||
subtableAccordion = new qtutil::Accordion{};
|
||||
auto *layout = new QVBoxLayout{};
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout->addWidget(subtableAccordion);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void RawviewTable::updateRowGroups(
|
||||
std::vector<stfl::ElementGroup<RawviewTableRow>> newGroups)
|
||||
{
|
||||
subtableAccordion->clear();
|
||||
subTables.clear();
|
||||
for (auto &group : newGroups)
|
||||
{
|
||||
if (group.size() > 0)
|
||||
{
|
||||
auto subtable = util::make_unique<RawviewGroupSubtable>(
|
||||
this, std::move(group));
|
||||
auto subtablePtr = subtable.get();
|
||||
auto titles = group.getTitles();
|
||||
QString title =
|
||||
"No grouping specified, use #group to do specify";
|
||||
if (titles.size() != 0)
|
||||
{
|
||||
title = titles.join(", ");
|
||||
}
|
||||
subtableAccordion->push_back(title, std::move(subtable),
|
||||
false);
|
||||
subTables.push_back(subtablePtr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RawviewTable::updateUI()
|
||||
{
|
||||
for (auto *subTable : subTables)
|
||||
{
|
||||
subTable->updateUI();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<cv::DMatch> RawviewTable::getMatchSelection()
|
||||
{
|
||||
std::vector<cv::DMatch> matches;
|
||||
for (auto subTable : subTables)
|
||||
{
|
||||
auto subMatches = subTable->getMatchSelection();
|
||||
matches.insert(matches.end(), subMatches.begin(), subMatches.end());
|
||||
}
|
||||
return matches;
|
||||
}
|
||||
|
||||
std::vector<cv::KeyPoint> RawviewTable::getKeyPointSelection()
|
||||
{
|
||||
std::vector<cv::KeyPoint> keyPoints;
|
||||
for (auto subTable : subTables)
|
||||
{
|
||||
auto subKeyPoints = subTable->getKeyPointSelection();
|
||||
keyPoints.insert(keyPoints.end(), subKeyPoints.begin(), subKeyPoints.end());
|
||||
}
|
||||
return keyPoints;
|
||||
}
|
||||
|
||||
void RawviewTable::setMatchSelection(std::vector<cv::DMatch> matches)
|
||||
{
|
||||
for (auto subTable : subTables)
|
||||
{
|
||||
subTable->setMatchSelection(matches);
|
||||
}
|
||||
}
|
||||
|
||||
void RawviewTable::setKeyPointSelection(std::vector<cv::KeyPoint> keyPoints)
|
||||
{
|
||||
for (auto subTable : subTables)
|
||||
{
|
||||
subTable->setKeyPointSelection(keyPoints);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
#ifndef CVVISUAL_RAWVIEWTABLE_HPP
|
||||
#define CVVISUAL_RAWVIEWTABLE_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QList>
|
||||
|
||||
#include "../view/rawview.hpp"
|
||||
#include "rawview_table_row.hpp"
|
||||
#include "../stfl/element_group.hpp"
|
||||
#include "../qtutil/accordion.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "rawview_group_subtable.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
|
||||
namespace view
|
||||
{
|
||||
class Rawview;
|
||||
}
|
||||
|
||||
namespace gui
|
||||
{
|
||||
|
||||
class RawviewTableCollumn;
|
||||
|
||||
/**
|
||||
* @brief A table (consisting of subtables) displaying raw match data.
|
||||
*/
|
||||
class RawviewTable : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor of this class.
|
||||
* @param parent parent view
|
||||
*/
|
||||
RawviewTable(view::Rawview *parent);
|
||||
|
||||
/**
|
||||
* @brief Update the inherited groups of rows and rebuild the UI fully.
|
||||
* @param newGroups new groups for this table
|
||||
*/
|
||||
void updateRowGroups(
|
||||
const std::vector<stfl::ElementGroup<RawviewTableRow>> newGroups);
|
||||
|
||||
/**
|
||||
* @brief Updates the UI
|
||||
*/
|
||||
void updateUI();
|
||||
|
||||
/**
|
||||
* @brief Returns the parent view.
|
||||
* @return parent view
|
||||
*/
|
||||
view::Rawview *getParent()
|
||||
{
|
||||
return parent;
|
||||
}
|
||||
|
||||
std::vector<cv::DMatch> getMatchSelection();
|
||||
|
||||
std::vector<cv::KeyPoint> getKeyPointSelection();
|
||||
|
||||
public slots:
|
||||
|
||||
void setMatchSelection(std::vector<cv::DMatch> matches);
|
||||
|
||||
void setKeyPointSelection(std::vector<cv::KeyPoint> keyPoints);
|
||||
|
||||
private:
|
||||
view::Rawview *parent;
|
||||
qtutil::Accordion *subtableAccordion;
|
||||
std::vector<RawviewGroupSubtable *> subTables{};
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,325 @@
|
||||
#include "rawview_table_row.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#include <QTableWidgetItem>
|
||||
#include <QImage>
|
||||
|
||||
#include "../qtutil/util.hpp"
|
||||
#include "../stfl/stringutils.hpp"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
RawviewTableRow::RawviewTableRow(cv::DMatch match, cv::KeyPoint keyPoint1,
|
||||
cv::KeyPoint keyPoint2)
|
||||
: match{ match }, keyPoint1{ keyPoint1 }, keyPoint2{ keyPoint2 },
|
||||
hasLonelyKeyPoint_{ false }
|
||||
{
|
||||
}
|
||||
|
||||
RawviewTableRow::RawviewTableRow(cv::KeyPoint keyPoint, bool left)
|
||||
: keyPoint1{ keyPoint }, hasLonelyKeyPoint_{ true }, left{ left }
|
||||
{
|
||||
}
|
||||
|
||||
void RawviewTableRow::addToTable(QTableWidget *table, size_t row)
|
||||
{
|
||||
std::vector<QString> items;
|
||||
if (!hasLonelyKeyPoint_)
|
||||
{
|
||||
items = { QString::number(matchDistance()),
|
||||
QString::number(matchImgIdx()),
|
||||
QString::number(matchQueryIdx()),
|
||||
QString::number(matchTrainIdx()),
|
||||
QString::number(keyPoint1XCoord()),
|
||||
QString::number(keyPoint1YCoord()),
|
||||
QString::number(keyPoint1Size()),
|
||||
QString::number(keyPoint1Angle()),
|
||||
QString::number(keyPoint1Response()),
|
||||
QString::number(keyPoint1Octave()),
|
||||
QString::number(keyPoint1ClassId()),
|
||||
QString::number(keyPoint2XCoord()),
|
||||
QString::number(keyPoint2YCoord()),
|
||||
QString::number(keyPoint2Size()),
|
||||
QString::number(keyPoint2Angle()),
|
||||
QString::number(keyPoint2Response()),
|
||||
QString::number(keyPoint2Octave()),
|
||||
QString::number(keyPoint2ClassId()) };
|
||||
}
|
||||
else
|
||||
{
|
||||
items = { QString::number(keyPoint1XCoord()),
|
||||
QString::number(keyPoint1YCoord()),
|
||||
QString::number(keyPoint1Size()),
|
||||
QString::number(keyPoint1Angle()),
|
||||
QString::number(keyPoint1Response()),
|
||||
QString::number(keyPoint1Octave()),
|
||||
QString::number(keyPoint1ClassId()),
|
||||
QString::number(left ? 1 : 2) };
|
||||
}
|
||||
for (size_t i = 0; i < items.size(); i++)
|
||||
{
|
||||
auto *item = new QTableWidgetItem(items[i]);
|
||||
item->setFlags(item->flags() ^ Qt::ItemIsEditable);
|
||||
table->setItem(row, i, item);
|
||||
}
|
||||
}
|
||||
|
||||
QString RawviewTableRow::rowsToText(const std::vector<RawviewTableRow> &rows,
|
||||
const QString format,
|
||||
bool singleKeyPointRows)
|
||||
{
|
||||
QStringList lines;
|
||||
// header
|
||||
if (format == "CSV")
|
||||
{
|
||||
QStringList header;
|
||||
if (singleKeyPointRows)
|
||||
{
|
||||
header << "x"
|
||||
<< "y"
|
||||
<< "size"
|
||||
<< "angle"
|
||||
<< "response"
|
||||
<< "octave"
|
||||
<< "class id";
|
||||
}
|
||||
else
|
||||
{
|
||||
header << "match distance"
|
||||
<< "img idx"
|
||||
<< "query idx"
|
||||
<< "train idx"
|
||||
<< "key point 1 x"
|
||||
<< "y 1"
|
||||
<< "size 1"
|
||||
<< "angle 1"
|
||||
<< "response 1"
|
||||
<< "octave 1"
|
||||
<< "class id 1"
|
||||
<< "key point 2 x"
|
||||
<< "y 2"
|
||||
<< "size 2"
|
||||
<< "angle 2"
|
||||
<< "response 2"
|
||||
<< "octave 2"
|
||||
<< "class id 2";
|
||||
}
|
||||
lines << header.join(",");
|
||||
}
|
||||
// These functions are simple macros
|
||||
auto intstr = [](int number)
|
||||
{ return QString::number(number); };
|
||||
auto floatstr = [](float number)
|
||||
{ return QString::number(number); };
|
||||
// combine a name and a value string
|
||||
auto combinestr = [format](QString name, QString value)
|
||||
{
|
||||
QString fmtStr = "";
|
||||
if (format == "JSON")
|
||||
{
|
||||
fmtStr = "\"%1\": %2";
|
||||
}
|
||||
else if (format == "PYTHON")
|
||||
{
|
||||
fmtStr = "'%1': %2";
|
||||
}
|
||||
else if (format == "RUBY")
|
||||
{
|
||||
fmtStr = "\"%1\" => %2";
|
||||
}
|
||||
return fmtStr.arg(name, value);
|
||||
};
|
||||
// data sets
|
||||
for (auto &row : rows)
|
||||
{
|
||||
if (format == "CSV" && !singleKeyPointRows)
|
||||
{
|
||||
QStringList line;
|
||||
line << floatstr(row.matchDistance())
|
||||
<< intstr(row.matchImgIdx())
|
||||
<< intstr(row.matchQueryIdx())
|
||||
<< intstr(row.matchTrainIdx())
|
||||
<< floatstr(row.keyPoint1XCoord())
|
||||
<< floatstr(row.keyPoint1YCoord())
|
||||
<< floatstr(row.keyPoint1Size())
|
||||
<< floatstr(row.keyPoint1Angle())
|
||||
<< floatstr(row.keyPoint1Response())
|
||||
<< intstr(row.keyPoint1Octave())
|
||||
<< floatstr(row.keyPoint1ClassId())
|
||||
<< floatstr(row.keyPoint2XCoord())
|
||||
<< floatstr(row.keyPoint2YCoord())
|
||||
<< floatstr(row.keyPoint2Size())
|
||||
<< floatstr(row.keyPoint2Angle())
|
||||
<< floatstr(row.keyPoint2Response())
|
||||
<< intstr(row.keyPoint2Octave())
|
||||
<< floatstr(row.keyPoint2ClassId());
|
||||
lines << line.join(",");
|
||||
}
|
||||
else if ((format == "JSON" || format == "PYTHON" ||
|
||||
format == "RUBY") &&
|
||||
!singleKeyPointRows)
|
||||
{
|
||||
QStringList match;
|
||||
match << combinestr("match distance",
|
||||
floatstr(row.matchDistance()))
|
||||
<< combinestr("img idx",
|
||||
intstr(row.matchImgIdx()))
|
||||
<< combinestr("query idx",
|
||||
intstr(row.matchQueryIdx()))
|
||||
<< combinestr("train idx",
|
||||
intstr(row.matchTrainIdx()));
|
||||
QStringList keyPoint1;
|
||||
keyPoint1
|
||||
<< combinestr("x", floatstr(row.keyPoint1XCoord()))
|
||||
<< combinestr("y", floatstr(row.keyPoint1YCoord()))
|
||||
<< combinestr("size", floatstr(row.keyPoint1Size()))
|
||||
<< combinestr("angle",
|
||||
floatstr(row.keyPoint1Angle()))
|
||||
<< combinestr("response",
|
||||
floatstr(row.keyPoint1Response()))
|
||||
<< combinestr("octave",
|
||||
intstr(row.keyPoint1Octave()))
|
||||
<< combinestr("class id",
|
||||
floatstr(row.keyPoint1ClassId()));
|
||||
QStringList keyPoint2;
|
||||
keyPoint2
|
||||
<< combinestr("x", floatstr(row.keyPoint2XCoord()))
|
||||
<< combinestr("y", floatstr(row.keyPoint2YCoord()))
|
||||
<< combinestr("size", floatstr(row.keyPoint2Size()))
|
||||
<< combinestr("angle",
|
||||
floatstr(row.keyPoint2Angle()))
|
||||
<< combinestr("response",
|
||||
floatstr(row.keyPoint2Response()))
|
||||
<< combinestr("octave",
|
||||
intstr(row.keyPoint2Octave()))
|
||||
<< combinestr("class id",
|
||||
floatstr(row.keyPoint2ClassId()));
|
||||
lines << QString("{%1, %2, %3}").arg(
|
||||
combinestr("match", QString("{%1}").arg(
|
||||
match.join(", "))),
|
||||
combinestr("key point 1",
|
||||
QString("{%1}")
|
||||
.arg(keyPoint1.join(", "))),
|
||||
combinestr("key point 2",
|
||||
QString("{%1}").arg(
|
||||
keyPoint2.join(", "))));
|
||||
}
|
||||
if (format == "CSV" && singleKeyPointRows)
|
||||
{
|
||||
QStringList line;
|
||||
line << floatstr(row.keyPoint1XCoord())
|
||||
<< floatstr(row.keyPoint1YCoord())
|
||||
<< floatstr(row.keyPoint1Size())
|
||||
<< floatstr(row.keyPoint1Angle())
|
||||
<< floatstr(row.keyPoint1Response())
|
||||
<< intstr(row.keyPoint1Octave())
|
||||
<< floatstr(row.keyPoint1ClassId())
|
||||
<< intstr(row.isLeftSingleKeyPoint() ? 1 : 2);
|
||||
lines << line.join(",");
|
||||
}
|
||||
else if ((format == "JSON" || format == "PYTHON" ||
|
||||
format == "RUBY") &&
|
||||
singleKeyPointRows)
|
||||
{
|
||||
QStringList keyPoint;
|
||||
keyPoint
|
||||
<< combinestr("x", floatstr(row.keyPoint1XCoord()))
|
||||
<< combinestr("y", floatstr(row.keyPoint1YCoord()))
|
||||
<< combinestr("size", floatstr(row.keyPoint1Size()))
|
||||
<< combinestr("angle",
|
||||
floatstr(row.keyPoint1Angle()))
|
||||
<< combinestr("response",
|
||||
floatstr(row.keyPoint1Response()))
|
||||
<< combinestr("octave",
|
||||
intstr(row.keyPoint1Octave()))
|
||||
<< combinestr("class id",
|
||||
floatstr(row.keyPoint1ClassId()))
|
||||
<< combinestr(
|
||||
"img number",
|
||||
intstr(row.isLeftSingleKeyPoint() ? 1 : 2));
|
||||
lines << QString("{%1}").arg(keyPoint.join(", "));
|
||||
}
|
||||
}
|
||||
QString text = "";
|
||||
// join the data sets with the header to a single block of text
|
||||
if (format == "CSV")
|
||||
{
|
||||
text = lines.join("\r\n"); // see RFC 4180
|
||||
}
|
||||
else if (format == "JSON" || format == "PYTHON" || format == "RUBY")
|
||||
{
|
||||
text = QString("[%1]").arg(lines.join(",\r\n"));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
std::vector<QString> RawviewTableRow::getAvailableTextFormats()
|
||||
{
|
||||
return { QString("CSV"), QString("JSON"),
|
||||
QString("PYTHON"), QString("RUBY") };
|
||||
}
|
||||
|
||||
std::pair<QList<RawviewTableRow>, QList<RawviewTableRow>>
|
||||
createRawviewTableRows(const std::vector<cv::KeyPoint> &keyPoints1,
|
||||
const std::vector<cv::KeyPoint> &keyPoints2,
|
||||
const std::vector<cv::DMatch> &matches,
|
||||
bool usesTrainDescriptor)
|
||||
{
|
||||
QList<RawviewTableRow> matchRowList;
|
||||
QList<RawviewTableRow> singleRowList;
|
||||
std::set<size_t> usedKeyPoints1;
|
||||
std::set<size_t> usedKeyPoints2;
|
||||
for (auto &match : matches)
|
||||
{
|
||||
int leftIndex = match.queryIdx;
|
||||
int rightIndex = usesTrainDescriptor ? match.trainIdx : match.imgIdx;
|
||||
if (leftIndex >= (int)keyPoints1.size() || rightIndex >= (int)keyPoints2.size())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
usedKeyPoints1.insert(match.queryIdx);
|
||||
usedKeyPoints2.insert(match.trainIdx);
|
||||
matchRowList.append(
|
||||
RawviewTableRow(match, keyPoints1.at(leftIndex),
|
||||
keyPoints2.at(rightIndex)));
|
||||
}
|
||||
for (size_t i = 0; i < usedKeyPoints1.size(); i++)
|
||||
{
|
||||
if (usedKeyPoints1.find(i) != usedKeyPoints1.end())
|
||||
{
|
||||
singleRowList.push_back(
|
||||
RawviewTableRow(keyPoints1.at(i), true));
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < usedKeyPoints2.size(); i++)
|
||||
{
|
||||
if (usedKeyPoints2.find(i) != usedKeyPoints2.end())
|
||||
{
|
||||
singleRowList.push_back(
|
||||
RawviewTableRow(keyPoints2.at(i), false));
|
||||
}
|
||||
}
|
||||
return std::make_pair(matchRowList, singleRowList);
|
||||
}
|
||||
|
||||
QList<RawviewTableRow>
|
||||
createSingleKeyPointRawviewTableRows(const std::vector<cv::KeyPoint> &keyPoints,
|
||||
bool left)
|
||||
{
|
||||
auto retList = QList<RawviewTableRow>();
|
||||
for (auto &keyPoint : keyPoints)
|
||||
{
|
||||
retList.append(RawviewTableRow(keyPoint, left));
|
||||
}
|
||||
return retList;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
#ifndef CVVISUAL_RAWVIEWTABLEROW_HPP
|
||||
#define CVVISUAL_RAWVIEWTABLEROW_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/features.hpp>
|
||||
|
||||
#include <QTableWidget>
|
||||
#include <QString>
|
||||
#include <QList>
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A simple container wrapper for the cv::DMatch and cv::KeyPoint class.
|
||||
* See the opencv documentation for more information on the getter methods.
|
||||
*/
|
||||
class RawviewTableRow
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor of this class.
|
||||
* @param match match that this row inherits.
|
||||
* @param keyPoint1 "left" key point of the match
|
||||
* @param keyPoint2 "right" key point of the match
|
||||
*/
|
||||
RawviewTableRow(cv::DMatch match, cv::KeyPoint keyPoint1,
|
||||
cv::KeyPoint keyPoint2);
|
||||
|
||||
/**
|
||||
* @brief Constructor of this class for a single key point.
|
||||
* The keypoint is stored as the first key point.
|
||||
* @param keyPoint only key point of this object,
|
||||
* @param left is the given key point is a left one?
|
||||
*/
|
||||
RawviewTableRow(cv::KeyPoint keyPoint, bool left = true);
|
||||
|
||||
/**
|
||||
* @brief Add this row to the given table.
|
||||
* @note It does only fills the row in the table with the given index
|
||||
* with its data.
|
||||
* @param table given table
|
||||
* @param row given row index
|
||||
*/
|
||||
void addToTable(QTableWidget *table, size_t row);
|
||||
|
||||
float matchDistance() const
|
||||
{
|
||||
return match.distance;
|
||||
}
|
||||
|
||||
int matchImgIdx() const
|
||||
{
|
||||
return match.imgIdx;
|
||||
}
|
||||
|
||||
int matchQueryIdx() const
|
||||
{
|
||||
return match.queryIdx;
|
||||
}
|
||||
|
||||
int matchTrainIdx() const
|
||||
{
|
||||
return match.trainIdx;
|
||||
}
|
||||
|
||||
float keyPoint1XCoord() const
|
||||
{
|
||||
return keyPoint1.pt.x;
|
||||
}
|
||||
|
||||
float keyPoint1YCoord() const
|
||||
{
|
||||
return keyPoint1.pt.y;
|
||||
}
|
||||
|
||||
cv::Point2f keyPoint1Coords() const
|
||||
{
|
||||
return keyPoint1.pt;
|
||||
}
|
||||
|
||||
float keyPoint1Size() const
|
||||
{
|
||||
return keyPoint1.size;
|
||||
}
|
||||
|
||||
float keyPoint1Angle() const
|
||||
{
|
||||
return keyPoint1.angle;
|
||||
}
|
||||
|
||||
float keyPoint1Response() const
|
||||
{
|
||||
return keyPoint1.response;
|
||||
}
|
||||
|
||||
int keyPoint1Octave() const
|
||||
{
|
||||
return keyPoint1.octave;
|
||||
}
|
||||
|
||||
int keyPoint1ClassId() const
|
||||
{
|
||||
return keyPoint1.class_id;
|
||||
}
|
||||
|
||||
float keyPoint2XCoord() const
|
||||
{
|
||||
return keyPoint2.pt.x;
|
||||
}
|
||||
|
||||
float keyPoint2YCoord() const
|
||||
{
|
||||
return keyPoint2.pt.y;
|
||||
}
|
||||
|
||||
cv::Point2f keyPoint2Coords() const
|
||||
{
|
||||
return keyPoint2.pt;
|
||||
}
|
||||
|
||||
float keyPoint2Size() const
|
||||
{
|
||||
return keyPoint2.size;
|
||||
}
|
||||
|
||||
float keyPoint2Angle() const
|
||||
{
|
||||
return keyPoint2.angle;
|
||||
}
|
||||
|
||||
float keyPoint2Response() const
|
||||
{
|
||||
return keyPoint2.response;
|
||||
}
|
||||
|
||||
int keyPoint2Octave() const
|
||||
{
|
||||
return keyPoint2.octave;
|
||||
}
|
||||
|
||||
int keyPoint2ClassId() const
|
||||
{
|
||||
return keyPoint2.class_id;
|
||||
}
|
||||
|
||||
cv::DMatch getMatch() const
|
||||
{
|
||||
return match;
|
||||
}
|
||||
|
||||
cv::KeyPoint getKeyPoint1() const
|
||||
{
|
||||
return keyPoint1;
|
||||
}
|
||||
|
||||
cv::KeyPoint getKeyPoint2() const
|
||||
{
|
||||
return keyPoint2;
|
||||
}
|
||||
|
||||
bool hasSingleKeyPoint() const
|
||||
{
|
||||
return hasLonelyKeyPoint_;
|
||||
}
|
||||
|
||||
bool isLeftSingleKeyPoint() const
|
||||
{
|
||||
return left;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serealizes the given rows into a single block of text.
|
||||
* The currently supported formats are:
|
||||
* - `CSV` : Valid RFC 4180 CSV, with the same columns like the
|
||||
* table.
|
||||
* - `JSON` : Valid JSON (each row is an object consisting of three
|
||||
* sub objects:
|
||||
* `match`, `keypoint 1` and `keypoint 2`).
|
||||
* - `PYTHON`: Valid python code (see JSON).
|
||||
* - `RUBY` : Valid ruby code (see JSON).
|
||||
* @param rows given rows
|
||||
* @param format the format of the resulting representation (see above)
|
||||
* @param singleKeyPointRows do the given rows consist of single key
|
||||
* point rows?
|
||||
* @return block representation of the given rows.
|
||||
*/
|
||||
static QString rowsToText(const std::vector<RawviewTableRow> &rows,
|
||||
const QString format,
|
||||
bool singleKeyPointRows = false);
|
||||
|
||||
/**
|
||||
* @brief Returns the currently available text formats for the
|
||||
* rowsToText method.
|
||||
* @return {"CSV", "JSON", "PYTHON", "RUBY"}
|
||||
*/
|
||||
static std::vector<QString> getAvailableTextFormats();
|
||||
|
||||
private:
|
||||
cv::DMatch match;
|
||||
cv::KeyPoint keyPoint1;
|
||||
cv::KeyPoint keyPoint2;
|
||||
bool hasLonelyKeyPoint_;
|
||||
bool left = false;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Create a list of rows from the given key points and matches.
|
||||
* And one for the single key points.
|
||||
*
|
||||
* It creates a row for each match and uses the key points to get the two
|
||||
* locations of each one..
|
||||
* @param keyPoints1 given "left" key points
|
||||
* @param keyPoints2 given "right" key points
|
||||
* @param matches given matches
|
||||
* @param usesTrainDescriptor Use the trainIdx property of each match to get the
|
||||
* "right" key points?
|
||||
* @return first element is the match row list, second is the single key point
|
||||
*row list
|
||||
*/
|
||||
std::pair<QList<RawviewTableRow>, QList<RawviewTableRow>>
|
||||
createRawviewTableRows(const std::vector<cv::KeyPoint> &keyPoints1,
|
||||
const std::vector<cv::KeyPoint> &keyPoints2,
|
||||
const std::vector<cv::DMatch> &matches,
|
||||
bool usesTrainDescriptor = true);
|
||||
|
||||
/**
|
||||
* @brief Create a list of rows from the given key points.
|
||||
* It creates a row for each key point, that does only contain this key point.
|
||||
* @param keyPoints given key points
|
||||
* @param left are the given key points are "left" ones?
|
||||
* @return resulting list
|
||||
*/
|
||||
QList<RawviewTableRow>
|
||||
createSingleKeyPointRawviewTableRows(const std::vector<cv::KeyPoint> &keyPoints,
|
||||
bool left = true);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,42 @@
|
||||
#ifndef CVVISUAL_TABWIDGET
|
||||
#define CVVISUAL_TABWIDGET
|
||||
|
||||
#include <QTabWidget>
|
||||
#include <QTabBar>
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace gui
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A simple to QTabWidget Subclass, enabling the access to protected
|
||||
* members.
|
||||
*/
|
||||
class TabWidget : public QTabWidget
|
||||
{
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor of this class.
|
||||
*/
|
||||
TabWidget(QWidget *parent) : QTabWidget(parent)
|
||||
{
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Returns the shown tab bar.
|
||||
* This method helps to access the member tabBar which has by default
|
||||
* only a protected setter.
|
||||
* @return shown tab bar.
|
||||
*/
|
||||
QTabBar *getTabBar() const
|
||||
{
|
||||
return tabBar();
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "call.hpp"
|
||||
#include <atomic>
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
size_t newCallId()
|
||||
{
|
||||
static std::atomic_size_t nextId (1);
|
||||
return nextId++;
|
||||
}
|
||||
|
||||
Call::Call() : metaData_{}, id{ newCallId() }, calltype{}
|
||||
{
|
||||
}
|
||||
|
||||
Call::Call(impl::CallMetaData callData, QString type, QString description,
|
||||
QString requestedView)
|
||||
: metaData_{ std::move(callData) }, id{ newCallId() },
|
||||
calltype{ std::move(type) }, description_{ std::move(description) },
|
||||
requestedView_{ std::move(requestedView) }
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
@@ -0,0 +1,113 @@
|
||||
#ifndef CVVISUAL_CALL_HPP
|
||||
#define CVVISUAL_CALL_HPP
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "opencv2/cvv/call_meta_data.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Returns a new, unique id for calls.
|
||||
*/
|
||||
size_t newCallId();
|
||||
|
||||
/**
|
||||
* @brief Baseclass for all calls. Provides access to the common functionality.
|
||||
*/
|
||||
class Call
|
||||
{
|
||||
public:
|
||||
virtual ~Call()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the unique id of the call.
|
||||
*/
|
||||
size_t getId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string that identifies what kind of call this is.
|
||||
*/
|
||||
const QString &type() const
|
||||
{
|
||||
return calltype;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the number of images that are part of the call.
|
||||
*/
|
||||
virtual size_t matrixCount() const = 0;
|
||||
|
||||
/**
|
||||
* Returns the n'th matrix that is involved in the call.
|
||||
*
|
||||
* @throws std::out_of_range if index is higher then matrixCount().
|
||||
*/
|
||||
virtual const cv::Mat &matrixAt(size_t index) const = 0;
|
||||
|
||||
/**
|
||||
* @brief provides a description of the call.
|
||||
*/
|
||||
const QString &description() const
|
||||
{
|
||||
return description_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a string which view was requested by the caller of the
|
||||
* API.
|
||||
*/
|
||||
const QString &requestedView() const
|
||||
{
|
||||
return requestedView_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Provides read-access to the meta-data of the call (from where
|
||||
* it came).
|
||||
*/
|
||||
const CallMetaData &metaData() const
|
||||
{
|
||||
return metaData_;
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Default-construcs a new Call with a new, unique ID.
|
||||
*/
|
||||
Call();
|
||||
|
||||
/**
|
||||
* @brief Construcs a new Call with a new, unique ID and the provided
|
||||
* data.
|
||||
*/
|
||||
Call(impl::CallMetaData callData, QString type, QString description,
|
||||
QString requestedView);
|
||||
|
||||
Call(const Call &) = default;
|
||||
Call(Call &&) = default;
|
||||
|
||||
impl::CallMetaData metaData_;
|
||||
size_t id;
|
||||
QString calltype;
|
||||
QString description_;
|
||||
QString requestedView_;
|
||||
};
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,116 @@
|
||||
#include "data_controller.hpp"
|
||||
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
class CallEquality
|
||||
{
|
||||
public:
|
||||
CallEquality(size_t Id) : Id{ Id }
|
||||
{
|
||||
}
|
||||
|
||||
bool operator()(const std::unique_ptr<Call> &call) const
|
||||
{
|
||||
return call->getId() == Id;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t Id;
|
||||
};
|
||||
}
|
||||
|
||||
void DataController::addCall(std::unique_ptr<Call> call)
|
||||
{
|
||||
auto ref = util::makeRef(*call);
|
||||
calls.push_back(std::move(call));
|
||||
viewController.addCall(ref);
|
||||
callUI();
|
||||
}
|
||||
|
||||
void DataController::removeCall(size_t Id)
|
||||
{
|
||||
auto it = std::find_if(calls.begin(), calls.end(), CallEquality{ Id });
|
||||
if (it == calls.end())
|
||||
{
|
||||
throw std::invalid_argument{ "there is no call with this id" };
|
||||
}
|
||||
calls.erase(it);
|
||||
}
|
||||
|
||||
const Call &DataController::getCall(size_t Id) const
|
||||
{
|
||||
auto it = std::find_if(calls.begin(), calls.end(), CallEquality{ Id });
|
||||
if (it == calls.end())
|
||||
{
|
||||
throw std::invalid_argument{ "there is no call with this id" };
|
||||
}
|
||||
return **it;
|
||||
}
|
||||
|
||||
Call &DataController::getCall(size_t Id)
|
||||
{
|
||||
auto it = std::find_if(calls.begin(), calls.end(), CallEquality{ Id });
|
||||
if (it == calls.end())
|
||||
{
|
||||
throw std::invalid_argument{ "there is no call with this id" };
|
||||
}
|
||||
return **it;
|
||||
}
|
||||
|
||||
bool DataController::hasCall(size_t Id)
|
||||
{
|
||||
auto it = std::find_if(calls.begin(), calls.end(), CallEquality{ Id });
|
||||
return it != calls.end();
|
||||
}
|
||||
|
||||
size_t DataController::numCalls() const
|
||||
{
|
||||
return calls.size();
|
||||
}
|
||||
|
||||
void DataController::callUI()
|
||||
{
|
||||
viewController.exec();
|
||||
}
|
||||
|
||||
void DataController::lastCall()
|
||||
{
|
||||
viewController.showExitProgramButton();
|
||||
callUI();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Actual implementation of the global DataController.
|
||||
*
|
||||
* This is required to be able to delete it.
|
||||
*/
|
||||
static std::unique_ptr<DataController> &realSingleton()
|
||||
{
|
||||
static std::unique_ptr<DataController> var = nullptr;
|
||||
return var;
|
||||
}
|
||||
|
||||
void deleteDataController()
|
||||
{
|
||||
realSingleton().reset();
|
||||
}
|
||||
|
||||
DataController &dataController()
|
||||
{
|
||||
auto& controller = realSingleton();
|
||||
if(!realSingleton().get())
|
||||
{
|
||||
controller = util::make_unique<DataController>();
|
||||
}
|
||||
return *controller;
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
@@ -0,0 +1,94 @@
|
||||
#ifndef CVVISUAL_DATA_CONTROLLER_HPP
|
||||
#define CVVISUAL_DATA_CONTROLLER_HPP
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include "call.hpp"
|
||||
#include "../controller/view_controller.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The central controller of the debug-framework that owns all the
|
||||
* calldata.
|
||||
*/
|
||||
class DataController
|
||||
{
|
||||
public:
|
||||
DataController() = default;
|
||||
~DataController()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new call to the calls-list.
|
||||
*/
|
||||
void addCall(std::unique_ptr<Call> call);
|
||||
|
||||
/**
|
||||
* Remove a call.
|
||||
* @throws std::invalid_argument if no such call exists
|
||||
*/
|
||||
void removeCall(size_t Id);
|
||||
|
||||
/**
|
||||
* Get read-access to a certain call.
|
||||
*/
|
||||
const Call &getCall(size_t Id) const;
|
||||
|
||||
/**
|
||||
* Get read/write-access to a certain call.
|
||||
*/
|
||||
Call &getCall(size_t Id);
|
||||
|
||||
bool hasCall(size_t Id);
|
||||
|
||||
/**
|
||||
* Get the number of currently managed calls.
|
||||
*/
|
||||
size_t numCalls() const;
|
||||
|
||||
/**
|
||||
* Passes control to the View-controller.
|
||||
*
|
||||
* Returns when the ViewController signals that normal program-execution
|
||||
*shall continue.
|
||||
*
|
||||
*/
|
||||
void callUI();
|
||||
|
||||
/**
|
||||
* @brief Replace the continue-buttons with close-buttons and show the
|
||||
*UI.
|
||||
*
|
||||
* This function is intended to be called directly before main returns
|
||||
*after all the actual
|
||||
* work is done.
|
||||
*/
|
||||
void lastCall();
|
||||
|
||||
private:
|
||||
std::vector<std::unique_ptr<Call>> calls;
|
||||
controller::ViewController viewController;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Destructs the global Singleton.
|
||||
*/
|
||||
void deleteDataController();
|
||||
|
||||
/**
|
||||
* Provides access to a global DataController that is created upon the first
|
||||
* call.
|
||||
*/
|
||||
DataController &dataController();
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,22 @@
|
||||
#include "opencv2/cvv/dmatch.hpp"
|
||||
|
||||
#include "opencv2/cvv/call_meta_data.hpp"
|
||||
#include "match_call.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
void debugDMatch(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, const CallMetaData &data,
|
||||
const char *description, const char *view,
|
||||
bool useTrainDescriptor)
|
||||
{
|
||||
debugMatchCall(img1, std::move(keypoints1), img2, std::move(keypoints2),
|
||||
std::move(matches), data, description, view,
|
||||
useTrainDescriptor);
|
||||
}
|
||||
}
|
||||
} // namespaces
|
||||
@@ -0,0 +1,18 @@
|
||||
#include "opencv2/cvv/filter.hpp"
|
||||
|
||||
#include "opencv2/cvv/call_meta_data.hpp"
|
||||
#include "filter_call.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
void debugFilter(cv::InputArray original, cv::InputArray result,
|
||||
const CallMetaData &data, const char *description,
|
||||
const char *view)
|
||||
{
|
||||
debugFilterCall(original, result, data, description, view, "filter");
|
||||
}
|
||||
}
|
||||
} // namespaces
|
||||
@@ -0,0 +1,45 @@
|
||||
#include "filter_call.hpp"
|
||||
|
||||
#include "data_controller.hpp"
|
||||
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
FilterCall::FilterCall(cv::InputArray in, cv::InputArray out,
|
||||
impl::CallMetaData data, QString type,
|
||||
QString description, QString requestedView)
|
||||
: Call( data, std::move(type),
|
||||
std::move(description), std::move(requestedView) ),
|
||||
input_{ in.getMat().clone() }, output_{ out.getMat().clone() }
|
||||
{
|
||||
}
|
||||
|
||||
const cv::Mat &FilterCall::matrixAt(size_t index) const
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return original();
|
||||
case 1:
|
||||
return result();
|
||||
default:
|
||||
throw std::out_of_range{ "" };
|
||||
}
|
||||
}
|
||||
|
||||
void debugFilterCall(cv::InputArray original, cv::InputArray result,
|
||||
const CallMetaData &data, const char *description,
|
||||
const char *view, const char *filter)
|
||||
{
|
||||
dataController().addCall(util::make_unique<FilterCall>(
|
||||
original, result, data, filter,
|
||||
description ? QString::fromLocal8Bit(description)
|
||||
: QString{ "<no description>" },
|
||||
view ? QString::fromLocal8Bit(view) : QString{}));
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
@@ -0,0 +1,66 @@
|
||||
#ifndef CVVISUAL_FILTER_CALL_HPP
|
||||
#define CVVISUAL_FILTER_CALL_HPP
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "call.hpp"
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* All data of a filter-call: Location, original image and result.
|
||||
*/
|
||||
class FilterCall : public Call
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a FilterCall.
|
||||
*/
|
||||
FilterCall(cv::InputArray in, cv::InputArray out,
|
||||
impl::CallMetaData data, QString type, QString description,
|
||||
QString requestedView);
|
||||
|
||||
size_t matrixCount() const override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
const cv::Mat &matrixAt(size_t index) const override;
|
||||
|
||||
/**
|
||||
* @returns the original image
|
||||
*/
|
||||
const cv::Mat &original() const
|
||||
{
|
||||
return input_;
|
||||
}
|
||||
/**
|
||||
* @returns the filtered image
|
||||
*/
|
||||
const cv::Mat &result() const
|
||||
{
|
||||
return output_;
|
||||
}
|
||||
|
||||
private:
|
||||
// TODO: in case we REALLY want to support several input-images: make
|
||||
// this a std::vector
|
||||
// TODO: those are typedefs for references, make it clean:
|
||||
cv::Mat input_;
|
||||
cv::Mat output_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a FilterCall and adds it to the global data-controller.
|
||||
*/
|
||||
void debugFilterCall(cv::InputArray original, cv::InputArray result,
|
||||
const CallMetaData &data, const char *description,
|
||||
const char *view, const char *filter);
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "opencv2/cvv/final_show.hpp"
|
||||
|
||||
#include "data_controller.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
void finalShow()
|
||||
{
|
||||
auto &controller = impl::dataController();
|
||||
if (controller.numCalls() != 0)
|
||||
{
|
||||
controller.lastCall();
|
||||
}
|
||||
impl::deleteDataController();
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
@@ -0,0 +1,108 @@
|
||||
#include "init.hpp"
|
||||
|
||||
|
||||
// filters
|
||||
#include "../qtutil/filterselectorwidget.hpp"
|
||||
#include "../qtutil/filter/grayfilterwidget.hpp"
|
||||
#include "../qtutil/filter/sobelfilterwidget.hpp"
|
||||
#include "../qtutil/filter/channelreorderfilter.hpp"
|
||||
#include "../qtutil/filter/diffFilterWidget.hpp"
|
||||
#include "../qtutil/filter/overlayfilterwidget.hpp"
|
||||
#include "../qtutil/filter/changed_pixels_widget.hpp"
|
||||
|
||||
#include "../gui/filter_call_tab.hpp"
|
||||
#include "../view/filter_view.hpp"
|
||||
#include "../view/defaultfilterview.hpp"
|
||||
#include "../view/dual_filter_view.hpp"
|
||||
#include "../view/singlefilterview.hpp"
|
||||
|
||||
#include "../gui/match_call_tab.hpp"
|
||||
#include "../view/match_view.hpp"
|
||||
#include "../view/linematchview.hpp"
|
||||
#include "../view/rawview.hpp"
|
||||
#include "../view/translationsmatchview.hpp"
|
||||
#include "../view/pointmatchview.hpp"
|
||||
|
||||
#include "../qtutil/matchview/matchselectionselector.hpp"
|
||||
#include "../qtutil/matchview/matchintervallselection.hpp"
|
||||
#include "../qtutil/matchview/matchportionselector.hpp"
|
||||
|
||||
#include "../qtutil/matchview/matchsettingsselector.hpp"
|
||||
#include "../qtutil/matchview/singlecolormatchpen.hpp"
|
||||
#include "../qtutil/matchview/falsecolormatchpen.hpp"
|
||||
#include "../qtutil/matchview/matchshowsetting.hpp"
|
||||
|
||||
#include "../qtutil/matchview/keypointselectionselector.hpp"
|
||||
#include "../qtutil/matchview/keypointintervallselection.hpp"
|
||||
#include "../qtutil/matchview/keypointportionselector.hpp"
|
||||
|
||||
#include "../qtutil/matchview/keypointsettingsselector.hpp"
|
||||
#include "../qtutil/matchview/singlecolorkeypointpen.hpp"
|
||||
#include "../qtutil/matchview/falsecolorkeypointpen.hpp"
|
||||
#include "../qtutil/matchview/keypointshowsetting.hpp"
|
||||
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
void initializeFilterAndViews()
|
||||
{
|
||||
static bool alreadyCalled = false;
|
||||
if (alreadyCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
alreadyCalled = true;
|
||||
|
||||
// filter for filter-selector-widget
|
||||
qtutil::registerFilter<1, 1, qtutil::GrayFilterWidget>("Gray filter");
|
||||
qtutil::registerFilter<1, 1, qtutil::SobelFilterWidget>("Sobel");
|
||||
qtutil::registerFilter<1, 1, qtutil::ChannelReorderFilter>(
|
||||
"Reorder channels");
|
||||
|
||||
qtutil::registerFilter<2, 1, qtutil::DiffFilterFunction>("Difference");
|
||||
qtutil::registerFilter<2, 1, qtutil::OverlayFilterWidget>("Overlay");
|
||||
qtutil::registerFilter<2, 1, qtutil::ChangedPixelsWidget>("Changed Pixels");
|
||||
|
||||
// filter-views:
|
||||
cvv::gui::FilterCallTab::registerFilterView<
|
||||
cvv::view::DefaultFilterView> ("DefaultFilterView");
|
||||
cvv::gui::FilterCallTab::registerFilterView<cvv::view::DualFilterView> (
|
||||
"DualFilterView");
|
||||
cvv::gui::FilterCallTab::registerFilterView<
|
||||
cvv::view::SingleFilterView>("SingleFilterView");
|
||||
|
||||
// match-views:
|
||||
cvv::gui::MatchCallTab::registerMatchView<cvv::view::LineMatchView>(
|
||||
"LineMatchView");
|
||||
cvv::gui::MatchCallTab::registerMatchView<
|
||||
cvv::view::TranslationMatchView>("TranslationMatchView");
|
||||
cvv::gui::MatchCallTab::registerMatchView<cvv::view::PointMatchView>(
|
||||
"PointMatchView");
|
||||
cvv::gui::MatchCallTab::registerMatchView<cvv::view::Rawview>(
|
||||
"RawView");
|
||||
|
||||
//match Settings
|
||||
cvv::qtutil::registerMatchSettings<cvv::qtutil::SingleColorMatchPen>("Single Color");
|
||||
cvv::qtutil::registerMatchSettings<cvv::qtutil::FalseColorMatchPen>("False Color");
|
||||
//cvv::qtutil::registerMatchSettings<cvv::qtutil::MatchShowSetting>("Show/Hide");
|
||||
|
||||
//match Selector
|
||||
cvv::qtutil::registerMatchSelection<cvv::qtutil::MatchIntervallSelector>("Intervall Selector");
|
||||
cvv::qtutil::registerMatchSelection<cvv::qtutil::MatchPortionSelection>("Portion Selector");
|
||||
|
||||
//keypoint Settings
|
||||
cvv::qtutil::registerKeyPointSetting<cvv::qtutil::SingleColorKeyPen>("Single Color");
|
||||
cvv::qtutil::registerKeyPointSetting<cvv::qtutil::FalseColorKeyPointPen>("False Color");
|
||||
//cvv::qtutil::registerKeyPointSetting<cvv::qtutil::KeyPointShowSetting>("Show/Hide");
|
||||
|
||||
//keypoint Selection
|
||||
cvv::qtutil::registerKeyPointSelection<cvv::qtutil::KeyPointIntervallSelector>("Intervall Selector");
|
||||
cvv::qtutil::registerKeyPointSelection<cvv::qtutil::KeyPointPortionSelection>("Portion Selector");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#ifndef CVVISUAL_INIT_HPP
|
||||
#define CVVISUAL_INIT_HPP
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
/**
|
||||
* @brief Initializes filters and views.
|
||||
*/
|
||||
void initializeFilterAndViews();
|
||||
}
|
||||
}
|
||||
#endif // CVVISUAL_INIT_HPP
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "match_call.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "data_controller.hpp"
|
||||
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
MatchCall::MatchCall(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, impl::CallMetaData data,
|
||||
QString type, QString description, QString requestedView,
|
||||
bool useTrainDescriptor)
|
||||
: Call( data, std::move(type),
|
||||
std::move(description), std::move(requestedView) ),
|
||||
img1_{ img1.getMat().clone() }, keypoints1_{ std::move(keypoints1) },
|
||||
img2_{ img2.getMat().clone() }, keypoints2_{ std::move(keypoints2) },
|
||||
matches_{ std::move(matches) }, usesTrainDescriptor_{ useTrainDescriptor }
|
||||
{
|
||||
}
|
||||
|
||||
const cv::Mat &MatchCall::matrixAt(size_t index) const
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case 0:
|
||||
return img1();
|
||||
case 1:
|
||||
return img2();
|
||||
default:
|
||||
throw std::out_of_range{ "" };
|
||||
}
|
||||
}
|
||||
|
||||
void debugMatchCall(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, const CallMetaData &data,
|
||||
const char *description, const char *view,
|
||||
bool useTrainDescriptor)
|
||||
{
|
||||
dataController().addCall(util::make_unique<MatchCall>(
|
||||
img1, std::move(keypoints1), img2, std::move(keypoints2),
|
||||
std::move(matches), data, "match",
|
||||
description ? QString::fromLocal8Bit(description)
|
||||
: QString{ "<no description>" },
|
||||
view ? QString::fromLocal8Bit(view) : QString{},
|
||||
useTrainDescriptor));
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
@@ -0,0 +1,104 @@
|
||||
#ifndef CVVISUAL_MATCH_CALL_HPP
|
||||
#define CVVISUAL_MATCH_CALL_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <type_traits>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "call.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* Contains all the calldata (= location, images and their keypoints).
|
||||
*/
|
||||
class MatchCall : public Call
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a MatchCall.
|
||||
*/
|
||||
MatchCall(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, impl::CallMetaData data,
|
||||
QString type, QString description, QString requestedView,
|
||||
bool useTrainDescriptor);
|
||||
|
||||
size_t matrixCount() const override
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
const cv::Mat &matrixAt(size_t index) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns the first Mat.
|
||||
*/
|
||||
const cv::Mat &img1() const
|
||||
{
|
||||
return img1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the second Mat.
|
||||
*/
|
||||
const cv::Mat &img2() const
|
||||
{
|
||||
return img2_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the keypoints for the first Mat.
|
||||
*/
|
||||
const std::vector<cv::KeyPoint> &keyPoints1() const
|
||||
{
|
||||
return keypoints1_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the keypoints for the second Mat.
|
||||
*/
|
||||
const std::vector<cv::KeyPoint> &keyPoints2() const
|
||||
{
|
||||
return keypoints2_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the matches.
|
||||
*/
|
||||
const std::vector<cv::DMatch> &matches() const
|
||||
{
|
||||
return matches_;
|
||||
}
|
||||
|
||||
bool usesTrainDescriptor() const
|
||||
{
|
||||
return usesTrainDescriptor_;
|
||||
}
|
||||
|
||||
private:
|
||||
cv::Mat img1_;
|
||||
std::vector<cv::KeyPoint> keypoints1_;
|
||||
cv::Mat img2_;
|
||||
std::vector<cv::KeyPoint> keypoints2_;
|
||||
std::vector<cv::DMatch> matches_;
|
||||
bool usesTrainDescriptor_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a MatchCall and adds it to the global data-controller.
|
||||
*/
|
||||
void debugMatchCall(cv::InputArray img1, std::vector<cv::KeyPoint> keypoints1,
|
||||
cv::InputArray img2, std::vector<cv::KeyPoint> keypoints2,
|
||||
std::vector<cv::DMatch> matches, const CallMetaData &data,
|
||||
const char *description, const char *view,
|
||||
bool useTrainDescriptor);
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,17 @@
|
||||
#include "opencv2/cvv/show_image.hpp"
|
||||
|
||||
#include "opencv2/cvv/call_meta_data.hpp"
|
||||
#include "single_image_call.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
void showImage(cv::InputArray img, const CallMetaData &data,
|
||||
const char *description, const char *view)
|
||||
{
|
||||
debugSingleImageCall(img, data, description, view, "singleImage");
|
||||
}
|
||||
}
|
||||
} // namespaces
|
||||
@@ -0,0 +1,42 @@
|
||||
#include "single_image_call.hpp"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "data_controller.hpp"
|
||||
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
SingleImageCall::SingleImageCall(cv::InputArray img, impl::CallMetaData data,
|
||||
QString type, QString description,
|
||||
QString requestedView)
|
||||
: Call( data, std::move(type),
|
||||
std::move(description), std::move(requestedView) ),
|
||||
img{ img.getMat().clone() }
|
||||
{
|
||||
}
|
||||
|
||||
const cv::Mat &SingleImageCall::matrixAt(size_t index) const
|
||||
{
|
||||
if (index)
|
||||
{
|
||||
throw std::out_of_range{ "" };
|
||||
}
|
||||
return img;
|
||||
}
|
||||
|
||||
void debugSingleImageCall(cv::InputArray img, const CallMetaData &data,
|
||||
const char *description, const char *view,
|
||||
const char *filter)
|
||||
{
|
||||
dataController().addCall(util::make_unique<SingleImageCall>(
|
||||
img, data, filter, description ? QString::fromLocal8Bit(description)
|
||||
: QString{ "<no description>" },
|
||||
view ? QString::fromLocal8Bit(view) : QString{}));
|
||||
}
|
||||
}
|
||||
} // namespaces cvv::impl
|
||||
@@ -0,0 +1,55 @@
|
||||
#ifndef CVVISUAL_SINGLE_IMAGE_CALL_HPP
|
||||
#define CVVISUAL_SINGLE_IMAGE_CALL_HPP
|
||||
|
||||
#include "call.hpp"
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace impl
|
||||
{
|
||||
|
||||
/**
|
||||
* All data of a filter-call: Location, original image and result.
|
||||
*/
|
||||
class SingleImageCall : public Call
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a SingleImageCall.
|
||||
*/
|
||||
SingleImageCall(cv::InputArray img, impl::CallMetaData data,
|
||||
QString type, QString description,
|
||||
QString requestedView);
|
||||
|
||||
size_t matrixCount() const override
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
const cv::Mat &matrixAt(size_t index) const override;
|
||||
|
||||
/**
|
||||
* @returns the original image
|
||||
*/
|
||||
const cv::Mat &mat() const
|
||||
{
|
||||
return img;
|
||||
}
|
||||
|
||||
private:
|
||||
cv::Mat img;
|
||||
};
|
||||
|
||||
/**
|
||||
* Constructs a SingleImageCall and adds it to the global data-controller.
|
||||
*/
|
||||
void debugSingleImageCall(cv::InputArray img, const CallMetaData &data,
|
||||
const char *description, const char *view,
|
||||
const char *filter);
|
||||
}
|
||||
} // namespaces
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,110 @@
|
||||
#include "accordion.hpp"
|
||||
|
||||
#include <QScrollArea>
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
Accordion::Accordion(QWidget *parent)
|
||||
: QWidget{ parent }, elements_{}, layout_{ nullptr }
|
||||
{
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
layout_ = *lay;
|
||||
layout_->setAlignment(Qt::AlignTop);
|
||||
|
||||
// needed because scrollArea->setLayout(layout_); does not work
|
||||
auto resizehelper = util::make_unique<QWidget>();
|
||||
resizehelper->setLayout(lay.release());
|
||||
|
||||
auto scrollArea = util::make_unique<QScrollArea>();
|
||||
scrollArea->setWidget(resizehelper.release());
|
||||
// needed because no contained widget demands a size
|
||||
scrollArea->setWidgetResizable(true);
|
||||
|
||||
auto mainLayout = util::make_unique<QVBoxLayout>();
|
||||
mainLayout->addWidget(scrollArea.release());
|
||||
setLayout(mainLayout.release());
|
||||
}
|
||||
|
||||
void Accordion::collapseAll(bool b)
|
||||
{
|
||||
for (auto &elem : elements_)
|
||||
{
|
||||
elem.second->collapse(b);
|
||||
}
|
||||
}
|
||||
|
||||
void Accordion::hideAll(bool b)
|
||||
{
|
||||
for (auto &elem : elements_)
|
||||
{
|
||||
elem.second->setVisible(!b);
|
||||
}
|
||||
}
|
||||
|
||||
Accordion::Handle Accordion::insert(const QString &title,
|
||||
std::unique_ptr<QWidget> widget,
|
||||
bool isCollapsed, std::size_t position)
|
||||
{
|
||||
// create element
|
||||
auto widgetPtr = widget.get();
|
||||
elements_.emplace(
|
||||
widgetPtr, util::make_unique<Collapsable>(title, std::move(widget),
|
||||
isCollapsed).release());
|
||||
// insert element
|
||||
layout_->insertWidget(position, &element(widgetPtr));
|
||||
return widgetPtr;
|
||||
}
|
||||
|
||||
void Accordion::remove(Handle handle)
|
||||
{
|
||||
Collapsable *elem = &element(handle);
|
||||
layout_->removeWidget(elem);
|
||||
elements_.erase(handle);
|
||||
elem->setParent(0);
|
||||
elem->deleteLater();
|
||||
}
|
||||
|
||||
void Accordion::clear()
|
||||
{
|
||||
// clear layout
|
||||
for (auto &elem : elements_)
|
||||
{
|
||||
layout_->removeWidget(elem.second);
|
||||
elem.second->setParent(nullptr);
|
||||
elem.second->deleteLater();
|
||||
}
|
||||
elements_.clear();
|
||||
}
|
||||
|
||||
std::pair<QString, Collapsable *> Accordion::pop(Handle handle)
|
||||
{
|
||||
Collapsable *elem = &element(handle);
|
||||
// remove from layout
|
||||
layout_->removeWidget(elem);
|
||||
std::pair<QString, Collapsable *> result{ element(handle).title(),
|
||||
elem };
|
||||
// remove from map
|
||||
elements_.erase(handle);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<std::pair<QString, Collapsable *>> Accordion::popAll()
|
||||
{
|
||||
std::vector<std::pair<QString, Collapsable *>> result{};
|
||||
for (auto &elem : elements_)
|
||||
{
|
||||
// remove from layout
|
||||
layout_->removeWidget(elem.second);
|
||||
result.push_back(
|
||||
std::pair<QString, Collapsable *>{ elem.second->title(),
|
||||
elem.second });
|
||||
}
|
||||
// remove from map
|
||||
elements_.clear();
|
||||
return result;
|
||||
}
|
||||
}
|
||||
} // end namespaces qtutil, cvv
|
||||
@@ -0,0 +1,292 @@
|
||||
#ifndef CVVISUAL_ACCORDION_HPP
|
||||
#define CVVISUAL_ACCORDION_HPP
|
||||
// STD
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <map>
|
||||
#include <limits>
|
||||
// QT
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
// CVV
|
||||
#include "collapsable.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../util/observer_ptr.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief The Accordion class.
|
||||
*
|
||||
* Contains multiple widgets and their title. These get stored in collapsables.
|
||||
* The collapsables are stored in a collumn.
|
||||
*/
|
||||
class Accordion : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief The handle type to access elements
|
||||
*/
|
||||
using Handle = QWidget *;
|
||||
|
||||
/**
|
||||
* @brief Constructs an empty accordion.
|
||||
* @param parent The parent widget
|
||||
*/
|
||||
explicit Accordion(QWidget *parent = nullptr);
|
||||
|
||||
~Accordion()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the element corrsponding to handle
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
* @return The element corrsponding to handle
|
||||
*/
|
||||
Collapsable &element(Handle handle)
|
||||
{
|
||||
return *elements_.at(handle);
|
||||
}
|
||||
|
||||
const Collapsable &element(Handle handle) const
|
||||
{
|
||||
return *elements_.at(handle);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the title above the element.
|
||||
* @param handle The element
|
||||
* @param title The new title.
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
*/
|
||||
void setTitle(Handle handle, const QString &title)
|
||||
{
|
||||
element(handle).setTitle(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current title above the element.
|
||||
* @param handle The element
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
* @return The current title above the element.
|
||||
*/
|
||||
QString title(Handle handle) const
|
||||
{
|
||||
return element(handle).title();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Collapses an element
|
||||
* @param handle The element to collapse
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: collapses the widget
|
||||
* false: expands the widget
|
||||
* @endparblock
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
*/
|
||||
void collapse(Handle handle, bool b = true)
|
||||
{
|
||||
element(handle).collapse(b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Expands an element
|
||||
* @param handle Element to expand
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: expands the widget
|
||||
* false: collapses the widget
|
||||
* @endparblock
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
*/
|
||||
void expand(Handle handle, bool b = true)
|
||||
{
|
||||
collapse(handle, !b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Collapses all elements
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: collapses all elements
|
||||
* false: expands all elements
|
||||
* @endparblock
|
||||
*/
|
||||
void collapseAll(bool b = true);
|
||||
|
||||
/**
|
||||
* @brief Expands all elements
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: expands all elements
|
||||
* false: collapses all elements
|
||||
* @endparblock
|
||||
*/
|
||||
void expandAll(bool b = true)
|
||||
{
|
||||
collapseAll(!b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes the element invisible
|
||||
* @param handle The element
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: makes the element invisible
|
||||
* false: makes the element visible
|
||||
* @endparblock
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
*/
|
||||
void hide(Handle handle, bool b = true)
|
||||
{
|
||||
element(handle).setVisible(!b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes the element visible
|
||||
* @param handle The element
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: makes the element visible
|
||||
* false: makes the element invisible
|
||||
* @endparblock
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
*/
|
||||
void show(Handle handle, bool b = true)
|
||||
{
|
||||
hide(handle, !b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets all elements' visibility to !b
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: makes all elements invisible
|
||||
* false: makes all elements visible
|
||||
* @endparblock
|
||||
*/
|
||||
void hideAll(bool b = true);
|
||||
|
||||
/**
|
||||
* @brief Sets all elements' visibility to b
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: makes all elements visible
|
||||
* false: makes all elements invisible
|
||||
* @endparblock
|
||||
*/
|
||||
void showAll(bool b = true)
|
||||
{
|
||||
hideAll(!b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Inserts a widget at the given position
|
||||
* @param title The title to display
|
||||
* @param widget The widget to display
|
||||
* @param isCollapsed Whether the widget is collapsed after creation
|
||||
* @param position The position. If it is greater than the number of
|
||||
*elements the widget
|
||||
* will be added to the end
|
||||
* @return The handle to access the element
|
||||
*/
|
||||
Handle insert(const QString &title, std::unique_ptr<QWidget> widget,
|
||||
bool isCollapsed = true,
|
||||
std::size_t position =
|
||||
std::numeric_limits<std::size_t>::max());
|
||||
|
||||
/**
|
||||
* @brief Adds a widget to the end of the Accordion
|
||||
* @param title The title to display
|
||||
* @param widget The widget to display
|
||||
* @param isCollapsed Whether the widget is collapsed after creation
|
||||
* @return The handle to access the element
|
||||
*/
|
||||
Handle push_back(const QString &title, std::unique_ptr<QWidget> widget,
|
||||
bool isCollapsed = true)
|
||||
{
|
||||
return insert(title, std::move(widget), isCollapsed);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds a widget to the front of the Accordion
|
||||
* @param title The title to display
|
||||
* @param widget The widget to display
|
||||
* @param isCollapsed Whether the widget is collapsed after creation
|
||||
* @return The handle to access the element
|
||||
*/
|
||||
Handle push_front(const QString &title, std::unique_ptr<QWidget> widget,
|
||||
bool isCollapsed = true)
|
||||
{
|
||||
return insert(title, std::move(widget), isCollapsed, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes the element and deletes it immediately.
|
||||
* @param handle Handle of the element
|
||||
* @param del
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
*/
|
||||
void remove(Handle handle);
|
||||
|
||||
/**
|
||||
* @brief Removes all elements and deletes them immediately.
|
||||
* @param del
|
||||
*/
|
||||
void clear();
|
||||
|
||||
/**
|
||||
* @brief Removes an element and returns its title and Collapsable.
|
||||
* (ownership remains)
|
||||
* @param handle Handle of the element
|
||||
* @throw std::out_of_range If there is no element corresponding to
|
||||
* handle
|
||||
* @return Title and reference
|
||||
*/
|
||||
std::pair<QString, Collapsable *> pop(Handle handle);
|
||||
|
||||
/**
|
||||
* @brief Removes all elements from the Accordion and returns their
|
||||
*titles
|
||||
* and Collapsables (ownership remains)
|
||||
* @return A vector containing all titles and references
|
||||
*/
|
||||
std::vector<std::pair<QString, Collapsable *>> popAll();
|
||||
|
||||
/**
|
||||
* @brief Returns the number of elements
|
||||
* @return The number of elements
|
||||
*/
|
||||
std::size_t size() const
|
||||
{
|
||||
return elements_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Storage for all elements
|
||||
*/
|
||||
std::map<Handle, Collapsable *> elements_;
|
||||
|
||||
/**
|
||||
* @brief Layout for all elements
|
||||
*/
|
||||
util::ObserverPtr<QVBoxLayout> layout_;
|
||||
}; // Accordion
|
||||
}
|
||||
} // end namespaces qtutil, cvv
|
||||
#endif // CVVISUAL_ACCORDION_HPP
|
||||
@@ -0,0 +1,475 @@
|
||||
#ifndef CVVISUAL_AUTOFILTERWIDGET_HPP
|
||||
#define CVVISUAL_AUTOFILTERWIDGET_HPP
|
||||
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <chrono>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include <QWidget>
|
||||
#include <QCheckBox>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
|
||||
#include "filterselectorwidget.hpp"
|
||||
#include "signalslot.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../util/observer_ptr.hpp"
|
||||
#include "signalslot.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
template <std::size_t In, std::size_t Out> class AutoFilterWidget;
|
||||
|
||||
/**
|
||||
* @brief Contains internal structures or classes.
|
||||
*
|
||||
* Stores the image input/output, the name and update signals for all output
|
||||
*images.
|
||||
* Also provides the label to pass messages from the filter and provides a check
|
||||
*box to select
|
||||
* the input to be filtered (can be deactivated).
|
||||
*/
|
||||
namespace structures
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Represents an entry of an autofilterwidget.
|
||||
*/
|
||||
template <std::size_t In, std::size_t Out>
|
||||
class AutoFilterWidgetEntry : public QWidget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The input type for a filter.
|
||||
*/
|
||||
using InputArray = typename AutoFilterWidget<In, Out>::InputArray;
|
||||
/**
|
||||
* The type of an output parameter of a filter.
|
||||
*/
|
||||
using OutputArray = typename AutoFilterWidget<In, Out>::OutputArray;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param name The name shown to the user.
|
||||
* @param in Image input
|
||||
* @param out Image output
|
||||
* @param parent Parent widget
|
||||
*/
|
||||
AutoFilterWidgetEntry(const QString &name, InputArray in,
|
||||
OutputArray out, QWidget *parent = nullptr)
|
||||
: QWidget{ parent }, name_{ name }, checkBox_{ nullptr },
|
||||
message_{ nullptr }, in_(in), out_(out), signals_()
|
||||
{
|
||||
auto box = util::make_unique<QCheckBox>(name);
|
||||
checkBox_ = *box;
|
||||
|
||||
auto msg = util::make_unique<QLabel>();
|
||||
message_ = *msg;
|
||||
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
lay->setAlignment(Qt::AlignTop);
|
||||
lay->setSpacing(0);
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->addWidget(box.release());
|
||||
lay->addWidget(msg.release());
|
||||
message_->setVisible(false);
|
||||
setLayout(lay.release());
|
||||
enableUserSelection(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Destructor
|
||||
*/
|
||||
~AutoFilterWidgetEntry()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks wheather the check box is checked.
|
||||
*/
|
||||
operator bool() const
|
||||
{
|
||||
return checkBox_->isChecked();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the image input.
|
||||
* @return The image input.
|
||||
*/
|
||||
InputArray input() const
|
||||
{
|
||||
return in_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the image output.
|
||||
* @return The image output.
|
||||
*/
|
||||
OutputArray output()
|
||||
{
|
||||
return out_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns references to the update signals.
|
||||
* @return References to the update signals.
|
||||
*/
|
||||
std::vector<util::Reference<const SignalMatRef>> signalsRef() const
|
||||
{
|
||||
std::vector<util::Reference<const SignalMatRef>> result{};
|
||||
for (auto &elem : signals_)
|
||||
{
|
||||
result.emplace_back(elem);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Emits all update signals.
|
||||
*/
|
||||
void emitAll() const
|
||||
{
|
||||
for (std::size_t i = 0; i < Out; i++)
|
||||
{
|
||||
signals_.at(i).emitSignal(out_.at(i).get());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the message to display.
|
||||
* @param msg The message to display (if msg == "" no message will be
|
||||
* shown.
|
||||
*/
|
||||
void setMessage(const QString &msg = "")
|
||||
{
|
||||
if (msg == "")
|
||||
{
|
||||
message_->setVisible(false);
|
||||
return;
|
||||
}
|
||||
message_->setVisible(true);
|
||||
message_->setText(QString("<font color='red'>") + name_ +
|
||||
QString(": ") + msg + QString("</font>"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enables/disables the checkbox.
|
||||
* @param enabled If true the box will be enabled.
|
||||
* If false the box will be disabled and checked.
|
||||
*/
|
||||
void enableUserSelection(bool enabled = true)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
checkBox_->setChecked(true);
|
||||
}
|
||||
checkBox_->setVisible(enabled);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The display name.
|
||||
*/
|
||||
QString name_;
|
||||
/**
|
||||
* @brief The check box.
|
||||
*/
|
||||
util::ObserverPtr<QCheckBox> checkBox_;
|
||||
/**
|
||||
* @brief The label to display messages.
|
||||
*/
|
||||
util::ObserverPtr<QLabel> message_;
|
||||
/**
|
||||
* @brief Image input.
|
||||
*/
|
||||
InputArray in_;
|
||||
/**
|
||||
* @brief Image output.
|
||||
*/
|
||||
OutputArray out_;
|
||||
/**
|
||||
* @brief The update signals for the output.
|
||||
*/
|
||||
std::array<const SignalMatRef, Out> signals_;
|
||||
};
|
||||
|
||||
} // structures
|
||||
|
||||
/**
|
||||
* @brief The AutoFilterWidget class automatically applies the selected filter
|
||||
* to all added entries.
|
||||
*/
|
||||
template <std::size_t In, std::size_t Out>
|
||||
class AutoFilterWidget : public FilterSelectorWidget<In, Out>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* The input type for a filter.
|
||||
*/
|
||||
using InputArray = typename FilterSelectorWidget<In, Out>::InputArray;
|
||||
/**
|
||||
* The type of an output parameter of a filter.
|
||||
*/
|
||||
using OutputArray = typename FilterSelectorWidget<In, Out>::OutputArray;
|
||||
|
||||
/**
|
||||
* @brief Constructor.
|
||||
* @param parent The parent widget.
|
||||
*/
|
||||
AutoFilterWidget(QWidget *parent = nullptr)
|
||||
: FilterSelectorWidget<In, Out>{ parent },
|
||||
slotEnableUserSelection_{ [this](bool b)
|
||||
{
|
||||
this->enableUserSelection(b);
|
||||
} },
|
||||
slotUseFilterIndividually_{ [this](bool b)
|
||||
{
|
||||
this->useFilterIndividually(b);
|
||||
} },
|
||||
entryLayout_{ nullptr }, applyFilterIndividually_{ false },
|
||||
entries_{}, earliestActivationTime_{}, slotApplyFilter_{ [this]()
|
||||
{
|
||||
this->autoApplyFilter();
|
||||
} },
|
||||
userSelection_{ true }
|
||||
{
|
||||
// add sublayout
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
entryLayout_ = *lay;
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
this->layout_->insertLayout(0, lay.release());
|
||||
// connect auto filter slot
|
||||
QObject::connect(&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()), &(this->slotApplyFilter_),
|
||||
SLOT(slot()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Adds an entry.
|
||||
* @param name The name of the enty.
|
||||
* @param in The image input.
|
||||
* @param out The image output.
|
||||
* @return The update signals for all output images.
|
||||
*/
|
||||
std::vector<util::Reference<const SignalMatRef>>
|
||||
addEntry(const QString &name, InputArray in, OutputArray out)
|
||||
{
|
||||
auto elem = util::make_unique<
|
||||
structures::AutoFilterWidgetEntry<In, Out>>(name, in, out);
|
||||
auto result = elem->signalsRef();
|
||||
elem->enableUserSelection(userSelection_);
|
||||
// store element
|
||||
entries_.emplace_back(*elem);
|
||||
// add it to the widget
|
||||
entryLayout_->addWidget(elem.release());
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Removes all entries.
|
||||
*/
|
||||
void removeAll()
|
||||
{
|
||||
structures::AutoFilterWidgetEntry<In, Out> *elemToDelete;
|
||||
for (auto &elem : entries_)
|
||||
{
|
||||
elemToDelete = elem.getPtr();
|
||||
// remove from layout
|
||||
entryLayout_->removeWidget(elemToDelete);
|
||||
// delete the element
|
||||
elemToDelete->deleteLater();
|
||||
}
|
||||
entries_.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enabels / disables the user to select entries to filter per
|
||||
* combo boxes.
|
||||
* @param enabled If true it will be enabled.
|
||||
*/
|
||||
void enableUserSelection(bool enabled = true)
|
||||
{
|
||||
userSelection_ = enabled;
|
||||
for (auto &elem : entries_)
|
||||
{
|
||||
elem.get().enableUserSelection(userSelection_);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets whether the filter will be applied to entries it can be
|
||||
* applied to
|
||||
* even when one other entry cant apply the filter.
|
||||
* @param individually If true each entry that can apply the filter does
|
||||
* so.
|
||||
*/
|
||||
void useFilterIndividually(bool individually = true)
|
||||
{
|
||||
applyFilterIndividually_ = individually;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a slot object that calls enableUserSelection.
|
||||
* @return A slot object that calls enableUserSelection.
|
||||
*/
|
||||
const SlotBool &slotEnableUserSelection() const
|
||||
{
|
||||
return slotEnableUserSelection_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a slot object that calls seFilterIndividually.
|
||||
* @return A slot object that calls seFilterIndividually.
|
||||
*/
|
||||
const SlotBool &slotUseFilterIndividually() const
|
||||
{
|
||||
return slotUseFilterIndividually_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief calls enableUserSelection
|
||||
*/
|
||||
const SlotBool slotEnableUserSelection_;
|
||||
/**
|
||||
* @brief calls seFilterIndividually.
|
||||
*/
|
||||
const SlotBool slotUseFilterIndividually_;
|
||||
/**
|
||||
* @brief Applies the filter when some settings where changed.
|
||||
*/
|
||||
void autoApplyFilter()
|
||||
{
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
// activate again?
|
||||
if (start < earliestActivationTime_)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// apply filter
|
||||
if (!applyFilterIndividually_)
|
||||
{
|
||||
// only apply all filters at once
|
||||
// check wheather all filters can be applied
|
||||
std::size_t failed = 0;
|
||||
for (auto &elem : entries_)
|
||||
{
|
||||
// activated?
|
||||
if (elem.get())
|
||||
{
|
||||
auto check = this->checkInput(
|
||||
elem.get().input());
|
||||
|
||||
if (!check.first)
|
||||
{
|
||||
// elem cant apply filter
|
||||
failed++;
|
||||
elem.get().setMessage(
|
||||
check.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// elem can apply filter. delete
|
||||
// message
|
||||
elem.get().setMessage("");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete message
|
||||
elem.get().setMessage("");
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
{
|
||||
// one filter failed
|
||||
return;
|
||||
}
|
||||
// all can apply filter
|
||||
// apply filters
|
||||
for (auto &elem : entries_)
|
||||
{
|
||||
// activated?
|
||||
if (elem.get())
|
||||
{
|
||||
this->applyFilter(elem.get().input(),
|
||||
elem.get().output());
|
||||
elem.get().emitAll();
|
||||
};
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // applyFilterIndividually_==true
|
||||
// filters can be applied individually
|
||||
for (auto &elem : entries_)
|
||||
{
|
||||
// activated?
|
||||
if (elem.get())
|
||||
{
|
||||
auto check = this->checkInput(
|
||||
elem.get().input());
|
||||
if (!check.first)
|
||||
{
|
||||
// set message
|
||||
elem.get().setMessage(
|
||||
check.second);
|
||||
}
|
||||
else
|
||||
{
|
||||
// apply filter+set message
|
||||
elem.get().setMessage("");
|
||||
this->applyFilter(
|
||||
elem.get().input(),
|
||||
elem.get().output());
|
||||
elem.get().emitAll();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// delete message
|
||||
elem.get().setMessage("");
|
||||
}
|
||||
}
|
||||
}
|
||||
// update activation time
|
||||
earliestActivationTime_ =
|
||||
std::chrono::high_resolution_clock::now() +
|
||||
(std::chrono::high_resolution_clock::now() -
|
||||
start); // duration
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief The layout containing the entries.
|
||||
*/
|
||||
util::ObserverPtr<QVBoxLayout> entryLayout_;
|
||||
/**
|
||||
* @brief Each entry that can apply the filter does so.
|
||||
*/
|
||||
bool applyFilterIndividually_;
|
||||
/**
|
||||
* @brief The entries.
|
||||
*/
|
||||
std::vector<util::Reference<structures::AutoFilterWidgetEntry<In, Out>>>
|
||||
entries_;
|
||||
/**
|
||||
* @brief Time for the earliest next activation for the filter.
|
||||
*/
|
||||
std::chrono::time_point<std::chrono::high_resolution_clock>
|
||||
earliestActivationTime_;
|
||||
/**
|
||||
* @brief Slot called when filter settings change.
|
||||
*/
|
||||
Slot slotApplyFilter_;
|
||||
/**
|
||||
* @brief Whether user selection is enabled
|
||||
*/
|
||||
bool userSelection_;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // CVVISUAL_AUTOFILTERWIDGET_HPP
|
||||
@@ -0,0 +1,70 @@
|
||||
#include "collapsable.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
Collapsable::Collapsable(const QString &title, std::unique_ptr<QWidget> widget,
|
||||
bool isCollapsed, QWidget *parent)
|
||||
: QFrame{ parent }, widget_{ widget.get() }, layout_{ nullptr }
|
||||
{
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
layout_ = *lay;
|
||||
// set alignment+border
|
||||
setLineWidth(1);
|
||||
setFrameStyle(QFrame::Box);
|
||||
layout_->setAlignment(Qt::AlignTop);
|
||||
layout_->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
// build header
|
||||
auto tmpButton = util::make_unique<QPushButton>();
|
||||
button_ = tmpButton.get();
|
||||
button_->setEnabled(true);
|
||||
button_->setText(title);
|
||||
button_->setCheckable(true);
|
||||
|
||||
// build widget
|
||||
setLayout(lay.release());
|
||||
layout_->addWidget(tmpButton.release());
|
||||
layout_->addWidget(widget.release());
|
||||
|
||||
// connect signals and slots
|
||||
QObject::connect(button_, SIGNAL(clicked()), this,
|
||||
SLOT(toggleVisibility()));
|
||||
|
||||
// collapse/ expand according to isCollapsed
|
||||
collapse(isCollapsed);
|
||||
}
|
||||
|
||||
// Collapsable::Collapsable(const QString& title,QWidget& widget, bool
|
||||
// isCollapsed, QWidget *parent):
|
||||
// Collapsable{title, std::unique_ptr<QWidget>{&widget}, isCollapsed,
|
||||
//parent} {}
|
||||
|
||||
void Collapsable::collapse(bool b)
|
||||
{
|
||||
button_->setChecked(!b);
|
||||
if (b)
|
||||
{
|
||||
widget_->hide();
|
||||
}
|
||||
else
|
||||
{
|
||||
widget_->show();
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *Collapsable::detachWidget()
|
||||
{
|
||||
if (!widget_)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
layout_->removeWidget(widget_);
|
||||
QWidget *tmp = widget_;
|
||||
widget_ = nullptr;
|
||||
return tmp;
|
||||
}
|
||||
}
|
||||
} // end namespaces qtutil, cvv
|
||||
@@ -0,0 +1,139 @@
|
||||
#ifndef CVVISUAL_COLLAPSABLE_H
|
||||
#define CVVISUAL_COLLAPSABLE_H
|
||||
// std
|
||||
#include <cstddef>
|
||||
// QT
|
||||
#include <QString>
|
||||
#include <QWidget>
|
||||
#include <QPushButton>
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
#include <QFrame>
|
||||
|
||||
#include "../util/util.hpp"
|
||||
#include "../util/observer_ptr.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Contains a widget and a title.
|
||||
*
|
||||
* The widget can be collapsed and expanded with a button.
|
||||
* If the widget is collapsed only button and title are shown.
|
||||
*/
|
||||
class Collapsable : public QFrame
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a collapsable
|
||||
* @param title The title above the widget.
|
||||
* @param widget The widget to store.
|
||||
* @param isCollapsed If true the contained widget will be collapsed.
|
||||
* (It will be shown
|
||||
* otherwise.)
|
||||
*/
|
||||
// explicit Collapsable(const QString& title, QWidget& widget, bool
|
||||
// isCollapsed = true,
|
||||
// QWidget *parent = 0);
|
||||
explicit Collapsable(const QString &title,
|
||||
std::unique_ptr<QWidget> widget,
|
||||
bool isCollapsed = true, QWidget *parent = 0);
|
||||
|
||||
~Collapsable()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Collapses the contained widget.
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: collapses the widget
|
||||
* false: expands the widget
|
||||
* @endparblock
|
||||
*/
|
||||
void collapse(bool b = true);
|
||||
|
||||
/**
|
||||
* @brief Expands the contained widget.
|
||||
* @param b
|
||||
* @parblock
|
||||
* true: expands the widget
|
||||
* false: collapses the widget
|
||||
* @endparblock
|
||||
*/
|
||||
void expand(bool b = true)
|
||||
{
|
||||
collapse(!b);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the title above the widget.
|
||||
*/
|
||||
void setTitle(const QString &title)
|
||||
{
|
||||
button_->setText(title);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the current title above the widget.
|
||||
* @return The current title above the widget
|
||||
*/
|
||||
QString title() const
|
||||
{
|
||||
return button_->text();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns a reference to the contained widget.
|
||||
* @return A reference to the contained widget.
|
||||
*/
|
||||
QWidget &widget()
|
||||
{
|
||||
return *widget_;
|
||||
}
|
||||
|
||||
const QWidget &widget() const
|
||||
{
|
||||
return *widget_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Detaches the contained widget. (ownership remains)
|
||||
* @return The contained widget
|
||||
*/
|
||||
QWidget *detachWidget();
|
||||
|
||||
private
|
||||
slots:
|
||||
/**
|
||||
* @brief Toggles the visibility.
|
||||
*/
|
||||
void toggleVisibility()
|
||||
{
|
||||
collapse(widget_->isVisible());
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief The contained widget
|
||||
*/
|
||||
QWidget *widget_;
|
||||
|
||||
/**
|
||||
* @brief The button to toggle the widget
|
||||
*/
|
||||
QPushButton *button_;
|
||||
|
||||
/**
|
||||
* @brief The layout containing the header and widget
|
||||
*/
|
||||
util::ObserverPtr<QVBoxLayout> layout_;
|
||||
}; // Collapsable
|
||||
}
|
||||
} // end namespaces qtutil, cvv
|
||||
|
||||
#endif // CVVISUAL_COLLAPSABLE_H
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "changed_pixels_widget.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
|
||||
#include "../types.hpp"
|
||||
|
||||
|
||||
//forward
|
||||
template<int Depth>
|
||||
void changedPixelImage(const cv::Mat& mat0, const cv::Mat& mat1, cv::Mat& out);
|
||||
|
||||
template<int Depth, int Channels>
|
||||
void changedPixelImage(const cv::Mat& mat0, const cv::Mat& mat1, cv::Mat& out);
|
||||
|
||||
static void changedPixelImage(const cv::Mat& mat0, const cv::Mat& mat1, cv::Mat& out)
|
||||
{
|
||||
// need same size
|
||||
if (mat0.size() != mat1.size())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//need same # of channels
|
||||
if (mat0.channels()!=mat1.channels())
|
||||
{
|
||||
return;
|
||||
}
|
||||
//need same depth
|
||||
if (mat0.depth()!=mat1.depth())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
//split depth
|
||||
switch(mat0.depth())
|
||||
{
|
||||
case CV_8U :changedPixelImage<CV_8U >(mat0,mat1,out); break;
|
||||
case CV_8S :changedPixelImage<CV_8S >(mat0,mat1,out); break;
|
||||
case CV_16U:changedPixelImage<CV_16U>(mat0,mat1,out); break;
|
||||
case CV_16S:changedPixelImage<CV_16S>(mat0,mat1,out); break;
|
||||
case CV_32S:changedPixelImage<CV_32S>(mat0,mat1,out); break;
|
||||
case CV_32F:changedPixelImage<CV_32F>(mat0,mat1,out); break;
|
||||
case CV_64F:changedPixelImage<CV_64F>(mat0,mat1,out); break;
|
||||
}
|
||||
}
|
||||
|
||||
template<int Depth>
|
||||
void changedPixelImage(const cv::Mat& mat0, const cv::Mat& mat1, cv::Mat& out)
|
||||
{
|
||||
switch(mat0.channels())
|
||||
{
|
||||
case 1 :changedPixelImage<Depth,1 >(mat0,mat1,out); break;
|
||||
case 2 :changedPixelImage<Depth,2 >(mat0,mat1,out); break;
|
||||
case 3 :changedPixelImage<Depth,3 >(mat0,mat1,out); break;
|
||||
case 4 :changedPixelImage<Depth,4 >(mat0,mat1,out); break;
|
||||
case 5 :changedPixelImage<Depth,5 >(mat0,mat1,out); break;
|
||||
case 6 :changedPixelImage<Depth,6 >(mat0,mat1,out); break;
|
||||
case 7 :changedPixelImage<Depth,7 >(mat0,mat1,out); break;
|
||||
case 8 :changedPixelImage<Depth,8 >(mat0,mat1,out); break;
|
||||
case 9 :changedPixelImage<Depth,9 >(mat0,mat1,out); break;
|
||||
case 10 :changedPixelImage<Depth,10>(mat0,mat1,out); break;
|
||||
}
|
||||
}
|
||||
|
||||
template<int Depth, int Channels>
|
||||
void changedPixelImage(const cv::Mat& mat0, const cv::Mat& mat1, cv::Mat& out)
|
||||
{
|
||||
using PixelInType=cvv::qtutil::PixelType<Depth,Channels>;
|
||||
using PixelOutType=cvv::qtutil::DepthType<CV_8U>;
|
||||
|
||||
|
||||
cv::Mat result=cv::Mat::zeros(mat0.rows, mat0.cols,CV_8U);
|
||||
bool same;
|
||||
const PixelInType* in0;
|
||||
const PixelInType* in1;
|
||||
for(int i=0;i<result.rows;i++)
|
||||
{
|
||||
for(int j=0;j<result.cols;j++)
|
||||
{
|
||||
same=true;
|
||||
in0=&(mat0.at<PixelInType>(i,j));
|
||||
in1=&(mat1.at<PixelInType>(i,j));
|
||||
|
||||
for(int chan=0;chan<Channels;chan++)
|
||||
{
|
||||
same = same && (in0[chan]==in1[chan]);
|
||||
}
|
||||
//same color => set pixel color white
|
||||
if(same)
|
||||
{
|
||||
(result.at<PixelOutType>(i,j))=255;
|
||||
}
|
||||
}
|
||||
}
|
||||
out=result;
|
||||
}
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
|
||||
ChangedPixelsWidget::ChangedPixelsWidget(QWidget* parent): FilterFunctionWidget<2, 1>{parent}
|
||||
{
|
||||
auto lay=util::make_unique<QVBoxLayout>();
|
||||
lay->addWidget(util::make_unique<QLabel>("Changed pixels will be black<br>"
|
||||
" unchanged white.").release());
|
||||
setLayout(lay.release());
|
||||
}
|
||||
|
||||
void ChangedPixelsWidget::applyFilter(ChangedPixelsWidget::InputArray in,
|
||||
ChangedPixelsWidget::OutputArray out) const
|
||||
{
|
||||
changedPixelImage(in.at(0).get(),in.at(1).get(),out.at(0).get());
|
||||
}
|
||||
|
||||
std::pair<bool, QString> ChangedPixelsWidget::checkInput(InputArray in) const
|
||||
{
|
||||
if (in.at(0).get().size() != in.at(1).get().size())
|
||||
{
|
||||
return std::make_pair(false, "images need to have same size");
|
||||
}
|
||||
|
||||
size_t inChannels = in.at(0).get().channels();
|
||||
|
||||
if (inChannels != static_cast<size_t>(in.at(1).get().channels()))
|
||||
{
|
||||
return std::make_pair(
|
||||
false, "images need to have same number of channels");
|
||||
}
|
||||
|
||||
if (inChannels>10 || inChannels<1)
|
||||
{
|
||||
return std::make_pair(
|
||||
false, "images need to have 1 up to 10 channels");
|
||||
}
|
||||
|
||||
int i0depth=in.at(0).get().depth();
|
||||
|
||||
if (i0depth!=in.at(1).get().depth())
|
||||
{
|
||||
return std::make_pair(
|
||||
false, "images need to have the same depth");
|
||||
}
|
||||
|
||||
if (!((i0depth==CV_8U)||(i0depth==CV_8S)||(i0depth==CV_16U)||(i0depth==CV_16S)||
|
||||
(i0depth==CV_32S)||(i0depth==CV_32F)||(i0depth==CV_64F)))
|
||||
{
|
||||
return std::make_pair(false, "images have unknown depth");
|
||||
}
|
||||
|
||||
return std::make_pair(true, "");
|
||||
}
|
||||
|
||||
|
||||
} // qtutil
|
||||
} // cvv
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef CVVISUAL_CHANGED_PIXELS_WIDGET_HPP
|
||||
#define CVVISUAL_CHANGED_PIXELS_WIDGET_HPP
|
||||
|
||||
#include "../filterfunctionwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief A Comparator that will create a Mat that highlights exactly the changed
|
||||
* pixels (black) and leaves unchanged pixels white.
|
||||
*/
|
||||
class ChangedPixelsWidget : public FilterFunctionWidget<2, 1>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
ChangedPixelsWidget(QWidget* parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Applys the filter to in and saves the result in out.
|
||||
* @param in The input images.
|
||||
* @param out The output images.
|
||||
*/
|
||||
void applyFilter(InputArray in, OutputArray out) const override;
|
||||
|
||||
/**
|
||||
* @brief Checks whether input can be progressed by the applyFilter
|
||||
*function.
|
||||
* @param in The input images.
|
||||
* @return bool = true: the filter can be executed.
|
||||
* bool = false: the filter cant be executed (e.g. images
|
||||
*have wrong depth)
|
||||
* QString = message for the user (e.g. why the filter can't
|
||||
*be progressed.)
|
||||
*/
|
||||
std::pair<bool, QString> checkInput(InputArray in) const override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "channelreorderfilter.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
|
||||
#include "../util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
ChannelReorderFilter::ChannelReorderFilter(QWidget *parent)
|
||||
: FilterFunctionWidget<1, 1>{ parent }, layout_{ nullptr },
|
||||
channel_{ nullptr }, channelAssignment_{}
|
||||
{
|
||||
setToolTip(
|
||||
"nonexistant channels from source will be seen as a zero mat");
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
layout_ = *lay;
|
||||
auto channel = util::make_unique<QSpinBox>();
|
||||
channel_ = *channel;
|
||||
|
||||
// channelselector
|
||||
channel_->setRange(1, 10);
|
||||
QObject::connect(channel_.getPtr(), SIGNAL(valueChanged(int)), this,
|
||||
SLOT(setChannel(int)));
|
||||
|
||||
// build ui
|
||||
layout_->addWidget(
|
||||
util::make_unique<QLabel>("Number of channels").release());
|
||||
layout_->addWidget(channel.release());
|
||||
layout_->addWidget(util::make_unique<QLabel>(
|
||||
"Assignment for the old channels").release());
|
||||
setLayout(lay.release());
|
||||
|
||||
channel_->setValue(3);
|
||||
}
|
||||
|
||||
std::pair<bool, QString> ChannelReorderFilter::checkInput(InputArray in) const
|
||||
{
|
||||
if (in.at(0)->channels() < 1)
|
||||
{
|
||||
return { false, "<1 channel" };
|
||||
}
|
||||
return { true, "" };
|
||||
}
|
||||
|
||||
void ChannelReorderFilter::applyFilter(InputArray in, OutputArray out) const
|
||||
{
|
||||
auto chans = splitChannels(in.at(0).get());
|
||||
cv::Mat zeros = cv::Mat::zeros(chans.front().rows, chans.front().cols,
|
||||
chans.front().type());
|
||||
std::vector<cv::Mat> toMerge{};
|
||||
for (std::size_t i = 0; i < channelAssignment_.size(); i++)
|
||||
{
|
||||
if (static_cast<std::size_t>(
|
||||
channelAssignment_.at(i)->value()) < chans.size())
|
||||
{
|
||||
toMerge.push_back(
|
||||
chans.at(channelAssignment_.at(i)->value()));
|
||||
}
|
||||
else
|
||||
{
|
||||
toMerge.push_back(zeros);
|
||||
}
|
||||
}
|
||||
out.at(0).get() = mergeChannels(toMerge);
|
||||
}
|
||||
|
||||
void ChannelReorderFilter::setChannel(std::size_t n)
|
||||
{
|
||||
if (n == channelAssignment_.size())
|
||||
{
|
||||
// stop rec + update
|
||||
signalFilterSettingsChanged().emitSignal();
|
||||
return;
|
||||
}
|
||||
else if (n < channelAssignment_.size())
|
||||
{
|
||||
// remove one channel
|
||||
QSpinBox *box = channelAssignment_.back().getPtr();
|
||||
channelAssignment_.pop_back();
|
||||
layout_->removeWidget(box);
|
||||
box->setParent(nullptr);
|
||||
box->deleteLater();
|
||||
}
|
||||
else
|
||||
{
|
||||
// add one channel
|
||||
auto box = util::make_unique<QSpinBox>();
|
||||
box->setRange(0, 9);
|
||||
box->setSingleStep(1);
|
||||
box->setValue(channelAssignment_.size());
|
||||
channelAssignment_.emplace_back(*box);
|
||||
// connect
|
||||
QObject::connect(box.get(), SIGNAL(valueChanged(int)),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
layout_->addWidget(box.release());
|
||||
}
|
||||
// rec
|
||||
setChannel(n);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
#ifndef CVVISUAL_CHANNELREORDERFILTER_HPP
|
||||
#define CVVISUAL_CHANNELREORDERFILTER_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QSpinBox>
|
||||
|
||||
#include "../filterfunctionwidget.hpp"
|
||||
#include "../../util/observer_ptr.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief Class providing a filter that reorders an input mat's channels.
|
||||
*/
|
||||
class ChannelReorderFilter : public FilterFunctionWidget<1, 1>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = FilterFunctionWidget<1, 1>::InputArray;
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = FilterFunctionWidget<1, 1>::OutputArray;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
ChannelReorderFilter(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Applys the filter to in and saves the result in out.
|
||||
* @param in The input images.
|
||||
* @param out The output images.
|
||||
*/
|
||||
virtual void applyFilter(InputArray in, OutputArray out) const override;
|
||||
|
||||
/**
|
||||
* @brief Checks whether input can be progressed by the applyFilter
|
||||
*function.
|
||||
* @param in The input images.
|
||||
* @return bool = true: the filter can be executed.
|
||||
* bool = false: the filter cant be executed (e.g. images
|
||||
*have wrong depth)
|
||||
* QString = message for the user (e.g. why the filter can't
|
||||
*be progressed.)
|
||||
*/
|
||||
virtual std::pair<bool, QString> checkInput(InputArray) const override;
|
||||
|
||||
/**
|
||||
* @brief Returns the number of output channels.
|
||||
* @return the number of output channels.
|
||||
*/
|
||||
int outputChannels()
|
||||
{
|
||||
return channel_->value();
|
||||
}
|
||||
|
||||
private
|
||||
slots:
|
||||
/**
|
||||
* @brief Sets the number of channels.
|
||||
* @param n The number of channels.
|
||||
*/
|
||||
void setChannel(int n)
|
||||
{
|
||||
setChannel(static_cast<std::size_t>(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the number of channels.
|
||||
* @param n The number of channels.
|
||||
*/
|
||||
void setChannel(std::size_t n);
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief The layout.
|
||||
*/
|
||||
util::ObserverPtr<QVBoxLayout> layout_;
|
||||
/**
|
||||
* @brief The spinbox to select the number of channels.
|
||||
*/
|
||||
util::ObserverPtr<QSpinBox> channel_;
|
||||
/**
|
||||
* @brief Spin boxes for the channel reordering.
|
||||
*/
|
||||
std::vector<util::ObserverPtr<QSpinBox>> channelAssignment_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CVVISUAL_CHANNELREORDERFILTER_HPP
|
||||
@@ -0,0 +1,132 @@
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QString>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "../../util/util.hpp"
|
||||
#include "diffFilterWidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
using namespace cv;
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
DiffFilterFunction::DiffFilterFunction(QWidget *parent)
|
||||
: FilterFunctionWidget<2, 1>{ parent },
|
||||
filterType_{ DiffFilterType::GRAYSCALE }
|
||||
{
|
||||
auto layout = util::make_unique<QVBoxLayout>();
|
||||
auto comboBox = util::make_unique<QComboBox>();
|
||||
|
||||
filterMap_.insert(
|
||||
std::make_pair<std::string, std::function<void(void)>>(
|
||||
"Hue", [this]()
|
||||
{ filterType_ = DiffFilterType::HUE; }));
|
||||
filterMap_.insert(
|
||||
std::make_pair<std::string, std::function<void(void)>>(
|
||||
"Saturation", [this]()
|
||||
{ filterType_ = DiffFilterType::SATURATION; }));
|
||||
filterMap_.insert(
|
||||
std::make_pair<std::string, std::function<void(void)>>(
|
||||
"Value", [this]()
|
||||
{ filterType_ = DiffFilterType::VALUE; }));
|
||||
filterMap_.insert(
|
||||
std::make_pair<std::string, std::function<void(void)>>(
|
||||
"Grayscale", [this]()
|
||||
{ filterType_ = DiffFilterType::GRAYSCALE; }));
|
||||
|
||||
// Register filter names at comboBox
|
||||
comboBox->addItems(DiffFilterFunction::extractStringListfromMap());
|
||||
connect(comboBox.get(), SIGNAL(currentIndexChanged(const QString &)),
|
||||
this, SLOT(updateFilterType(const QString &)));
|
||||
|
||||
// Add title of comboBox and comboBox to the layout
|
||||
layout->addWidget(
|
||||
util::make_unique<QLabel>("Select a filter").release());
|
||||
layout->addWidget(comboBox.release());
|
||||
setLayout(layout.release());
|
||||
}
|
||||
|
||||
void DiffFilterFunction::applyFilter(InputArray in, OutputArray out) const
|
||||
{
|
||||
|
||||
auto check = checkInput(in);
|
||||
if (!check.first)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (filterType_ == DiffFilterType::GRAYSCALE)
|
||||
{
|
||||
out.at(0).get() = cv::abs(in.at(0).get() - in.at(1).get());
|
||||
return;
|
||||
}
|
||||
|
||||
cv::Mat originalHSV, filteredHSV;
|
||||
cv::cvtColor(in.at(0).get(), originalHSV, COLOR_BGR2HSV);
|
||||
cv::cvtColor(in.at(1).get(), filteredHSV, COLOR_BGR2HSV);
|
||||
auto diffHSV = cv::abs(originalHSV - filteredHSV);
|
||||
|
||||
std::array<cv::Mat, 3> splitVector;
|
||||
cv::split(diffHSV, splitVector.data());
|
||||
|
||||
out.at(0).get() = splitVector.at(static_cast<size_t>(filterType_));
|
||||
|
||||
}
|
||||
|
||||
std::pair<bool, QString> DiffFilterFunction::checkInput(InputArray in) const
|
||||
{
|
||||
|
||||
if (in.at(0).get().size() != in.at(1).get().size())
|
||||
{
|
||||
return std::make_pair(false, "Images need to have same size");
|
||||
}
|
||||
|
||||
size_t inChannels = in.at(0).get().channels();
|
||||
|
||||
if (inChannels != static_cast<size_t>(in.at(1).get().channels()))
|
||||
{
|
||||
return std::make_pair(
|
||||
false, "Images need to have same number of channels");
|
||||
}
|
||||
|
||||
if (inChannels == 1 && filterType_ != DiffFilterType::GRAYSCALE)
|
||||
{
|
||||
return std::make_pair(false, "Images are grayscale, but "
|
||||
"selected Filter can only "
|
||||
"progress 3-channel images");
|
||||
}
|
||||
|
||||
if (inChannels != 1 && inChannels != 3 && inChannels != 4)
|
||||
{
|
||||
return std::make_pair(
|
||||
false, "Images must have one, three or four channels");
|
||||
}
|
||||
|
||||
|
||||
return std::make_pair(true, "Images can be converted");
|
||||
}
|
||||
|
||||
QStringList DiffFilterFunction::extractStringListfromMap() const
|
||||
{
|
||||
QStringList stringList{};
|
||||
for (auto mapElem : filterMap_)
|
||||
{
|
||||
stringList << QString::fromStdString(mapElem.first);
|
||||
}
|
||||
return stringList;
|
||||
}
|
||||
|
||||
void DiffFilterFunction::updateFilterType(const QString &name)
|
||||
{
|
||||
filterMap_.find(name.toStdString())->second();
|
||||
signalFilterSettingsChanged().emitSignal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
|
||||
#ifndef CVVISUAL_DIFF_FILTER_WIDGET_HPP
|
||||
#define CVVISUAL_DIFF_FILTER_WIDGET_HPP
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../filterselectorwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Enum of the possible types of difference filters.
|
||||
*/
|
||||
enum class DiffFilterType
|
||||
{
|
||||
HUE = 0,
|
||||
SATURATION = 1,
|
||||
VALUE = 2,
|
||||
LUMINANCE = VALUE,
|
||||
GRAYSCALE = 3
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Class providing functionality to compute a difference image of two
|
||||
* input matrices.
|
||||
*/
|
||||
class DiffFilterFunction : public FilterFunctionWidget<2, 1>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = FilterFunctionWidget<2, 1>::InputArray;
|
||||
// std::array<util::Reference<const cv::Mat>,2>
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = FilterFunctionWidget<2, 1>::OutputArray;
|
||||
// std::array<util::Reference<cv::Mat>,1>
|
||||
|
||||
/**
|
||||
* @brief Constructs DiffFilterFunction with default filter grayscale.
|
||||
* @param parent The parent of the widget
|
||||
*/
|
||||
DiffFilterFunction(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Applys difference filter specified by filterType_.
|
||||
* @param in Array of input matrices
|
||||
* @param out Array of output matrices
|
||||
*/
|
||||
void applyFilter(InputArray in, OutputArray out) const CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief Checks whether matrices in 'in' can be processed by this
|
||||
* DiffFilter
|
||||
*/
|
||||
std::pair<bool, QString> checkInput(InputArray in) const CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
DiffFilterType filterType_;
|
||||
//< Type of difference filter that is to be applied
|
||||
|
||||
std::unordered_map<std::string, std::function<void(void)>> filterMap_;
|
||||
//< Map of all available filters with their names
|
||||
|
||||
/**
|
||||
* @brief Extracts the names of all available filters from filterMap_.
|
||||
*/
|
||||
QStringList extractStringListfromMap() const;
|
||||
|
||||
private
|
||||
slots:
|
||||
/**
|
||||
* @brief Sets filterType_ and emits signFilterSettingsChanged.
|
||||
* @param type The name of the new DiffFilterType.
|
||||
*/
|
||||
void updateFilterType(const QString &type);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
#include "grayfilterwidget.hpp"
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
|
||||
#include "../filterselectorwidget.hpp"
|
||||
#include "../../util/util.hpp"
|
||||
#include "../util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
GrayFilterWidget::GrayFilterWidget(QWidget *parent)
|
||||
: FilterFunctionWidget<1, 1>{ parent }, layout_{ nullptr },
|
||||
channel_{ nullptr }, chanValues_{}
|
||||
{
|
||||
// set a tooltip
|
||||
setToolTip(
|
||||
"nonexistant channels from source will be seen as a zero mat");
|
||||
// create the layout
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
layout_ = *lay;
|
||||
// create the spinbox to select the number of channels
|
||||
auto channel = util::make_unique<QSpinBox>();
|
||||
channel_ = *channel;
|
||||
// create a button to set up the default gray filter
|
||||
auto button = util::make_unique<QPushButton>("use default rgb to gray");
|
||||
QObject::connect(button.get(), SIGNAL(clicked()), this, SLOT(setStd()));
|
||||
|
||||
// set up the spinbox to select the number of channels.
|
||||
channel_->setRange(1, 10);
|
||||
// and connect it with the slot setChannel.
|
||||
QObject::connect(channel_.getPtr(), SIGNAL(valueChanged(int)), this,
|
||||
SLOT(setChannel(int)));
|
||||
|
||||
// build ui (some labels for the user are added)
|
||||
layout_->addWidget(button.release());
|
||||
layout_->addWidget(
|
||||
util::make_unique<QLabel>("Number of channels").release());
|
||||
layout_->addWidget(channel.release());
|
||||
layout_->addWidget(
|
||||
util::make_unique<QLabel>("Percentage for channels").release());
|
||||
setLayout(lay.release());
|
||||
|
||||
// set up the default gray filter
|
||||
setStd();
|
||||
}
|
||||
|
||||
void GrayFilterWidget::applyFilter(InputArray in, OutputArray out) const
|
||||
{
|
||||
// check weather the filter can be applied
|
||||
if (!(checkInput(in).first))
|
||||
{
|
||||
return;
|
||||
}
|
||||
// the filter can be applied
|
||||
// split the cannels of the input image
|
||||
auto channels = splitChannels(in.at(0).get());
|
||||
// create a zero image
|
||||
cv::Mat tmp = cv::Mat::zeros(in.at(0).get().rows, in.at(0).get().cols,
|
||||
in.at(0).get().depth());
|
||||
// multiply all channels with their factor and add it to tmp
|
||||
// if there are factors for more channels than the input image has, this
|
||||
// channels
|
||||
// will be ignored
|
||||
for (std::size_t i = 0;
|
||||
((i < channels.size()) && (i < chanValues_.size())); i++)
|
||||
{
|
||||
// multiply each channel with its factor and add the result to
|
||||
// tmp
|
||||
tmp += channels.at(i) * (chanValues_.at(i)->value());
|
||||
}
|
||||
// finally assign tmp to out
|
||||
out.at(0).get() = tmp;
|
||||
}
|
||||
|
||||
std::pair<bool, QString> GrayFilterWidget::checkInput(InputArray) const
|
||||
{
|
||||
// checks wheather the current settings are valid.
|
||||
// add up all factors
|
||||
double sum = 0;
|
||||
for (auto &elem : chanValues_)
|
||||
{
|
||||
sum += (elem->value());
|
||||
}
|
||||
// check wheather the sum is <=1
|
||||
if (sum > 1)
|
||||
{
|
||||
// the settings are invalid => return fale + a error message
|
||||
return { false, QString{ "total : " } + QString::number(sum) +
|
||||
QString{ " > 1" } };
|
||||
}
|
||||
// the settings are valid
|
||||
return { true, "" };
|
||||
}
|
||||
|
||||
void GrayFilterWidget::setChannel(std::size_t n)
|
||||
{
|
||||
/*
|
||||
* this function is recursive.
|
||||
*/
|
||||
if (n == chanValues_.size())
|
||||
{
|
||||
// stop recursion
|
||||
return;
|
||||
}
|
||||
else if (n < chanValues_.size())
|
||||
{
|
||||
// currently there are more channels than requested.
|
||||
// => remove one channel
|
||||
// remove a spin box from the vector
|
||||
QDoubleSpinBox *box = chanValues_.back().getPtr();
|
||||
chanValues_.pop_back();
|
||||
// remove it from the layout
|
||||
layout_->removeWidget(box);
|
||||
// reset the parent
|
||||
box->setParent(nullptr);
|
||||
// finally delete it
|
||||
box->deleteLater();
|
||||
}
|
||||
else
|
||||
{
|
||||
// currently less channel than requested
|
||||
// => add one channel
|
||||
// create a new spinbox, set its range and step size.
|
||||
auto box = util::make_unique<QDoubleSpinBox>();
|
||||
box->setRange(0, 1);
|
||||
box->setSingleStep(0.01);
|
||||
// add this box to the vector
|
||||
chanValues_.emplace_back(*box);
|
||||
// connect it to signFilterSettingsChanged_.
|
||||
QObject::connect(box.get(), SIGNAL(valueChanged(double)),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
// and add it to the layout
|
||||
layout_->addWidget(box.release());
|
||||
}
|
||||
// recursion
|
||||
setChannel(n);
|
||||
}
|
||||
|
||||
void GrayFilterWidget::setStd()
|
||||
{
|
||||
// use 3 channels (b g r)
|
||||
channel_->setValue(3);
|
||||
// set factor for b
|
||||
chanValues_.at(0)->setValue(0.114);
|
||||
// set factor for g
|
||||
chanValues_.at(1)->setValue(0.587);
|
||||
// set factor for r
|
||||
chanValues_.at(2)->setValue(0.299);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
#ifndef CVVISUAL_GRAYFILTERWIDGET_HPP
|
||||
#define CVVISUAL_GRAYFILTERWIDGET_HPP
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QSpinBox>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QWidget>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
#include "../filterfunctionwidget.hpp"
|
||||
#include "../../util/observer_ptr.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Represents a gray filter.
|
||||
*
|
||||
* The user can select the factors used for every channel.
|
||||
*/
|
||||
class GrayFilterWidget : public FilterFunctionWidget<1, 1>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = FilterFunctionWidget<1, 1>::InputArray;
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = FilterFunctionWidget<1, 1>::OutputArray;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
GrayFilterWidget(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Applys the filter to in and saves the result in out.
|
||||
* @param in The input images.
|
||||
* @param out The output images.
|
||||
*/
|
||||
virtual void applyFilter(InputArray in, OutputArray out) const override;
|
||||
|
||||
/**
|
||||
* @brief Checks whether input can be progressed by the applyFilter
|
||||
*function.
|
||||
* @param in The input images.
|
||||
* @return bool = true: the filter can be executed.
|
||||
* bool = false: the filter cant be executed (e.g. images
|
||||
*have wrong depth)
|
||||
* QString = message for the user (e.g. why the filter can't
|
||||
*be progressed.)
|
||||
*/
|
||||
virtual std::pair<bool, QString> checkInput(InputArray) const override;
|
||||
|
||||
private
|
||||
slots:
|
||||
/**
|
||||
* @brief Sets the number of channels.
|
||||
* @param n The number of channels.
|
||||
*/
|
||||
void setChannel(int n)
|
||||
{
|
||||
setChannel(static_cast<std::size_t>(n));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the number of channels.
|
||||
* @param n The number of channels.
|
||||
*/
|
||||
void setChannel(std::size_t n);
|
||||
|
||||
/**
|
||||
* @brief Sets the standard gray filter. (0.299*R + 0.587*G + 0.114*B)
|
||||
*/
|
||||
void setStd();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief The layout.
|
||||
*/
|
||||
util::ObserverPtr<QVBoxLayout> layout_;
|
||||
/**
|
||||
* @brief The spinbox to select the number of channels.
|
||||
*/
|
||||
util::ObserverPtr<QSpinBox> channel_;
|
||||
/**
|
||||
* @brief Spin boxes for the factor for each channel.
|
||||
*/
|
||||
std::vector<util::ObserverPtr<QDoubleSpinBox>> chanValues_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CVVISUAL_GRAYFILTERWIDGET_HPP
|
||||
@@ -0,0 +1,79 @@
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
|
||||
#include <Qt>
|
||||
#include "QLabel"
|
||||
#include "QSlider"
|
||||
#include "QVBoxLayout"
|
||||
|
||||
#include "../../util/util.hpp"
|
||||
#include "overlayfilterwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
OverlayFilterWidget::OverlayFilterWidget(QWidget *parent)
|
||||
: FilterFunctionWidget<2, 1>{ parent }, opacityOfFilterImg_{ 0.5 }
|
||||
{
|
||||
|
||||
auto layout = util::make_unique<QVBoxLayout>();
|
||||
auto slider = util::make_unique<QSlider>(Qt::Horizontal);
|
||||
|
||||
slider->setRange(0, 100);
|
||||
slider->setSliderPosition(50);
|
||||
slider->setTickPosition(QSlider::TicksAbove);
|
||||
slider->setTickInterval(10);
|
||||
|
||||
connect(slider.get(), SIGNAL(valueChanged(int)), this,
|
||||
SLOT(updateOpacity(int)));
|
||||
|
||||
// Add title of slider and slider to the layout
|
||||
layout->addWidget(util::make_unique<QLabel>(
|
||||
"Select opacity of right image").release());
|
||||
layout->addWidget(slider.release());
|
||||
setLayout(layout.release());
|
||||
|
||||
}
|
||||
|
||||
void OverlayFilterWidget::applyFilter(InputArray in, OutputArray out) const
|
||||
{
|
||||
|
||||
auto check = checkInput(in);
|
||||
if (!check.first)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
cv::addWeighted(in.at(0).get(), 1 - opacityOfFilterImg_, in.at(1).get(),
|
||||
opacityOfFilterImg_, 0, out.at(0).get());
|
||||
|
||||
}
|
||||
|
||||
std::pair<bool, QString> OverlayFilterWidget::checkInput(InputArray in) const
|
||||
{
|
||||
// check whether images have same size
|
||||
if (in.at(0).get().size() != in.at(1).get().size())
|
||||
{
|
||||
return std::make_pair(false, "Images need to have same size");
|
||||
}
|
||||
|
||||
// check whether images have same number of channels
|
||||
if (in.at(0).get().channels() != in.at(1).get().channels())
|
||||
{
|
||||
return std::make_pair(
|
||||
false, "Images need to have same number of channels");
|
||||
}
|
||||
|
||||
|
||||
return std::make_pair(true, "Images can be converted");
|
||||
}
|
||||
|
||||
void OverlayFilterWidget::updateOpacity(int newOpacity)
|
||||
{
|
||||
opacityOfFilterImg_ = newOpacity / 100.0;
|
||||
signalFilterSettingsChanged().emitSignal();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef CVVISUAL_OVERLAY_FILTER_WIDGET_HPP
|
||||
#define CVVISUAL_OVERLAY_FILTER_WIDGET_HPP
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "../../util/observer_ptr.hpp"
|
||||
#include "../filterselectorwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Class providing functionality to compute an overlay image of two
|
||||
* input matrices.
|
||||
*/
|
||||
class OverlayFilterWidget : public FilterFunctionWidget<2, 1>
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = FilterFunctionWidget<2, 1>::InputArray;
|
||||
// std::array<util::Reference<const cv::Mat>,2>
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = FilterFunctionWidget<2, 1>::OutputArray;
|
||||
// std::array<util::Reference<cv::Mat>,1>
|
||||
|
||||
/**
|
||||
* @brief Constructs OverlayFilterWidget with default opacity 0,5.
|
||||
* @param parent The parent of the widget
|
||||
*/
|
||||
OverlayFilterWidget(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* The opacity of the second image while overlaying is indicated by
|
||||
* opacityOfFilterImg_.
|
||||
* @brief Overlays the original images
|
||||
* @param in Array of input matrices
|
||||
* @param out Array of output matrices
|
||||
*/
|
||||
void applyFilter(InputArray in, OutputArray out) const CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* Checks whether the matrices have the same size and same number of
|
||||
* channels.
|
||||
* @brief Checks whether matrices in "in" can be processed by Overlayfilter
|
||||
* @param in Array of input matrices
|
||||
*/
|
||||
std::pair<bool, QString> checkInput(InputArray in) const CV_OVERRIDE;
|
||||
|
||||
private:
|
||||
double opacityOfFilterImg_;
|
||||
//< Opacity of the second input image when ovelaying
|
||||
|
||||
private
|
||||
slots:
|
||||
/**
|
||||
* @brief Sets opacityOfFilterImg_ and emits signFilterSettingsChanged.
|
||||
* @param op New opacity
|
||||
*/
|
||||
void updateOpacity(int op);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,283 @@
|
||||
#include "sobelfilterwidget.hpp"
|
||||
|
||||
#include "opencv2/imgproc.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "../../util/util.hpp"
|
||||
#include "../filterfunctionwidget.hpp"
|
||||
#include "../filterselectorwidget.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
using namespace cv;
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
SobelFilterWidget::SobelFilterWidget(QWidget *parent)
|
||||
: FilterFunctionWidget<1, 1>{ parent }, dx_{ nullptr }, dy_{ nullptr },
|
||||
ksize_{ nullptr }, borderType_{ nullptr }, gray_{ nullptr },
|
||||
grayFilter_{ nullptr }, reorder_{ nullptr }, reorderFilter_{ nullptr }
|
||||
{
|
||||
auto dx = util::make_unique<QSpinBox>();
|
||||
dx_ = *dx;
|
||||
auto dy = util::make_unique<QSpinBox>();
|
||||
dy_ = *dy;
|
||||
auto ksize = util::make_unique<QComboBox>();
|
||||
ksize_ = *ksize;
|
||||
auto borderType = util::make_unique<QComboBox>();
|
||||
borderType_ = *borderType;
|
||||
// set up elements
|
||||
dx_->setRange(0, 6);
|
||||
dy_->setRange(0, 6);
|
||||
ksize_->addItem("1");
|
||||
ksize_->addItem("3");
|
||||
ksize_->addItem("5");
|
||||
ksize_->addItem("7");
|
||||
ksize_->addItem("FILTER_SCHARR(-1)");
|
||||
ksize_->setCurrentIndex(1);
|
||||
|
||||
borderType_->addItem("BORDER_DEFAULT");
|
||||
borderType_->addItem("BORDER_CONSTANT");
|
||||
borderType_->addItem("BORDER_REPLICATE");
|
||||
borderType_->addItem("BORDER_REFLECT");
|
||||
borderType_->addItem("BORDER_REFLECT_101");
|
||||
|
||||
// connect
|
||||
QObject::connect(dx_.getPtr(), SIGNAL(valueChanged(int)),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
QObject::connect(dy_.getPtr(), SIGNAL(valueChanged(int)),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
QObject::connect(ksize_.getPtr(), SIGNAL(currentIndexChanged(int)),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
QObject::connect(borderType_.getPtr(), SIGNAL(currentIndexChanged(int)),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
|
||||
// subfilter reorder
|
||||
auto reorder = util::make_unique<QCheckBox>("Reorder channels");
|
||||
reorder_ = *reorder;
|
||||
auto reorderFilter = util::make_unique<ChannelReorderFilter>();
|
||||
reorderFilter_ = *reorderFilter;
|
||||
reorder_->setChecked(false);
|
||||
reorderFilter_->setVisible(false);
|
||||
// visible
|
||||
QObject::connect(reorder_.getPtr(), SIGNAL(clicked(bool)),
|
||||
reorderFilter_.getPtr(), SLOT(setVisible(bool)));
|
||||
// settings
|
||||
QObject::connect(reorder_.getPtr(), SIGNAL(clicked()),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
QObject::connect(
|
||||
&(reorderFilter_.getPtr()->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()), &(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
|
||||
// subfilter gray
|
||||
auto gray = util::make_unique<QCheckBox>("Apply gray filter");
|
||||
gray_ = *gray;
|
||||
auto grayFilter = util::make_unique<GrayFilterWidget>();
|
||||
grayFilter_ = *grayFilter;
|
||||
gray_->setChecked(false);
|
||||
grayFilter_->setVisible(false);
|
||||
// visible
|
||||
QObject::connect(gray_.getPtr(), SIGNAL(clicked(bool)),
|
||||
grayFilter_.getPtr(), SLOT(setVisible(bool)));
|
||||
// settings
|
||||
QObject::connect(gray_.getPtr(), SIGNAL(clicked()),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
QObject::connect(&(grayFilter_.getPtr()->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
|
||||
// build ui
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
lay->addWidget(reorder.release());
|
||||
lay->addWidget(reorderFilter.release());
|
||||
lay->addWidget(gray.release());
|
||||
lay->addWidget(grayFilter.release());
|
||||
lay->addWidget(util::make_unique<QLabel>("dx").release());
|
||||
lay->addWidget(dx.release());
|
||||
lay->addWidget(util::make_unique<QLabel>("dy").release());
|
||||
lay->addWidget(dy.release());
|
||||
lay->addWidget(util::make_unique<QLabel>("ksize").release());
|
||||
lay->addWidget(ksize.release());
|
||||
lay->addWidget(util::make_unique<QLabel>("borderType").release());
|
||||
lay->addWidget(borderType.release());
|
||||
setLayout(lay.release());
|
||||
// emit first update
|
||||
signalFilterSettingsChanged().emitSignal();
|
||||
}
|
||||
|
||||
void SobelFilterWidget::applyFilter(InputArray in, OutputArray out) const
|
||||
{
|
||||
int ksize = 3;
|
||||
switch (ksize_->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
ksize = 1;
|
||||
break;
|
||||
case 1:
|
||||
ksize = 3;
|
||||
break;
|
||||
case 2:
|
||||
ksize = 5;
|
||||
break;
|
||||
case 3:
|
||||
ksize = 7;
|
||||
break;
|
||||
case 4:
|
||||
ksize = FILTER_SCHARR;
|
||||
break;
|
||||
}
|
||||
|
||||
int borderType = cv::BORDER_DEFAULT;
|
||||
switch (borderType_->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
borderType = cv::BORDER_DEFAULT;
|
||||
break;
|
||||
case 1:
|
||||
borderType = cv::BORDER_CONSTANT;
|
||||
break;
|
||||
case 2:
|
||||
borderType = cv::BORDER_REPLICATE;
|
||||
break;
|
||||
case 3:
|
||||
borderType = cv::BORDER_REFLECT;
|
||||
break;
|
||||
case 4:
|
||||
borderType = cv::BORDER_REFLECT_101;
|
||||
break;
|
||||
}
|
||||
|
||||
int dx = dx_->value();
|
||||
int dy = dy_->value();
|
||||
// apply filter
|
||||
cvv::util::Reference<const cv::Mat> inar = in.at(0).get();
|
||||
cvv::util::Reference<cv::Mat> outar = out.at(0).get();
|
||||
// first reorder
|
||||
if (reorder_->isChecked())
|
||||
{
|
||||
reorderFilter_->applyFilter({ { inar } }, { { outar } });
|
||||
// out should be new input
|
||||
inar = outar.get();
|
||||
}
|
||||
// then gray
|
||||
if (gray_->isChecked())
|
||||
{
|
||||
grayFilter_->applyFilter({ { inar } }, { { outar } });
|
||||
// out should be new input
|
||||
inar = outar.get();
|
||||
}
|
||||
Sobel(inar.get(), outar.get(), -1, dx, dy, ksize, 1, 0, borderType);
|
||||
}
|
||||
|
||||
std::pair<bool, QString> SobelFilterWidget::checkInput(InputArray in) const
|
||||
{
|
||||
// check depth in CV_8U,CV_16U,CV_16S,CV_32F,CV_64F
|
||||
switch (in.at(0).get().depth())
|
||||
{
|
||||
case CV_8U:
|
||||
case CV_16U:
|
||||
case CV_16S:
|
||||
case CV_32F:
|
||||
case CV_64F:
|
||||
break;
|
||||
default:
|
||||
return { false, QString("unsupported depth: ") +
|
||||
QString::number(in.at(0).get().depth()) };
|
||||
}
|
||||
|
||||
// check subfilter
|
||||
if (gray_->isChecked())
|
||||
{
|
||||
auto resultGray = grayFilter_->checkInput(in);
|
||||
if (!resultGray.first)
|
||||
{
|
||||
return resultGray;
|
||||
}
|
||||
}
|
||||
if (reorder_->isChecked())
|
||||
{
|
||||
auto resultReorder = reorderFilter_->checkInput(in);
|
||||
if (!resultReorder.first)
|
||||
{
|
||||
return resultReorder;
|
||||
}
|
||||
}
|
||||
|
||||
// check channels
|
||||
if (!(gray_->isChecked())) // gray filter => channels will be 1
|
||||
{
|
||||
if ((reorder_->isChecked()) &&
|
||||
(reorderFilter_->outputChannels() > 4)) // no gray
|
||||
{
|
||||
return { false, "channels>4 (use gray filter or "
|
||||
"reorder with <=4 output channels)" };
|
||||
}
|
||||
else if ((in.at(0).get().channels() > 4)) // no gray filter +
|
||||
// reorder
|
||||
{
|
||||
return { false, "channels>4 (use gray filter or "
|
||||
"reorder with <=4 output channels)" };
|
||||
}
|
||||
}
|
||||
|
||||
int dx = dx_->value();
|
||||
int dy = dy_->value();
|
||||
if (dx == 0 && dy == 0)
|
||||
{
|
||||
return { false, "dx=0 and dy=0" };
|
||||
}
|
||||
// dx,dy<ksize, if sharr: dx XOR dy
|
||||
if (dx_->value() == 0 && dy_->value() == 0)
|
||||
{
|
||||
return { false, "dx=0 and dy=0" };
|
||||
}
|
||||
|
||||
int ksize = 3;
|
||||
switch (ksize_->currentIndex())
|
||||
{
|
||||
case 0:
|
||||
ksize = 1;
|
||||
break;
|
||||
case 1:
|
||||
ksize = 3;
|
||||
break;
|
||||
case 2:
|
||||
ksize = 5;
|
||||
break;
|
||||
case 3:
|
||||
ksize = 7;
|
||||
break;
|
||||
case 4:
|
||||
ksize = FILTER_SCHARR;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ksize == FILTER_SCHARR)
|
||||
{
|
||||
if (dx + dy != 1)
|
||||
{
|
||||
return { false, "ksize=FILTER_SCHARR but dx+dy != 1" };
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ((dx >= 3 || dy >= 3) && (dx >= ksize || dy >= ksize))
|
||||
{
|
||||
return { false, "dx or dy is to big" };
|
||||
}
|
||||
}
|
||||
|
||||
return { true, "" };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
#ifndef CVVISUAL_SOBELFILTERWIDGET_HPP
|
||||
#define CVVISUAL_SOBELFILTERWIDGET_HPP
|
||||
|
||||
#include <QSpinBox>
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "../../util/observer_ptr.hpp"
|
||||
#include "../filterfunctionwidget.hpp"
|
||||
#include "grayfilterwidget.hpp"
|
||||
#include "channelreorderfilter.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief Represents the opencv sobel filter.
|
||||
*/
|
||||
class SobelFilterWidget : public FilterFunctionWidget<1, 1>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = typename FilterFunctionWidget<1, 1>::InputArray;
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = typename FilterFunctionWidget<1, 1>::OutputArray;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
*/
|
||||
SobelFilterWidget(QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Applys the filter to in and saves the result in out.
|
||||
* @param in The input images.
|
||||
* @param out The output images.
|
||||
*/
|
||||
virtual void applyFilter(InputArray in, OutputArray out) const override;
|
||||
|
||||
/**
|
||||
* @brief Checks whether input can be progressed by the applyFilter
|
||||
*function.
|
||||
* @param in The input images.
|
||||
* @return bool = true: the filter can be executed.
|
||||
* bool = false: the filter cant be executed (e.g. images
|
||||
*have wrong depth)
|
||||
* QString = message for the user (e.g. why the filter can't
|
||||
*be progressed.)
|
||||
*/
|
||||
virtual std::pair<bool, QString> checkInput(InputArray in) const
|
||||
override;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Selection for parameter dx.
|
||||
*/
|
||||
util::ObserverPtr<QSpinBox> dx_;
|
||||
/**
|
||||
* @brief Selection for parameter dy.
|
||||
*/
|
||||
util::ObserverPtr<QSpinBox> dy_;
|
||||
/**
|
||||
* @brief Selection for parameter ksize.
|
||||
*/
|
||||
util::ObserverPtr<QComboBox> ksize_;
|
||||
/**
|
||||
* @brief Selection for parameter borderType.
|
||||
*/
|
||||
util::ObserverPtr<QComboBox> borderType_;
|
||||
/**
|
||||
* @brief Wheather a gray filter should be applied first (after
|
||||
* reorder).
|
||||
*/
|
||||
util::ObserverPtr<QCheckBox> gray_;
|
||||
/**
|
||||
* @brief a gray filter.
|
||||
*/
|
||||
util::ObserverPtr<GrayFilterWidget> grayFilter_;
|
||||
/**
|
||||
* @brief Wheather a reorder filter should be applied first.
|
||||
*/
|
||||
util::ObserverPtr<QCheckBox> reorder_;
|
||||
/**
|
||||
* @brief a reorder filter.
|
||||
*/
|
||||
util::ObserverPtr<ChannelReorderFilter> reorderFilter_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif // SOBELFILTERWIDGET_HPP
|
||||
@@ -0,0 +1,102 @@
|
||||
#ifndef CVVISUAL_FILTERFUNCTIONWIDGET_HPP
|
||||
#define CVVISUAL_FILTERFUNCTIONWIDGET_HPP
|
||||
// STD
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
// QT
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
|
||||
// OCV
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
// cvv
|
||||
#include "signalslot.hpp"
|
||||
#include "../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief The input type for FilterFunctionWidgets.
|
||||
*/
|
||||
template <std::size_t In>
|
||||
using CvvInputArray = std::array<util::Reference<const cv::Mat>, In>;
|
||||
|
||||
/**
|
||||
* @brief The output type for FilterFunctionWidgets.
|
||||
*/
|
||||
template <std::size_t Out>
|
||||
using CvvOutputArray = std::array<util::Reference<cv::Mat>, Out>;
|
||||
|
||||
/**
|
||||
* @brief The type for the input of the filter.
|
||||
*
|
||||
* Inherit from it if you want to provide an image filter.
|
||||
* Use the widget to let the user choose parameters.
|
||||
* Emit stateChanged when user input leads to different parameters.
|
||||
*
|
||||
* @tparam In The number of input images.
|
||||
* @tparam Out The number of output images.
|
||||
*/
|
||||
template <std::size_t In, std::size_t Out>
|
||||
class FilterFunctionWidget : public QWidget
|
||||
{
|
||||
static_assert(Out > 0, "Out should be >0.");
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = CvvInputArray<In>;
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = CvvOutputArray<Out>;
|
||||
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param parent Parent widget.
|
||||
*/
|
||||
FilterFunctionWidget(QWidget *parent = nullptr)
|
||||
: QWidget{ parent }, signFilterSettingsChanged_{}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Applys the filter to in and saves the result in out.
|
||||
* @param in The input images.
|
||||
* @param out The output images.
|
||||
*/
|
||||
virtual void applyFilter(InputArray in, OutputArray out) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Checks whether input can be progressed by the applyFilter
|
||||
*function.
|
||||
* @param in The input images.
|
||||
* @return bool = true: the filter can be executed.
|
||||
* bool = false: the filter cant be executed (e.g. images
|
||||
*have wrong depth)
|
||||
* QString = message for the user (e.g. why the filter can't
|
||||
*be progressed.)
|
||||
*/
|
||||
virtual std::pair<bool, QString> checkInput(InputArray in) const = 0;
|
||||
|
||||
const Signal &signalFilterSettingsChanged() const
|
||||
{
|
||||
return signFilterSettingsChanged_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Signal to emit when user input leads to different parameters.
|
||||
*/
|
||||
const Signal signFilterSettingsChanged_;
|
||||
};
|
||||
}
|
||||
} // end namespaces qtutil, cvv
|
||||
#endif // CVVISUAL_FILTERFUNCTIONWIDGET_HPP
|
||||
@@ -0,0 +1,206 @@
|
||||
#ifndef CVVISUAL_FILTERSELECTORWIDGET_HPP
|
||||
#define CVVISUAL_FILTERSELECTORWIDGET_HPP
|
||||
// STD
|
||||
#include <stdexcept>
|
||||
#include <array>
|
||||
#include <type_traits>
|
||||
|
||||
// QT
|
||||
#include <QWidget>
|
||||
#include <QString>
|
||||
#include <QComboBox>
|
||||
#include <QPushButton>
|
||||
|
||||
// OCV
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
// CVV
|
||||
#include "signalslot.hpp"
|
||||
#include "registerhelper.hpp"
|
||||
#include "filterfunctionwidget.hpp"
|
||||
#include "../util/util.hpp"
|
||||
#include "../util/observer_ptr.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
// forward
|
||||
template <std::size_t In, std::size_t Out, class Filter>
|
||||
bool registerFilter(const QString &name);
|
||||
|
||||
/**
|
||||
* @brief The FilterSelectorWidget class
|
||||
*/
|
||||
template <std::size_t In, std::size_t Out>
|
||||
class FilterSelectorWidget
|
||||
: public RegisterHelper<FilterFunctionWidget<In, Out>, QWidget *>,
|
||||
public FilterFunctionWidget<In, Out>
|
||||
{
|
||||
static_assert(Out > 0, "Out must not be 0!");
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief The input type.
|
||||
*/
|
||||
using InputArray = typename FilterFunctionWidget<In, Out>::InputArray;
|
||||
|
||||
/**
|
||||
* @brief The output type.
|
||||
*/
|
||||
using OutputArray = typename FilterFunctionWidget<In, Out>::OutputArray;
|
||||
|
||||
/**
|
||||
* @brief Constuctor
|
||||
* @param parent The parent widget.
|
||||
*/
|
||||
FilterSelectorWidget(QWidget *parent = nullptr)
|
||||
: RegisterHelper<FilterFunctionWidget<In, Out>, QWidget *>{},
|
||||
FilterFunctionWidget<In, Out>{ parent }, layout_{ nullptr },
|
||||
currentFilter_{ nullptr }, slotFilterSelected_{ [this]()
|
||||
{
|
||||
this->updatedSelectedFilter();
|
||||
} }
|
||||
{
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
layout_ = *lay;
|
||||
this->layout_->setAlignment(Qt::AlignTop);
|
||||
this->layout_->setSpacing(0);
|
||||
this->layout_->addWidget((this->comboBox_));
|
||||
// connect elem selected with update for it
|
||||
QObject::connect(&(this->signalElementSelected()),
|
||||
SIGNAL(signal(QString)),
|
||||
&(this->slotFilterSelected_), SLOT(slot()));
|
||||
this->setLayout(lay.release());
|
||||
// update for initial selection (if it is valid)
|
||||
if (this->has(this->selection()))
|
||||
{
|
||||
updatedSelectedFilter();
|
||||
}
|
||||
// add an apply button
|
||||
auto button = util::make_unique<QPushButton>("apply");
|
||||
// connect it
|
||||
QObject::connect(button.get(), SIGNAL(clicked()),
|
||||
&(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
this->layout_->addWidget(button.release());
|
||||
}
|
||||
|
||||
~FilterSelectorWidget()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Applies the selected filter.
|
||||
* @param in Input images.
|
||||
* @param out Output images.
|
||||
* @throw std::invalid_argument checkInput(in).first==false
|
||||
*/
|
||||
virtual void applyFilter(InputArray in, OutputArray out) const override
|
||||
{
|
||||
auto check = checkInput(in);
|
||||
if (!check.first)
|
||||
{
|
||||
throw std::invalid_argument{
|
||||
check.second.toStdString()
|
||||
};
|
||||
}
|
||||
return currentFilter_->applyFilter(in, out);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Checks whether input can be progressed by the selected filter.
|
||||
* @param in The input images.
|
||||
* @return bool = true: the filter can be executed.
|
||||
* bool = false: the filter cant be executed (e.g. images
|
||||
*have wrong depth)
|
||||
* QString = message for the user (e.g. why the filter can't
|
||||
*be progressed.)
|
||||
*/
|
||||
virtual std::pair<bool, QString> checkInput(InputArray in) const
|
||||
override
|
||||
{
|
||||
if (!currentFilter_)
|
||||
{
|
||||
return { false, "No entry selected." };
|
||||
}
|
||||
return currentFilter_->checkInput(in);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Registers a FilterFunctionWidget with a given name.
|
||||
* @param name The name.
|
||||
* @return true: If the function was registered. false: If the name was
|
||||
* taken
|
||||
* (the function was not registered!)
|
||||
*/
|
||||
template <class Filter> static bool registerFilter(const QString &name)
|
||||
{
|
||||
return qtutil::registerFilter<In, Out, Filter>(name);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The layout
|
||||
*/
|
||||
util::ObserverPtr<QVBoxLayout> layout_;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Performs the update after a selection occurred.
|
||||
*/
|
||||
void updatedSelectedFilter()
|
||||
{
|
||||
if ((this->currentFilter_))
|
||||
{
|
||||
layout_->removeWidget((this->currentFilter_.getPtr()));
|
||||
// disconnect
|
||||
QObject::disconnect(
|
||||
&(this->currentFilter_
|
||||
->signalFilterSettingsChanged()),
|
||||
0, &(this->signalFilterSettingsChanged()), 0);
|
||||
currentFilter_->deleteLater();
|
||||
}
|
||||
auto filt = (*this)()(nullptr);
|
||||
this->currentFilter_ = *filt;
|
||||
this->layout_->insertWidget(2, filt.release());
|
||||
// pass signal
|
||||
QObject::connect(
|
||||
&(this->currentFilter_->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()), &(this->signalFilterSettingsChanged()),
|
||||
SIGNAL(signal()));
|
||||
// settings changed
|
||||
this->signalFilterSettingsChanged().emitSignal();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief the current filter
|
||||
*/
|
||||
util::ObserverPtr<FilterFunctionWidget<In, Out>> currentFilter_;
|
||||
/**
|
||||
* @brief Slot called when user changes selection
|
||||
*/
|
||||
const Slot slotFilterSelected_;
|
||||
}; // FilterSelectorWidget
|
||||
|
||||
/**
|
||||
* @brief Registers a FilterFunctionWidget with a given name.
|
||||
* @param name The name.
|
||||
* @return true: If the function was registered. false: If the name was taken
|
||||
* (the function was not registered!)
|
||||
*/
|
||||
template <std::size_t In, std::size_t Out, class Filter>
|
||||
bool registerFilter(const QString &name)
|
||||
{
|
||||
return FilterSelectorWidget<In, Out>::registerElement(
|
||||
name, [](QWidget *parent)
|
||||
{
|
||||
return std::unique_ptr<FilterFunctionWidget<In, Out>>{
|
||||
new Filter{ parent }
|
||||
};
|
||||
});
|
||||
}
|
||||
}
|
||||
} // end namespaces qtutil, cvv
|
||||
#endif // CVVISUAL_FILTERSELECTORWIDGET_HPP
|
||||
@@ -0,0 +1,139 @@
|
||||
#include "histogram.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
Histogram::Histogram(const cv::Mat& mat, QWidget* parent)
|
||||
:QWidget{parent},
|
||||
histSize_(512, 200),
|
||||
histLineWidth_(2),
|
||||
histBackgroundColor_(255, 255, 255)
|
||||
{
|
||||
setMat(mat);
|
||||
|
||||
zoomableImage = new ZoomableImage();
|
||||
auto layout = new QHBoxLayout();
|
||||
layout->addWidget(zoomableImage);
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
void Histogram::setMat(const cv::Mat& mat)
|
||||
{
|
||||
mat_ = mat;
|
||||
}
|
||||
|
||||
cv::Rect Histogram::qrect2cvrect(const cv::Mat& mat, QRectF qrect)
|
||||
{
|
||||
double x1, y1, x2, y2;
|
||||
qrect.getCoords(&x1, &y1, &x2, &y2);
|
||||
x1 = std::max(0.0, x1);
|
||||
y1 = std::max(0.0, y1);
|
||||
x2 = std::min(static_cast<double>(mat.size().width), x2);
|
||||
y2 = std::min(static_cast<double>(mat.size().height), y2);
|
||||
double width = x2 - x1;
|
||||
double height = y2 - y1;
|
||||
|
||||
return cv::Rect(x1, y1, width, height);
|
||||
}
|
||||
|
||||
void Histogram::setArea(QRectF rect, qreal zoom)
|
||||
{
|
||||
(void)zoom;
|
||||
|
||||
channelHists_ = calcHist(mat_, qrect2cvrect(mat_, rect));
|
||||
histMat_ = drawHist(channelHists_, histSize_, histLineWidth_, histBackgroundColor_);
|
||||
|
||||
zoomableImage->setMat(histMat_);
|
||||
zoomableImage->showFullImage();
|
||||
}
|
||||
|
||||
std::vector<cv::Mat> Histogram::calcHist(cv::Mat mat, cv::Rect rect, int bins, float rangeMin,
|
||||
float rangeMax)
|
||||
{
|
||||
cv::Mat rectMat(mat, rect);
|
||||
cv::Mat histMat;
|
||||
|
||||
std::vector<cv::Mat> channelPlanes = splitChannels(rectMat);
|
||||
|
||||
int histSize = bins;
|
||||
float range[] = {rangeMin, rangeMax};
|
||||
const float* histRange = {range};
|
||||
bool uniform = true;
|
||||
bool accumulate = false;
|
||||
|
||||
std::vector<cv::Mat> channelHists(channelPlanes.size());
|
||||
|
||||
for (size_t chan = 0; chan < channelPlanes.size(); chan++)
|
||||
{
|
||||
cv::calcHist(&channelPlanes[chan], 1, 0, cv::Mat(), channelHists[chan], 1, &histSize,
|
||||
&histRange, uniform, accumulate);
|
||||
}
|
||||
|
||||
return channelHists;
|
||||
}
|
||||
|
||||
cv::Mat Histogram::drawHist(const std::vector<cv::Mat>& channelHists, cv::Size histSize,
|
||||
int lineWidth, const cv::Scalar& backgroundColor)
|
||||
{
|
||||
int binCount = channelHists[0].rows;
|
||||
int binWidth = cvRound(double(histSize.width)/binCount);
|
||||
std::vector<cv::Scalar> colors{cv::Scalar(255, 0, 0), cv::Scalar(0, 255, 0),
|
||||
cv::Scalar(0, 0, 255), cv::Scalar(0, 0, 0)}; // BGR
|
||||
|
||||
cv::Mat histMat(histSize, CV_8UC3, backgroundColor);
|
||||
|
||||
double maxVal = 0;
|
||||
for (auto& hist : channelHists)
|
||||
{
|
||||
double tmpMaxVal;
|
||||
cv::minMaxLoc(hist, NULL, &tmpMaxVal);
|
||||
maxVal = std::max(maxVal, tmpMaxVal);
|
||||
}
|
||||
|
||||
double valScale = histSize.height / maxVal;
|
||||
for (size_t channel = 0; channel < channelHists.size(); channel++)
|
||||
{
|
||||
auto& hist = channelHists[channel];
|
||||
auto& color = colors[channel];
|
||||
|
||||
for (int bin = 1; bin < binCount; bin++)
|
||||
{
|
||||
//printf("%zd:%d=%f\n", channel, bin, hist.at<float>(bin));
|
||||
auto pt1 = cv::Point(binWidth * (bin-1),
|
||||
histSize.height - cvRound(hist.at<float>(bin-1) * valScale));
|
||||
auto pt2 = cv::Point(binWidth * bin,
|
||||
histSize.height - cvRound(hist.at<float>(bin) * valScale));
|
||||
cv::line(histMat, pt1, pt2, color, lineWidth);
|
||||
}
|
||||
}
|
||||
|
||||
int binTextStep = binCount / 5;
|
||||
binTextStep = binTextStep - (binTextStep % 10); // round to tens
|
||||
int fontFace = cv::FONT_HERSHEY_SCRIPT_SIMPLEX;
|
||||
double fontScale = 0.5;
|
||||
auto textColor = cv::Scalar::all(0);
|
||||
int thickness = 1;
|
||||
for (int binTextId = 0; binTextId < binCount; binTextId += binTextStep)
|
||||
{
|
||||
auto text = QString::number(binTextId).toLatin1();
|
||||
auto textSize = cv::getTextSize(text.data(), fontFace, fontScale, thickness, NULL);
|
||||
auto textPt = cv::Point(std::max(0, binWidth * binTextId - textSize.width/2), histSize.height);
|
||||
cv::putText(histMat, text.data(), textPt, fontFace, fontScale, textColor, thickness);
|
||||
auto linePt1 = cv::Point(binWidth * binTextId, 0);
|
||||
auto linePt2 = cv::Point(binWidth * binTextId, histSize.height - textSize.height);
|
||||
cv::line(histMat, linePt1, linePt2, textColor);
|
||||
}
|
||||
|
||||
return histMat;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
#ifndef CVVISUAL_HISTOGRAM_HPP
|
||||
#define CVVISUAL_HISTOGRAM_HPP
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include <opencv2/core.hpp>
|
||||
#include <opencv2/imgproc.hpp>
|
||||
|
||||
#include "zoomableimage.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
class Histogram : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
Histogram(const cv::Mat& mat = cv::Mat{}, QWidget* parent = nullptr);
|
||||
|
||||
void setMat(const cv::Mat& mat);
|
||||
std::vector<cv::Mat> calcHist(cv::Mat mat, cv::Rect rect, int bins = 256, float rangeMin = 0.0, float rangeMax = 256.0);
|
||||
cv::Mat drawHist(const std::vector<cv::Mat>& channelHists, cv::Size histSize, int lineWidth = 2, const cv::Scalar& backgroundColor = cv::Scalar(255, 255, 255));
|
||||
|
||||
public slots:
|
||||
void setArea(QRectF, qreal);
|
||||
|
||||
private:
|
||||
cv::Rect qrect2cvrect(const cv::Mat& mat, QRectF qrect);
|
||||
|
||||
cv::Mat mat_;
|
||||
std::vector<cv::Mat> channelHists_;
|
||||
cv::Mat histMat_;
|
||||
cv::Size histSize_;
|
||||
int histLineWidth_;
|
||||
cv::Scalar histBackgroundColor_;
|
||||
ZoomableImage* zoomableImage;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CVVISUAL_HISTOGRAM_HPP
|
||||
@@ -0,0 +1,29 @@
|
||||
#include "histogramoptpanel.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QCheckBox>
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
HistogramOptPanel::HistogramOptPanel(const Histogram& hist, bool showHideButton, QWidget* parent)
|
||||
:QWidget{parent}
|
||||
{
|
||||
auto layout = new QVBoxLayout();
|
||||
layout->setContentsMargins(0,0,0,0);
|
||||
|
||||
if (showHideButton) {
|
||||
auto showCheckbox = new QCheckBox("Show Histogram");
|
||||
showCheckbox->setChecked(false);
|
||||
connect(showCheckbox, SIGNAL(clicked(bool)), &hist, SLOT(setVisible(bool)));
|
||||
layout->addWidget(showCheckbox);
|
||||
}
|
||||
|
||||
setLayout(layout);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
#ifndef CVVISUAL_HISTOGRAM_OPT_PANEL
|
||||
#define CVVISUAL_HISTOGRAM_OPT_PANEL
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
#include "histogram.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
class HistogramOptPanel
|
||||
: public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
HistogramOptPanel(const Histogram& hist, bool showHideButton = true, QWidget* parent = nullptr);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif // CVVISUAL_HISTOGRAM_OPT_PANEL
|
||||
@@ -0,0 +1,134 @@
|
||||
#ifndef CVVISUAL_INTERVALLSELECTOR_HPP
|
||||
#define CVVISUAL_INTERVALLSELECTOR_HPP
|
||||
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
|
||||
#include <QWidget>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QRadioButton>
|
||||
#include <QCheckBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
#include <QButtonGroup>
|
||||
|
||||
#include "../util/util.hpp"
|
||||
#include "../util/observer_ptr.hpp"
|
||||
#include "signalslot.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Selects elements from a double range. The elements are converted to a
|
||||
* double using the
|
||||
* given DoubleExtractor functor.
|
||||
*/
|
||||
class IntervallSelector : public QWidget
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor
|
||||
* @param min Minimal value
|
||||
* @param max Maximal value
|
||||
* @param parent Parent widget
|
||||
*/
|
||||
IntervallSelector(double min, double max, QWidget *parent = nullptr)
|
||||
: QWidget{ parent }, sigSettingsChanged_{}, min_{ nullptr },
|
||||
max_{ nullptr }, complement_{ nullptr }
|
||||
{
|
||||
auto minb = util::make_unique<QDoubleSpinBox>();
|
||||
min_ = *minb;
|
||||
|
||||
auto maxb = util::make_unique<QDoubleSpinBox>();
|
||||
max_ = *maxb;
|
||||
auto complement =
|
||||
util::make_unique<QCheckBox>("Select the complement");
|
||||
complement_ = *complement;
|
||||
|
||||
// set ranges
|
||||
minb->setRange(min, max);
|
||||
maxb->setValue(min);
|
||||
maxb->setRange(min, max);
|
||||
maxb->setValue(max);
|
||||
// connect
|
||||
QObject::connect(min_.getPtr(), SIGNAL(valueChanged(double)),
|
||||
&sigSettingsChanged_, SIGNAL(signal()));
|
||||
QObject::connect(max_.getPtr(), SIGNAL(valueChanged(double)),
|
||||
&sigSettingsChanged_, SIGNAL(signal()));
|
||||
QObject::connect(complement_.getPtr(), SIGNAL(clicked()),
|
||||
&sigSettingsChanged_, SIGNAL(signal()));
|
||||
// build ui
|
||||
auto lay = util::make_unique<QVBoxLayout>();
|
||||
lay->setContentsMargins(0, 0, 0, 0);
|
||||
lay->addWidget(
|
||||
util::make_unique<QLabel>(QString{"From lower bound ("}+
|
||||
QString::number(min)+QString{"):"}).release());
|
||||
lay->addWidget(minb.release());
|
||||
lay->addWidget(
|
||||
util::make_unique<QLabel>(QString{"To upper bound ("}+
|
||||
QString::number(max)+QString{"):"}).release());
|
||||
lay->addWidget(maxb.release());
|
||||
lay->addWidget(complement.release());
|
||||
setLayout(lay.release());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns elements from the selected range.
|
||||
* @param selection The selection.
|
||||
* @param extr Extractor functor (has to be double extr(Type))
|
||||
* @return the selected values
|
||||
*/
|
||||
template <class Type, class DoubleExtractor>
|
||||
std::vector<Type> select(std::vector<Type> selection,
|
||||
DoubleExtractor extr) const
|
||||
{
|
||||
std::vector<Type> result;
|
||||
|
||||
bool complement = complement_->isChecked();
|
||||
|
||||
std::copy_if(selection.begin(), selection.end(),
|
||||
std::back_insert_iterator<std::vector<Type>>(
|
||||
result),
|
||||
[=](Type t1)
|
||||
{
|
||||
return complement !=
|
||||
// check weather the element is in the interval
|
||||
(((min_->value()) <= extr(t1)) &&
|
||||
(extr(t1) <= max_->value()));
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the signal emitted when settings are changed.
|
||||
* @return The signal emitted when settings are changed.
|
||||
*/
|
||||
const Signal &signalSettingsChanged() const
|
||||
{
|
||||
return sigSettingsChanged_;
|
||||
}
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Emitted when settings are changed.
|
||||
*/
|
||||
const Signal sigSettingsChanged_;
|
||||
/**
|
||||
* @brief Spinbox to select the lower bound
|
||||
*/
|
||||
util::ObserverPtr<QDoubleSpinBox> min_;
|
||||
/**
|
||||
* @brief Spinbox to select the upper bound
|
||||
*/
|
||||
util::ObserverPtr<QDoubleSpinBox> max_;
|
||||
/**
|
||||
* @brief Weather the complement should be selected
|
||||
*/
|
||||
util::ObserverPtr<QCheckBox> complement_;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif // CVVISUAL_INTERVALLSELECTOR_HPP
|
||||
@@ -0,0 +1,81 @@
|
||||
#ifndef CVVISUAL_COLOR_UTIL
|
||||
#define CVVISUAL_COLOR_UTIL
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include <QColor>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief Returns the false color. (BGR)
|
||||
* @param value The double to convert.
|
||||
* @return The false color.
|
||||
*/
|
||||
cv::Vec<uint8_t,3> inline falseColor(double d)
|
||||
{
|
||||
static const std::vector<cv::Vec<uint8_t,3>> points{
|
||||
{176,0,13}, // 0.0
|
||||
{163,0,31}, // 0.1
|
||||
{131,0,75}, // 0.2
|
||||
{86,0,137}, // 0.3
|
||||
{36,0,205}, // 0.4
|
||||
{0,0,255}, // 0.5
|
||||
{0,49,255}, // 0.6
|
||||
{0,119,255}, // 0.7
|
||||
{0,183,255}, // 0.8
|
||||
{0,229,255}, // 0.9
|
||||
{0,251,255} // 1.0
|
||||
};
|
||||
|
||||
if(d<0)
|
||||
{
|
||||
return points[0];
|
||||
}
|
||||
if(d>1)
|
||||
{
|
||||
return points[10];
|
||||
}
|
||||
|
||||
int low = std::floor(d/0.1);
|
||||
int high = std::ceil(d/0.1);
|
||||
|
||||
//interp. factor
|
||||
double factorHi = d/0.1 - low;
|
||||
double factorLo = 1 - factorHi;
|
||||
|
||||
|
||||
return factorLo*(points[low]) + factorHi*(points[high]);
|
||||
/*{cv::saturate_cast<uint8_t>( (factorLo*(points[low][0]) + factorHi*(points[high][0]))),
|
||||
cv::saturate_cast<uint8_t>( (factorLo*(points[low][1]) + factorHi*(points[high][1]))),
|
||||
cv::saturate_cast<uint8_t>( (factorLo*(points[low][2]) + factorHi*(points[high][2])))};*/
|
||||
}
|
||||
|
||||
QColor inline getFalseColor(double value, double max, double min)
|
||||
{
|
||||
cv::Vec<uint8_t,3> color;
|
||||
if(value<=min)
|
||||
{
|
||||
color=falseColor(0);
|
||||
} else if(value>=max)
|
||||
{
|
||||
color=falseColor(1);
|
||||
}else if(max<=min)
|
||||
{
|
||||
color=falseColor(0);
|
||||
}else {
|
||||
double val01 = (value-min) / (max - min);
|
||||
color=falseColor(val01);
|
||||
}
|
||||
|
||||
return QColor{color[2],color[1],color[0]};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,100 @@
|
||||
#include "cvvkeypoint.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
CVVKeyPoint::CVVKeyPoint(const cv::KeyPoint &key, qtutil::ZoomableImage *image,
|
||||
QPen pen, QBrush brush, QGraphicsItem *parent)
|
||||
: QGraphicsObject{ parent }, cv::KeyPoint{ key }, image_{ image }, pen_{ pen },
|
||||
brush_{ brush }, show_{ true }
|
||||
{
|
||||
//setFlag(QGraphicsItem::ItemIsSelectable, true);
|
||||
//setSelected(true);
|
||||
setToolTip(QString
|
||||
{ "KeyPoint size: %1 \n angle %2 \n response %3 " }
|
||||
.arg(size)
|
||||
.arg(angle)
|
||||
.arg(response));
|
||||
if (image != nullptr)
|
||||
{
|
||||
updateImageSet(image->visibleArea(), image->zoom());
|
||||
connect(image, SIGNAL(updateArea(QRectF, qreal)), this,
|
||||
SLOT(updateImageSet(QRectF, qreal)));
|
||||
}
|
||||
}
|
||||
|
||||
void CVVKeyPoint::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
|
||||
QWidget *)
|
||||
{
|
||||
painter->setPen(pen_);
|
||||
painter->setBrush(brush_);
|
||||
painter->drawEllipse(boundingRect());
|
||||
}
|
||||
|
||||
void CVVKeyPoint::setZoomableImage(ZoomableImage *image)
|
||||
{
|
||||
image_ = image;
|
||||
updateImageSet(image->visibleArea(), image->zoom());
|
||||
connect(image, SIGNAL(updateArea(QRectF, qreal)), this,
|
||||
SLOT(updateImageSet(const QRectF &, const qreal &)));
|
||||
}
|
||||
|
||||
bool CVVKeyPoint::operator==(const cv::KeyPoint &o)
|
||||
{
|
||||
return o.pt == pt && o.size == size &&
|
||||
o.angle == angle && o.response == response &&
|
||||
o.octave == octave && o.class_id == class_id;
|
||||
}
|
||||
|
||||
void CVVKeyPoint::updateSettings(KeyPointSettings &settings)
|
||||
{
|
||||
settings.setSettings(*this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void CVVKeyPoint::setPen(const QPen &pen)
|
||||
{
|
||||
pen_ = pen;
|
||||
update();
|
||||
}
|
||||
|
||||
void CVVKeyPoint::setBrush(const QBrush &brush)
|
||||
{
|
||||
brush_ = brush;
|
||||
update();
|
||||
}
|
||||
|
||||
void CVVKeyPoint::setShow(bool b)
|
||||
{
|
||||
show_ = b;
|
||||
if(image_){
|
||||
setVisible(show_&imagePointisVisible());
|
||||
}
|
||||
}
|
||||
|
||||
QRectF CVVKeyPoint::boundingRect() const
|
||||
{
|
||||
// TODO throw image==nullptr
|
||||
return QRectF{
|
||||
QPointF{ imPointInScene().x() - 3, imPointInScene().y() - 3 },
|
||||
QPointF{ imPointInScene().x() + 3, imPointInScene().y() + 3 }
|
||||
};
|
||||
}
|
||||
|
||||
void CVVKeyPoint::updateImageSet(const QRectF &, const qreal &zoom)
|
||||
{
|
||||
imagePointInScene_=image_->mapImagePointToParent(
|
||||
QPointF{ pt.x, pt.y });
|
||||
|
||||
bool isInVisibleArea=imagePointisVisible();
|
||||
setVisible(show_ && isInVisibleArea);
|
||||
emit updatePoint(isInVisibleArea);
|
||||
zoom_ = zoom;
|
||||
prepareGeometryChange();
|
||||
// update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
#ifndef CVVISUAL_CVVKEYPOINT
|
||||
#define CVVISUAL_CVVKEYPOINT
|
||||
|
||||
#include <QGraphicsObject>
|
||||
#include <QPainter>
|
||||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
#include <QWidget>
|
||||
#include <QGraphicsScene>
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "keypointsettings.hpp"
|
||||
#include "../zoomableimage.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
class KeyPointSettings;
|
||||
|
||||
/**
|
||||
* @brief this class represents a Keypoint which is displayed
|
||||
* a Matchscene.
|
||||
**/
|
||||
class CVVKeyPoint : public QGraphicsObject,public cv::KeyPoint
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief the construor
|
||||
* @param key the keypoint with the image point
|
||||
* @param image the zoomable image
|
||||
*/
|
||||
CVVKeyPoint(const cv::KeyPoint &key,
|
||||
qtutil::ZoomableImage *image = nullptr,
|
||||
QPen pen = QPen{ Qt::red },
|
||||
QBrush brush = QBrush{ Qt::red },
|
||||
QGraphicsItem *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief this method maps the imagepoint to the scene
|
||||
* @return maps the imagepoint to the scene
|
||||
*/
|
||||
QPointF imPointInScene() const
|
||||
{return imagePointInScene_;}
|
||||
|
||||
/**
|
||||
* @brief boundingRect
|
||||
* @return the boundingRect
|
||||
*/
|
||||
QRectF boundingRect() const CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief returns the keypoint
|
||||
* @return the keypoint
|
||||
*/
|
||||
cv::KeyPoint keyPoint() const
|
||||
{return *this;}
|
||||
|
||||
/**
|
||||
* @brief the paint function.
|
||||
*/
|
||||
void paint(QPainter *painter, const QStyleOptionGraphicsItem *,
|
||||
QWidget *) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief returns true if this keypoint is in the visble area of its
|
||||
* image
|
||||
* @return true if this keypoint is in the visble area of its image
|
||||
*/
|
||||
bool imagePointisVisible()
|
||||
{return image_->visibleArea().contains(pt.x, pt.y); }
|
||||
|
||||
/**
|
||||
* @brief if show is true this keypoint will be visible if it is the
|
||||
* visibleArea
|
||||
* @return the show Value
|
||||
*/
|
||||
bool isShown() const
|
||||
{return show_;}
|
||||
|
||||
bool operator==(const cv::KeyPoint &o);
|
||||
|
||||
QPen getPen() const
|
||||
{return pen_;}
|
||||
|
||||
QBrush getBrush() const
|
||||
{return brush_; }
|
||||
|
||||
signals:
|
||||
/**
|
||||
* @brief this signal will be emitted when the imagepoint in the scene
|
||||
* has changed
|
||||
* @param visible it is true if this keypoint is in the visibleArea
|
||||
*/
|
||||
void updatePoint(bool visible);
|
||||
|
||||
public
|
||||
slots:
|
||||
/**
|
||||
* @brief updates the settings of this KeyPoint
|
||||
* @param settings the object which has new settings for this keypoint
|
||||
*/
|
||||
void updateSettings(KeyPointSettings &settings);
|
||||
|
||||
void setPen(const QPen &pen);
|
||||
|
||||
/**
|
||||
* @brief updates the brush of this KeyPoint
|
||||
* @param brush a new brush
|
||||
*/
|
||||
void setBrush(const QBrush &brush);
|
||||
|
||||
/**
|
||||
* @brief if show is true this keypoint will be visible if it is the
|
||||
* visibleArea
|
||||
* @param b the new show Value
|
||||
*/
|
||||
void setShow(bool b);
|
||||
|
||||
/**
|
||||
* @brief updates the coordinates and visibleState of this KeyPoint
|
||||
* @param visibleArea the visibleArea of the ZoomableImage
|
||||
* @param zoom the zoomfactor
|
||||
*/
|
||||
void updateImageSet(const QRectF &, const qreal &zoom);
|
||||
|
||||
/**
|
||||
* @brief this method sets and connects this keypoint which the given
|
||||
* ZoomableImage.
|
||||
* the ZoomableImage should be in a QGraphicScene and should have same
|
||||
* parent
|
||||
* @param image the image
|
||||
*/
|
||||
void setZoomableImage(ZoomableImage *image);
|
||||
|
||||
private:
|
||||
qtutil::ZoomableImage *image_=nullptr;
|
||||
|
||||
QPen pen_;
|
||||
QBrush brush_;
|
||||
qreal zoom_;
|
||||
bool show_;
|
||||
|
||||
QPointF imagePointInScene_;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,91 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include "cvvmatch.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
CVVMatch::CVVMatch(CVVKeyPoint *left_key, CVVKeyPoint *right_key,
|
||||
const cv::DMatch &match, const QPen &pen,
|
||||
QGraphicsItem *parent)
|
||||
: QGraphicsObject{ parent },cv::DMatch{ match }, left_key_{ left_key }, right_key_{ right_key },
|
||||
pen_{ pen }, show_{ true },
|
||||
left_key_visible_{ left_key->imagePointisVisible() },
|
||||
right_key_visible_{ right_key_->imagePointisVisible() }
|
||||
{
|
||||
//setFlag(QGraphicsItem::ItemIsSelectable);
|
||||
|
||||
setVisible(show_ && left_key_visible_ && right_key_visible_);
|
||||
//setSelected(true);
|
||||
|
||||
setToolTip(QString
|
||||
{ "Match distance: %1 \n queryIdx %2 \n trainIdx %3 \n imIdx %4 " }
|
||||
.arg(distance)
|
||||
.arg(queryIdx)
|
||||
.arg(trainIdx)
|
||||
.arg(imgIdx));
|
||||
|
||||
connect(left_key_, SIGNAL(updatePoint(bool)), this,
|
||||
SLOT(updateLeftKey(bool)));
|
||||
connect(right_key_, SIGNAL(updatePoint(bool)), this,
|
||||
SLOT(updateRightKey(bool)));
|
||||
}
|
||||
|
||||
QRectF CVVMatch::boundingRect() const
|
||||
{
|
||||
// TODO minmax
|
||||
return QRectF{ QPointF{ std::min(leftImPointInScene().rx(),
|
||||
rightImPointInScene().rx()),
|
||||
std::min(leftImPointInScene().ry(),
|
||||
rightImPointInScene().ry()) },
|
||||
QPointF{ std::max(leftImPointInScene().rx(),
|
||||
rightImPointInScene().rx()),
|
||||
std::max(leftImPointInScene().ry(),
|
||||
rightImPointInScene().ry()) } };
|
||||
}
|
||||
|
||||
void CVVMatch::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
|
||||
QWidget *)
|
||||
{
|
||||
painter->setPen(pen_);
|
||||
painter->drawLine(leftImPointInScene(), rightImPointInScene());
|
||||
}
|
||||
|
||||
bool CVVMatch::operator==(const cv::DMatch &o)
|
||||
{
|
||||
return o.queryIdx == queryIdx && o.trainIdx == trainIdx &&
|
||||
o.imgIdx == imgIdx;
|
||||
}
|
||||
|
||||
void CVVMatch::setPen(const QPen &pen)
|
||||
{
|
||||
pen_ = pen;
|
||||
update();
|
||||
}
|
||||
|
||||
void CVVMatch::setShow(const bool &b)
|
||||
{
|
||||
show_ = b;
|
||||
setVisible(show_ && left_key_visible_ && right_key_visible_);
|
||||
}
|
||||
|
||||
void CVVMatch::updateLeftKey(bool visible)
|
||||
{
|
||||
left_key_visible_ = visible;
|
||||
setVisible(show_ && left_key_visible_ && right_key_visible_);
|
||||
prepareGeometryChange();
|
||||
// update();
|
||||
}
|
||||
|
||||
void CVVMatch::updateRightKey(bool visible)
|
||||
{
|
||||
right_key_visible_ = visible;
|
||||
setVisible(show_ && left_key_visible_ && right_key_visible_);
|
||||
prepareGeometryChange();
|
||||
// update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,180 @@
|
||||
#ifndef CVVISUAL_CVVMATCH
|
||||
#define CVVISUAL_CVVMATCH
|
||||
|
||||
#include <QGraphicsObject>
|
||||
#include <QPainter>
|
||||
#include <QPointF>
|
||||
#include <QRectF>
|
||||
#include <QStyleOptionGraphicsItem>
|
||||
#include <QWidget>
|
||||
|
||||
|
||||
#include "opencv2/core.hpp"
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "matchsettings.hpp"
|
||||
#include "cvvkeypoint.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
class MatchSettings;
|
||||
|
||||
/**
|
||||
* @brief this class represents a match which is displayed
|
||||
* a Matchscene.
|
||||
*/
|
||||
class CVVMatch : public QGraphicsObject,public cv::DMatch
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief the constructor
|
||||
* @param left_key the left KeyPointPen
|
||||
* @param right_key the right KeyPointPen
|
||||
* @param match the match
|
||||
* @param pen a QPen
|
||||
* @param parent the parent Widget
|
||||
*/
|
||||
CVVMatch(CVVKeyPoint *left_key,
|
||||
CVVKeyPoint *right_key,
|
||||
const cv::DMatch &match,
|
||||
const QPen &pen = QPen{ Qt::red },
|
||||
QGraphicsItem *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief returns the boundingrect of this Mathc
|
||||
* @return the boundingrect of this Mathc
|
||||
*/
|
||||
virtual QRectF boundingRect() const CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief the paint function
|
||||
*/
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *,
|
||||
QWidget *) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief returns the left keypoint.
|
||||
* @return the left keypoint.
|
||||
*/
|
||||
cv::KeyPoint leftKeyPoint() const
|
||||
{
|
||||
return left_key_->keyPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the right keypoint.
|
||||
* @return the right keypoint.
|
||||
*/
|
||||
cv::KeyPoint rightKeyPoint() const
|
||||
{
|
||||
return right_key_->keyPoint();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief maps the leftImagePoint to scene
|
||||
* @return the scene point of the leftkeypoint
|
||||
*/
|
||||
QPointF leftImPointInScene() const
|
||||
{
|
||||
return left_key_->imPointInScene();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief maps the leftImagePoint to scene
|
||||
* @return the scene point of the rightkeypoint
|
||||
*/
|
||||
QPointF rightImPointInScene() const
|
||||
{
|
||||
return right_key_->imPointInScene();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the match value
|
||||
* @return the match value
|
||||
*/
|
||||
const cv::DMatch match() const
|
||||
{
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief returns the show value
|
||||
* @return the show value
|
||||
*/
|
||||
bool isShown() const
|
||||
{
|
||||
return show_;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief operator ==
|
||||
* @param o a cv::DMatch
|
||||
* @return true if this has the same match
|
||||
*/
|
||||
bool operator==(const cv::DMatch &o);
|
||||
|
||||
/**
|
||||
* @brief get current pen
|
||||
* @return current Pen
|
||||
*/
|
||||
QPen getPen() const
|
||||
{
|
||||
return pen_;
|
||||
}
|
||||
|
||||
public
|
||||
slots:
|
||||
|
||||
/**
|
||||
* @brief the match will call setSettings from settings
|
||||
* @param settings the settings for this match
|
||||
*/
|
||||
void updateSettings(MatchSettings &settings)
|
||||
{
|
||||
settings.setSettings(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief this method updates the Pen
|
||||
* @param pen the new Pen
|
||||
*/
|
||||
void setPen(const QPen &pen);
|
||||
|
||||
/**
|
||||
* @brief if show=true the match will be visible if both keypoints are
|
||||
* in the
|
||||
* visibleArea of its images
|
||||
* @param b new show value
|
||||
*/
|
||||
void setShow(const bool &b);
|
||||
|
||||
/**
|
||||
* @brief this slot will be called if the right keypoint has changed
|
||||
* @param visible if the rightKey in the visibleArea of its image
|
||||
*/
|
||||
virtual void updateRightKey(bool visible);
|
||||
|
||||
/**
|
||||
* @brief this slot will be called if the left keypoint has changed
|
||||
* @param visible if the leftKey in the visibleArea of its image
|
||||
*/
|
||||
virtual void updateLeftKey(bool visible);
|
||||
|
||||
protected:
|
||||
CVVKeyPoint *left_key_;
|
||||
CVVKeyPoint *right_key_;
|
||||
//cv::DMatch match_;
|
||||
|
||||
QPen pen_;
|
||||
bool show_;
|
||||
bool left_key_visible_;
|
||||
bool right_key_visible_;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,62 @@
|
||||
#include <QBrush>
|
||||
|
||||
#include "cvvpointmatch.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
CVVPointMatch::CVVPointMatch(CVVKeyPoint *left_key, CVVKeyPoint *right_key,
|
||||
const cv::DMatch &match, bool isLeftKey,
|
||||
qreal radius, const QPen &pen, const QBrush &brush,
|
||||
QGraphicsItem *parent)
|
||||
: CVVMatch{ left_key, right_key, match, pen, parent },
|
||||
isLeftKey_{ isLeftKey },
|
||||
radius_{ std::min(radius * match.distance, 10.0) }, brush_{ brush }
|
||||
{
|
||||
if (isLeftKey_)
|
||||
{
|
||||
right_key_visible_ = true;
|
||||
setVisible(left_key_visible_);
|
||||
}
|
||||
else
|
||||
{
|
||||
left_key_visible_ = true;
|
||||
setVisible(right_key_visible_);
|
||||
}
|
||||
}
|
||||
|
||||
QRectF CVVPointMatch::boundingRect() const
|
||||
{
|
||||
QPointF point =
|
||||
(isLeftKey_ ? leftImPointInScene() : rightImPointInScene());
|
||||
return QRectF{ QPointF{ point.x() - radius_, point.y() - radius_ },
|
||||
QPointF{ point.x() + radius_, point.y() + radius_ } };
|
||||
}
|
||||
|
||||
void CVVPointMatch::paint(QPainter *painter, const QStyleOptionGraphicsItem *,
|
||||
QWidget *)
|
||||
{
|
||||
painter->setPen(pen_);
|
||||
painter->setBrush(brush_);
|
||||
painter->drawEllipse(boundingRect());
|
||||
}
|
||||
|
||||
void CVVPointMatch::updateRightKey(bool visible)
|
||||
{
|
||||
if (!isLeftKey_)
|
||||
{
|
||||
CVVMatch::updateRightKey(visible);
|
||||
}
|
||||
}
|
||||
|
||||
void CVVPointMatch::updateLeftKey(bool visible)
|
||||
{
|
||||
if (isLeftKey_)
|
||||
{
|
||||
CVVMatch::updateLeftKey(visible);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#ifndef CVVISUAL_CVV_POINT_MATCH
|
||||
#define CVVISUAL_CVV_POINT_MATCH
|
||||
|
||||
#include <QBrush>
|
||||
|
||||
#include "cvvmatch.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief This CVVMatch will be shown as circles with a given Color and radius
|
||||
* this CVVMatches will be used in DepthView
|
||||
*/
|
||||
class CVVPointMatch : public CVVMatch
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief the constructor
|
||||
* @param left_key the left KeyPointPen
|
||||
* @param right_key the right KeyPointPen
|
||||
* @param matchValue the match distance
|
||||
* @param isLeftKey if true the match is at Pos of the left key,
|
||||
* otherwise it is at the
|
||||
* pos of the right key
|
||||
* @param radius the radius of the MatchPoint
|
||||
* @param pen the pen
|
||||
* @param brush the brush
|
||||
* @param parent the parent Widget
|
||||
*/
|
||||
CVVPointMatch(CVVKeyPoint *left_key, CVVKeyPoint *right_key,
|
||||
const cv::DMatch &match, bool isLeftKey = true,
|
||||
qreal radius = 1, const QPen &pen = QPen{ Qt::red },
|
||||
const QBrush &brush = QBrush{ Qt::red },
|
||||
QGraphicsItem *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief returns the boundingrect of this Mathc
|
||||
* @return the boundingrect of this Mathc
|
||||
*/
|
||||
virtual QRectF boundingRect() const override;
|
||||
|
||||
/**
|
||||
* @brief the paint function
|
||||
*/
|
||||
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *,
|
||||
QWidget *) override;
|
||||
|
||||
public
|
||||
slots:
|
||||
|
||||
/**
|
||||
* @brief this slot will be called if the right keypoint has changed
|
||||
* @param visible if the rightKey in the visibleArea of its image
|
||||
*/
|
||||
virtual void updateRightKey(bool visible) override;
|
||||
|
||||
/**
|
||||
* @brief this slot will be called if the left keypoint has changed
|
||||
* @param visible if the leftKey in the visibleArea of its image
|
||||
*/
|
||||
virtual void updateLeftKey(bool visible) override;
|
||||
|
||||
protected:
|
||||
bool isLeftKey_;
|
||||
qreal radius_;
|
||||
QBrush brush_;
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "falsecolorkeypointpen.hpp"
|
||||
#include "colorutil.hpp"
|
||||
#include "cvvmatch.hpp"
|
||||
#include "../../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
FalseColorKeyPointPen::FalseColorKeyPointPen(std::vector<cv::KeyPoint> univers, QWidget *parent)
|
||||
: KeyPointSettings{ parent },
|
||||
univers_{univers},
|
||||
maxDistance_{0.0},
|
||||
minDistance_{0.0}
|
||||
{
|
||||
auto layout = util::make_unique<QVBoxLayout>();
|
||||
auto valueChooser=util::make_unique<KeyPointValueChooser>();
|
||||
auto button = util::make_unique<QPushButton>("use false color");
|
||||
valueChooser_=valueChooser.get();
|
||||
|
||||
connect(valueChooser.get(),SIGNAL(valueChanged()),this,SLOT(updateMinMax()));
|
||||
connect(button.get(), SIGNAL(clicked()), this, SLOT(updateAll()));
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
layout->addWidget(valueChooser.release());
|
||||
layout->addWidget(button.release());
|
||||
|
||||
setLayout(layout.release());
|
||||
|
||||
updateMinMax();
|
||||
}
|
||||
|
||||
void FalseColorKeyPointPen::setSettings(CVVKeyPoint &key)
|
||||
{
|
||||
QPen pen= key.getPen();
|
||||
QBrush brush=key.getBrush();
|
||||
pen.setColor(getFalseColor( valueChooser_->getChoosenValue(key.keyPoint()), maxDistance_, minDistance_) );
|
||||
brush.setColor(getFalseColor( valueChooser_->getChoosenValue(key.keyPoint()), maxDistance_, minDistance_) );
|
||||
key.setPen(pen);
|
||||
key.setBrush(brush);
|
||||
}
|
||||
|
||||
void FalseColorKeyPointPen::updateMinMax()
|
||||
{
|
||||
maxDistance_=0.0;
|
||||
minDistance_=0.0;
|
||||
for(auto& key:univers_){
|
||||
maxDistance_=std::max(maxDistance_,valueChooser_->getChoosenValue(key));
|
||||
//minDistance_=std::max(minDistance_,valueChooser_->getChoosenValue(key));
|
||||
}
|
||||
updateAll();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#ifndef CVVISUAL_FALSE_COLOR_KEY_POINT_PEN
|
||||
#define CVVISUAL_FALSE_COLOR_KEY_POINT_PEN
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "keypointvaluechooser.hpp"
|
||||
#include "keypointsettings.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief this pen gives the falsecolor of the distance value to the key point
|
||||
*/
|
||||
class FalseColorKeyPointPen : public KeyPointSettings
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief the constructor
|
||||
* @param univers all keypoints (for max value)
|
||||
* @param parent the parent Widget
|
||||
*/
|
||||
FalseColorKeyPointPen(std::vector<cv::KeyPoint> univers, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief set the falseColor to the given keypoint
|
||||
*/
|
||||
virtual void setSettings(CVVKeyPoint &key) override;
|
||||
|
||||
private slots:
|
||||
|
||||
void updateMinMax();
|
||||
|
||||
private:
|
||||
KeyPointValueChooser* valueChooser_;
|
||||
std::vector<cv::KeyPoint> univers_;
|
||||
double maxDistance_;
|
||||
double minDistance_=0.0;//always 0
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
#include <QPushButton>
|
||||
#include <QDoubleSpinBox>
|
||||
#include <QLabel>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "falsecolormatchpen.hpp"
|
||||
#include "colorutil.hpp"
|
||||
#include "cvvmatch.hpp"
|
||||
#include "../../util/util.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
FalseColorMatchPen::FalseColorMatchPen(std::vector<cv::DMatch> univers, QWidget *parent)
|
||||
: MatchSettings{parent},
|
||||
maxDistance_{0.0},
|
||||
minDistance_{0.0}
|
||||
{
|
||||
auto layout = util::make_unique<QVBoxLayout>();
|
||||
auto button = util::make_unique<QPushButton>("use false color");
|
||||
|
||||
for(auto& match:univers){
|
||||
maxDistance_=std::max(maxDistance_,static_cast<double>(match.distance));
|
||||
//min_=std::max(min_,static_cast<double>(match.distance));
|
||||
}
|
||||
|
||||
connect(button.get(), SIGNAL(clicked()), this, SLOT(updateAll()));
|
||||
|
||||
layout->addWidget(button.release());
|
||||
|
||||
setLayout(layout.release());
|
||||
}
|
||||
|
||||
void FalseColorMatchPen::setSettings(CVVMatch &match)
|
||||
{
|
||||
QPen pen= match.getPen();
|
||||
pen.setColor(getFalseColor( static_cast<double>(match.match().distance), maxDistance_, minDistance_) );
|
||||
match.setPen(pen);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#ifndef CVVISUAL_FALSE_COLOR_MATCH_PEN
|
||||
#define CVVISUAL_FALSE_COLOR_MATCH_PEN
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "matchsettings.hpp"
|
||||
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief this pen gives the falsecolor of the distance value to the match
|
||||
*/
|
||||
class FalseColorMatchPen : public MatchSettings
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief the constructor
|
||||
* @param univers all matches (for max value)
|
||||
* @param parent the parent Widget
|
||||
*/
|
||||
FalseColorMatchPen(std::vector<cv::DMatch> univers, QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief set the falseColor of the distance to the given match
|
||||
*/
|
||||
virtual void setSettings(CVVMatch &match) override;
|
||||
|
||||
private:
|
||||
double maxDistance_;
|
||||
double minDistance_=0.0;//always 0
|
||||
};
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,64 @@
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
|
||||
#include "keypointintervallselection.hpp"
|
||||
#include "../../util/util.hpp"
|
||||
|
||||
namespace cvv{ namespace qtutil{
|
||||
|
||||
KeyPointIntervallSelector::KeyPointIntervallSelector(std::vector<cv::KeyPoint> keypoints, QWidget *parent):
|
||||
KeyPointSelection{parent},
|
||||
layout_{nullptr},
|
||||
selector_{nullptr},
|
||||
valueChooser_{nullptr},
|
||||
keypoints_{keypoints}
|
||||
{
|
||||
auto layout=util::make_unique<QVBoxLayout>();
|
||||
auto valueChooser=util::make_unique<KeyPointValueChooser>();
|
||||
|
||||
valueChooser_=valueChooser.get();
|
||||
|
||||
connect(valueChooser_,SIGNAL(valueChanged()),this,SLOT(changeSelecteValue()));
|
||||
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
layout_=layout.get();
|
||||
|
||||
layout->addWidget(valueChooser.release());
|
||||
setLayout(layout.release());
|
||||
|
||||
changeSelecteValue();
|
||||
}
|
||||
|
||||
std::vector<cv::KeyPoint> KeyPointIntervallSelector::select(const std::vector<cv::KeyPoint> &selection)
|
||||
{
|
||||
return selector_->select(selection, [&](const cv::KeyPoint& key)
|
||||
{return this->valueChooser_->getChoosenValue(key);}
|
||||
);
|
||||
}
|
||||
|
||||
void KeyPointIntervallSelector::changeSelecteValue()
|
||||
{
|
||||
if(selector_){
|
||||
layout_->removeWidget(selector_);
|
||||
selector_->deleteLater();
|
||||
}
|
||||
double min=-1;
|
||||
double max=0;
|
||||
for(auto& key:keypoints_)
|
||||
{
|
||||
|
||||
min=std::min(valueChooser_->getChoosenValue(key),min);
|
||||
max=std::max(valueChooser_->getChoosenValue(key),max);
|
||||
}
|
||||
auto selector=util::make_unique<IntervallSelector>(min,max);
|
||||
selector_=selector.get();
|
||||
connect(&(selector->signalSettingsChanged()),SIGNAL(signal()),this,SIGNAL(settingsChanged()));
|
||||
layout_->addWidget(selector.release());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,47 @@
|
||||
#ifndef CVVISUAL_KEY_POINT_INTERVALL_SELECTOR
|
||||
#define CVVISUAL_KEY_POINT_INTERVALL_SELECTOR
|
||||
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "keypointselection.hpp"
|
||||
#include "keypointvaluechooser.hpp"
|
||||
#include "../intervallselector.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
/**
|
||||
* @brief this widget select an intervall of matches from the given selection.
|
||||
* it use IntervallSelector
|
||||
*/
|
||||
class KeyPointIntervallSelector:public KeyPointSelection{
|
||||
|
||||
Q_OBJECT
|
||||
public:
|
||||
/**
|
||||
* @brief the constructor
|
||||
* @param matches all matches which can be selected
|
||||
* @param parent the parent widget
|
||||
*/
|
||||
KeyPointIntervallSelector(std::vector<cv::KeyPoint> key,QWidget*parent=nullptr);
|
||||
|
||||
/**
|
||||
* @brief select matches from the given selecton
|
||||
* @param selection the current selection
|
||||
* @return the selected matches
|
||||
*/
|
||||
virtual std::vector<cv::KeyPoint> select(const std::vector<cv::KeyPoint>& selection)override;
|
||||
|
||||
private slots:
|
||||
|
||||
void changeSelecteValue();
|
||||
private:
|
||||
QLayout* layout_;
|
||||
IntervallSelector* selector_;
|
||||
KeyPointValueChooser * valueChooser_;
|
||||
std::vector<cv::KeyPoint> keypoints_;
|
||||
};
|
||||
|
||||
}}
|
||||
#endif
|
||||
@@ -0,0 +1,221 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QGridLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLabel>
|
||||
#include <QFrame>
|
||||
|
||||
#include "keypointmanagement.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
KeyPointManagement::KeyPointManagement(std::vector<cv::KeyPoint> univers,QWidget *parent) :
|
||||
KeyPointSettings{parent},
|
||||
univers_{univers},
|
||||
selection_{univers_}
|
||||
{
|
||||
auto basicLayout=util::make_unique<QVBoxLayout>();
|
||||
auto buttonLayout=util::make_unique<QGridLayout>();
|
||||
auto settingsLayout=util::make_unique<QVBoxLayout>();
|
||||
auto selectorLayout=util::make_unique<QVBoxLayout>();
|
||||
|
||||
auto buttonFrame=util::make_unique<QFrame>();
|
||||
buttonFrame->setLineWidth(1);
|
||||
buttonFrame->setFrameStyle(QFrame::Box);
|
||||
|
||||
auto labelSettings=util::make_unique<QLabel>("Settings");
|
||||
auto labelSelection=util::make_unique<QLabel>("Selection");
|
||||
|
||||
auto buttonAddSetting=util::make_unique<QPushButton>("Add setting");
|
||||
auto buttonAddSelection=util::make_unique<QPushButton>("Add selector");
|
||||
//auto buttonApply=util::make_unique<QPushButton>("Apply settings");
|
||||
auto showOnlySelection=util::make_unique<QCheckBox>("Show selection only");
|
||||
auto buttonApplySelection=util::make_unique<QPushButton>("Apply Selection");
|
||||
auto buttonSelectAll=util::make_unique<QPushButton>("Select all");
|
||||
auto buttonSelectNone=util::make_unique<QPushButton>("Select none");
|
||||
|
||||
connect(buttonAddSetting.get(),SIGNAL(clicked()),this,SLOT(addSetting()));
|
||||
connect(buttonAddSelection.get(),SIGNAL(clicked()),this,SLOT(addSelection()));
|
||||
//connect(buttonApply.get(),SIGNAL(clicked()),this,SLOT(updateAll()));
|
||||
connect(showOnlySelection.get(),SIGNAL(clicked()),this,SLOT(updateAll()));
|
||||
connect(buttonApplySelection.get(),SIGNAL(clicked()),this,SLOT(applySelection()));
|
||||
connect(buttonSelectAll.get(),SIGNAL(clicked()),this,SLOT(selectAll()));
|
||||
connect(buttonSelectNone.get(),SIGNAL(clicked()),this,SLOT(selectNone()));
|
||||
|
||||
settingsLayout_=settingsLayout.get();
|
||||
selectorLayout_=selectorLayout.get();
|
||||
showOnlySelection_=showOnlySelection.get();
|
||||
|
||||
showOnlySelection->setChecked(true);
|
||||
|
||||
buttonLayout->addWidget(buttonAddSetting.release(),0,0);
|
||||
buttonLayout->addWidget(buttonAddSelection.release(),0,1);
|
||||
buttonLayout->addWidget(buttonApplySelection.release(),1,0);
|
||||
//buttonLayout->addWidget(buttonApply.release(),1,1);
|
||||
buttonLayout->addWidget(showOnlySelection.release(),1,1);
|
||||
buttonLayout->addWidget(buttonSelectAll.release(),2,0);
|
||||
buttonLayout->addWidget(buttonSelectNone.release(),2,1);
|
||||
|
||||
buttonFrame->setLayout(buttonLayout.release());
|
||||
|
||||
basicLayout->addWidget(buttonFrame.release());
|
||||
basicLayout->addWidget(labelSettings.release());
|
||||
basicLayout->addLayout(settingsLayout.release());
|
||||
basicLayout->addWidget(labelSelection.release());
|
||||
basicLayout->addLayout(selectorLayout.release());
|
||||
|
||||
basicLayout->setContentsMargins(0, 0, 0, 0);
|
||||
|
||||
setLayout(basicLayout.release());
|
||||
|
||||
addSelection();
|
||||
addSetting();
|
||||
}
|
||||
|
||||
void KeyPointManagement::setSettings(CVVKeyPoint &key)
|
||||
{
|
||||
if(showOnlySelection_->isChecked())
|
||||
{
|
||||
if (std::find_if(selection_.begin(), selection_.end(),
|
||||
[&](const cv::KeyPoint &o)
|
||||
{ return key == o; }) != selection_.end())
|
||||
{
|
||||
key.setShow(true);
|
||||
}else{
|
||||
key.setShow(false);
|
||||
}
|
||||
}/*else{
|
||||
if (std::find_if(selection_.begin(), selection_.end(),
|
||||
[&](const cv::KeyPoint &o)
|
||||
{ return key == o; }) != selection_.end())
|
||||
{
|
||||
connect(this,SIGNAL(applySettingsToSelection(KeyPointSettings&)),
|
||||
&key,SLOT(updateSettings(KeyPointSettings&)));
|
||||
for(auto setting: settingsList_)
|
||||
{
|
||||
setting->setSettings(key);
|
||||
}
|
||||
}else{
|
||||
|
||||
disconnect(this,SIGNAL(applySettingsToSelection(KeyPointSettings&)),
|
||||
&key,SLOT(updateSettings(KeyPointSettings&)));
|
||||
for(auto setting: settingsList_)
|
||||
{
|
||||
setting->setUnSelectedSettings(key);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
|
||||
void KeyPointManagement::addToSelection(const cv::KeyPoint &key)
|
||||
{
|
||||
selection_.push_back(key);
|
||||
emit updateSelection(selection_);
|
||||
updateAll();
|
||||
}
|
||||
|
||||
void KeyPointManagement::singleSelection(const cv::KeyPoint &key)
|
||||
{
|
||||
selection_.clear();
|
||||
selection_.push_back(key);
|
||||
emit updateSelection(selection_);
|
||||
updateAll();
|
||||
}
|
||||
|
||||
void KeyPointManagement::setSelection(
|
||||
const std::vector<cv::KeyPoint> &selection)
|
||||
{
|
||||
selection_.clear();
|
||||
for (auto &key : selection)
|
||||
{
|
||||
selection_.push_back(key);
|
||||
}
|
||||
emit updateSelection(selection_);
|
||||
updateAll();
|
||||
}
|
||||
|
||||
void KeyPointManagement::addSetting()
|
||||
{
|
||||
addSetting(util::make_unique<KeyPointSettingsSelector>(univers_));
|
||||
}
|
||||
|
||||
|
||||
void KeyPointManagement::addSetting(std::unique_ptr<KeyPointSettingsSelector> setting)
|
||||
{
|
||||
connect(setting.get(),SIGNAL(settingsChanged(KeyPointSettings &)),
|
||||
this,SIGNAL(settingsChanged(KeyPointSettings&)));
|
||||
|
||||
connect(setting.get(),SIGNAL(remove(KeyPointSettingsSelector *)),
|
||||
this,SLOT(removeSetting(KeyPointSettingsSelector*)));
|
||||
|
||||
settingsList_.push_back(setting.get());
|
||||
setting->setLineWidth(1);
|
||||
setting->setFrameStyle(QFrame::Box);
|
||||
settingsLayout_->addWidget(setting.release());
|
||||
}
|
||||
|
||||
void KeyPointManagement::removeSetting(KeyPointSettingsSelector *setting)
|
||||
{
|
||||
auto it = std::find(settingsList_.begin(), settingsList_.end(), setting);
|
||||
|
||||
if(it == settingsList_.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
settingsList_.erase(it);
|
||||
settingsLayout_->removeWidget(setting);
|
||||
setting->deleteLater();
|
||||
}
|
||||
|
||||
void KeyPointManagement::addSelection()
|
||||
{
|
||||
addSelection(util::make_unique<KeyPointSelectionSelector>(univers_));
|
||||
}
|
||||
|
||||
void KeyPointManagement::addSelection(std::unique_ptr<KeyPointSelectionSelector> selection)
|
||||
{
|
||||
connect(selection.get(),SIGNAL(remove(KeyPointSelectionSelector*))
|
||||
,this,SLOT(removeSelection(KeyPointSelectionSelector*)));
|
||||
|
||||
connect(selection.get(),SIGNAL(settingsChanged()),this,SLOT(applySelection()));
|
||||
selectorList_.push_back(selection.get());
|
||||
selection->setLineWidth(1);
|
||||
selection->setFrameStyle(QFrame::Box);
|
||||
selectorLayout_->addWidget(selection.release());
|
||||
}
|
||||
|
||||
void KeyPointManagement::removeSelection(KeyPointSelectionSelector *selector)
|
||||
{
|
||||
auto it = std::find(selectorList_.begin(), selectorList_.end(), selector);
|
||||
|
||||
if(it == selectorList_.end())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
selectorList_.erase(it);
|
||||
selectorLayout_->removeWidget(selector);
|
||||
|
||||
selector->deleteLater();
|
||||
}
|
||||
|
||||
void KeyPointManagement::applySelection()
|
||||
{
|
||||
std::vector<cv::KeyPoint> currentSelection=univers_;
|
||||
for(auto& selector:selectorList_){
|
||||
currentSelection=selector->select(currentSelection);
|
||||
}
|
||||
selection_=currentSelection;
|
||||
emit updateSelection(selection_);
|
||||
updateAll();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
#ifndef CVVISUAL_KEYPOINT_MANAGEMENT
|
||||
#define CVVISUAL_KEYPOINT_MANAGEMENT
|
||||
|
||||
#include <QCheckBox>
|
||||
|
||||
#include "../../util/util.hpp"
|
||||
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "keypointselectionselector.hpp"
|
||||
#include "keypointsettingsselector.hpp"
|
||||
#include "keypointsettings.hpp"
|
||||
#include "cvvkeypoint.hpp"
|
||||
|
||||
namespace cvv
|
||||
{
|
||||
namespace qtutil
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief the keypointmanagement class coordinates the selections and use settings for the selection.
|
||||
*/
|
||||
class KeyPointManagement : public KeyPointSettings
|
||||
{
|
||||
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief the constructor
|
||||
* @param univers all keypoints which can be selected
|
||||
* @param parent the parent widget
|
||||
*/
|
||||
KeyPointManagement(std::vector<cv::KeyPoint> univers,QWidget *parent = nullptr);
|
||||
|
||||
/**
|
||||
* @brief set the settings if this KeyPoint is selected
|
||||
*/
|
||||
virtual void setSettings(CVVKeyPoint &match) CV_OVERRIDE;
|
||||
|
||||
/**
|
||||
* @brief add the given KeyPointSettingsSelector to the list
|
||||
*/
|
||||
void addSetting(std::unique_ptr<KeyPointSettingsSelector>);
|
||||
|
||||
/**
|
||||
* @brief add the given KeyPointSelectionSelector to the list
|
||||
*/
|
||||
void addSelection(std::unique_ptr<KeyPointSelectionSelector>);
|
||||
|
||||
/**
|
||||
* @brief returns the current selection.
|
||||
*/
|
||||
std::vector<cv::KeyPoint> getCurrentSelection()
|
||||
{return selection_;}
|
||||
|
||||
public slots:
|
||||
|
||||
//selection
|
||||
/**
|
||||
* @brief add the given keypoint to the current selection.
|
||||
*/
|
||||
void addToSelection(const cv::KeyPoint &key);
|
||||
|
||||
/**
|
||||
* @brief set the selection to the given single match
|
||||
*/
|
||||
void singleSelection(const cv::KeyPoint &key);
|
||||
|
||||
/**
|
||||
* @brief set the current selection to the given selection
|
||||
*/
|
||||
void setSelection(const std::vector<cv::KeyPoint> &selection);
|
||||
|
||||
//KeyPointSettingSelector
|
||||
/**
|
||||
* @brief add a new Setting
|
||||
*/
|
||||
void addSetting();
|
||||
|
||||
void removeSetting(KeyPointSettingsSelector *setting);
|
||||
|
||||
//Match Selection
|
||||
/**
|
||||
* @brief add a KeyPointSelectionSelector to the list
|
||||
*/
|
||||
void addSelection();
|
||||
|
||||
/**
|
||||
* @brief remove a given KeyPointSelector from the list
|
||||
*/
|
||||
void removeSelection(KeyPointSelectionSelector *selector);
|
||||
|
||||
/**
|
||||
* @brief select with the selections
|
||||
*/
|
||||
void applySelection();
|
||||
|
||||
/**
|
||||
* @brief set Selection to univers.
|
||||
*/
|
||||
void selectAll()
|
||||
{setSelection(univers_);}
|
||||
|
||||
/**
|
||||
* @brief set selection to an empty list.
|
||||
*/
|
||||
void selectNone()
|
||||
{setSelection(std::vector<cv::KeyPoint>{});}
|
||||
|
||||
|
||||
signals:
|
||||
|
||||
/**
|
||||
* @brief this signal will be emitted when the selection was changed.
|
||||
* it can be used for syncronisation with other selector
|
||||
*/
|
||||
void updateSelection(const std::vector<cv::KeyPoint> &selection);
|
||||
|
||||
/**
|
||||
* @brief this singal has the same function like settingsChanged from KeyPointSettings,
|
||||
* but this will be only connect to the current selection
|
||||
*/
|
||||
void applySettingsToSelection(KeyPointSettings&);
|
||||
|
||||
private:
|
||||
std::vector<cv::KeyPoint> univers_;
|
||||
std::vector<cv::KeyPoint> selection_;
|
||||
std::vector<KeyPointSettingsSelector*> settingsList_;
|
||||
std::vector<KeyPointSelectionSelector*> selectorList_;
|
||||
|
||||
QLayout *settingsLayout_;
|
||||
QLayout *selectorLayout_;
|
||||
QCheckBox *showOnlySelection_;
|
||||
};
|
||||
}}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "keypointportionselector.hpp"
|
||||
|
||||
namespace cvv {namespace qtutil{
|
||||
|
||||
KeyPointPortionSelection::KeyPointPortionSelection(std::vector<cv::KeyPoint> , QWidget *parent):
|
||||
KeyPointSelection{parent}
|
||||
{
|
||||
auto layout=util::make_unique<QVBoxLayout>();
|
||||
auto valueChooser=util::make_unique<KeyPointValueChooser>();
|
||||
auto selector=util::make_unique<PortionSelector>();
|
||||
|
||||
selector_=selector.get();
|
||||
valueChooser_=valueChooser.get();
|
||||
|
||||
connect(&(selector->signalSettingsChanged()),SIGNAL(signal()),this,SIGNAL(settingsChanged()));
|
||||
|
||||
layout->addWidget(valueChooser.release());
|
||||
layout->addWidget(selector.release());
|
||||
|
||||
setLayout(layout.release());
|
||||
}
|
||||
|
||||
std::vector<cv::KeyPoint> KeyPointPortionSelection::select(const std::vector<cv::KeyPoint> &selection)
|
||||
{
|
||||
return selector_->select( selection ,
|
||||
[&](cv::KeyPoint arg1,cv::KeyPoint arg2)
|
||||
{return valueChooser_->getChoosenValue(arg1)<valueChooser_->getChoosenValue(arg2);});
|
||||
}
|
||||
|
||||
}}
|
||||
@@ -0,0 +1,28 @@
|
||||
#ifndef CVVISUAL_KEY_POINT_PORTION_SELECTOR
|
||||
#define CVVISUAL_KEY_POINT_PORTION_SELECTOR
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "opencv2/features.hpp"
|
||||
|
||||
#include "keypointselection.hpp"
|
||||
#include "keypointvaluechooser.hpp"
|
||||
#include "../portionselector.hpp"
|
||||
|
||||
namespace cvv {namespace qtutil{
|
||||
|
||||
class KeyPointPortionSelection:public KeyPointSelection{
|
||||
public:
|
||||
KeyPointPortionSelection(std::vector<cv::KeyPoint>, QWidget * parent=nullptr);
|
||||
|
||||
|
||||
virtual std::vector<cv::KeyPoint> select(const std::vector<cv::KeyPoint>& selection)override;
|
||||
|
||||
private:
|
||||
PortionSelector* selector_;
|
||||
KeyPointValueChooser * valueChooser_;
|
||||
};
|
||||
|
||||
}}
|
||||
|
||||
#endif
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user