├── .gitignore ├── CMakeLists.txt ├── README.md └── src ├── CMakeLists.txt ├── assets ├── glsl │ ├── shader.frag │ └── shader.vert └── image │ ├── brain.png │ ├── caffe.png │ ├── camera.png │ ├── camera_black.png │ ├── cctv.png │ ├── cloud_black.png │ ├── data.png │ ├── data_icon.png │ ├── detected.png │ ├── dumbbell.png │ ├── left-arrow.png │ ├── loadingShutter.png │ ├── media.png │ ├── memory.png │ ├── moreVert.png │ ├── opencv.png │ ├── opencv_icon.png │ ├── pause.png │ ├── play.png │ ├── plus.png │ ├── pngwave.png │ ├── round_check_black_48dp.png │ ├── round_check_white_48dp.png │ ├── round_close_fullscreen_white_48dp.png │ ├── round_computer_black_48dp.png │ ├── round_fullscreen_white_48dp.png │ ├── round_insert_photo_black_48dp.png │ ├── round_link_black_48dp.png │ ├── round_local_movies_black_48dp.png │ ├── round_wifi_black_48dp.png │ ├── settings.png │ ├── settings_black.png │ ├── show_chart.png │ ├── showchart_black.png │ ├── stop.png │ └── visibility.png ├── common ├── Colors.h ├── Common.h └── PropertyHelpers.h ├── component ├── CvMediaPlayer.cpp ├── CvMediaPlayer.h ├── CvMediaPlayerRenderer.cpp └── CvMediaPlayerRenderer.h ├── controller ├── CvFrameController.cpp ├── CvFrameController.h ├── MediaController.cpp └── MediaController.h ├── entity └── ContourInfo.h ├── main.cpp ├── model ├── ObjectListModel.cpp └── ObjectListModel.h ├── qtconfig.qrc ├── qtquickcontrols2.conf ├── service ├── CvBaseService.cpp ├── CvBaseService.h ├── GrayService.cpp ├── GrayService.h ├── MeasureA4Service.cpp ├── MeasureA4Service.h ├── MeasureCoinService.cpp ├── MeasureCoinService.h ├── UnDistortService.cpp ├── UnDistortService.h ├── WipeOffService.cpp └── WipeOffService.h ├── system ├── FileHandler.cpp ├── FileHandler.h ├── HttpManager.cpp ├── HttpManager.h ├── ImageProvider.cpp └── ImageProvider.h ├── util ├── CvImageUtil.cpp ├── CvImageUtil.h ├── CvLineUtil.cpp ├── CvLineUtil.h ├── CvPointUtil.cpp └── CvPointUtil.h └── views ├── main.qml ├── screen ├── MediaScreen │ ├── MediaOutputPane.qml │ ├── MediaScreen.qml │ ├── VideoInputSelectPane.qml │ └── VideoInputSettingsPane.qml └── ServiceScreen │ ├── CommonSettings.qml │ ├── OpenCV │ ├── CalibrateSettings.qml │ └── MeasureSettings.qml │ └── ServiceScreen.qml └── widget ├── ColorHelper.qml ├── ExpandingSectionButton.qml ├── FileLoader.qml ├── FolderLoader.qml ├── ImageLoaderView.qml ├── MenuTapBar.qml ├── MethodsHelpers.qml └── SeparatorLine.qml /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | cmake-build-debug/ 3 | cmake-build-release/ 4 | .idea/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | set(PROJECT_NAME qt-object-measure) 4 | project(${PROJECT_NAME}) 5 | 6 | add_subdirectory(src) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Qt Object Measure 2 | ==== 3 | - 使用 C++ 和 OpenCV 实现一个具有视频捕捉、图像显示、尺寸测量及结果显示和输出等的桌面应用,实现对圆形物体的圆心、直径和矩形物体的长和宽尺寸测量,包括相应定位及标定功能。 4 | - 实现了在 QML 中使用 OpenCV 画面显示,在 Qt 提供的定时器触发中断时,VideoCapture 读取 Mat 之后转换为 OpenGL 的纹理,交给 Qt 提供的 QQuickFramebufferObject (fbo) 组件渲染到客户端。 5 | - OpenCV 测量功能实现参考 6 | ~~~ 7 | https://github.com/murtazahassan/Real-Time-Object-Measurement 8 | ~~~ 9 | - 基于 QML 前端界面参考 10 | ~~~ 11 | https://github.com/MattLigocki/DNNAssist 12 | ~~~ 13 | ## 开发环境 14 | 1. 编程语言:C++ 11 15 | 2. 编译器:mingw730_64 16 | 3. 技术选型:Qt 5.12.12 + OpenCV 4.5.2 + OpenGL 3.3 17 | 4. 编程环境:CLion 2020.3.4 + Window 10 18 | ## 项目文件 19 | ~~~ 20 | ├── src 21 | │ └── assets // 静态资源文件 22 | │ └── common // 宏定义 23 | │ └── component // 注入组件 24 | │ └── controller // 控制层 25 | │ └── entity // 数据体 26 | │ └── model // 注入模型 27 | │ └── service // 图像处理 28 | │ └── system // 系统服务 29 | │ └── util // 工具类 30 | │ └── views // 程序入口 31 | │ └── screen // 页面 32 | │ └── widget // 通用 UI 组件 33 | │ └── main.qml // UI 入口 34 | │ └── main.c // 程序入口 35 | │ └── resource.qrc // 资源配置文件 36 | │ └── CMakeLists.txt 37 | ├── CMakeLists.txt 38 | ├── README.md 39 | ~~~ 40 | ## 示例 41 | ### 测量 42 | ![2e35c90413b5e17a9783c61c5a14509.png](https://s2.loli.net/2022/09/13/FaPNKkxJfb21WB5.png) 43 | ![9432324202b44b520aff7aaeef369c3.png](https://s2.loli.net/2022/09/13/iYnQHOFBb9v8gIw.png) 44 | ### 相机矫正 45 | ![7312380eb22791d751512fe4b535d13.png](https://s2.loli.net/2022/09/13/pZnFYKk93wmbsfj.png) 46 | ![b93f384d35b950175b02a155337e8f7.png](https://s2.loli.net/2022/09/13/nNWQMumKiXljBot.png) 47 | ## 本地运行 48 | ### 配置 Qt5 环境和 Toolchains 49 | - 安装 Qt 后将 `@/Tools/mingw730_64` 作为 CLion 的 Toolchains,添加环境变量 `@/5.12.12/mingw73_64/bin`, 修改 CMakeLists 中 `CMAKE_PREFIX_PATH ` 为 `@/5.12.12/mingw73_64/lib/cmake` 50 | ### 配置 OpenCV 4.5.2 环境 51 | - 下载已编译完成的 MinGW 版 OpenCV 库,并修改 CMakeLists 中 `OpenCV_DIR`,添加环境变量 `@/x64/mingw/bin` 52 | ~~~ 53 | git clone -b OpenCV-4.5.2-x64 git@github.com:huihut/OpenCV-MinGW-Build.git 54 | ~~~ 55 | ### 设置网络摄像头 56 | - `https://apkpure.com/cn/` 下载安装 DroidCam 或 IP 摄像头 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | project(${PROJECT_NAME}) 3 | 4 | 5 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 6 | 7 | # init qt 8 | set(CMAKE_AUTOMOC ON) # meta object compiler 9 | set(CMAKE_AUTORCC ON) # resource files 10 | # set(CMAKE_AUTOUIC ON) # UI files 11 | 12 | set(CMAKE_CXX_STANDARD 17) 13 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++17 -D FREEGLUT_STATIC") 14 | 15 | # find Qt package 16 | #set(QT_VERSION 6) 17 | #set(CMAKE_PREFIX_PATH "C:/Environment/qt/6.2.2/mingw_64/lib/cmake") 18 | set(QT_VERSION 5) 19 | set(CMAKE_PREFIX_PATH "C:/Environment/qt/5.12.12/mingw73_64/lib/cmake") 20 | find_package(Qt${QT_VERSION} COMPONENTS Gui Core Widgets Quick QuickControls2 Multimedia Xml Concurrent REQUIRED) 21 | 22 | # find OpenCV package 23 | set(OpenCV_DIR "C:/Environment/opencv-4.5.2-mingw-build") 24 | find_package(OpenCV REQUIRED) 25 | include_directories(${OpenCV_INCLUDE_DIRS}) 26 | link_directories(${OpenCV_LIBRARY_DIRS}) 27 | add_definitions(${OpenCV_DEFINITIONS}) 28 | 29 | # find source files 30 | file(GLOB_RECURSE DIR_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/*.cpp) 31 | file(GLOB_RECURSE DIR_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.h) 32 | file(GLOB_RECURSE DIR_HEADLESS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp) 33 | file(GLOB_RECURSE DIR_QRC ${CMAKE_CURRENT_SOURCE_DIR}/*.qrc) 34 | 35 | add_executable( 36 | ${PROJECT_NAME} 37 | ${DIR_SRCS} 38 | ${DIR_HEADERS} 39 | ${DIR_HEADLESS} 40 | ${DIR_QRC}) 41 | 42 | target_link_libraries( 43 | ${PROJECT_NAME} 44 | ${OpenCV_LIBS} 45 | Qt${QT_VERSION}::Gui 46 | Qt${QT_VERSION}::Core 47 | Qt${QT_VERSION}::Widgets 48 | Qt${QT_VERSION}::Quick 49 | Qt${QT_VERSION}::Multimedia 50 | Qt${QT_VERSION}::QuickControls2 51 | Qt${QT_VERSION}::Xml 52 | Qt${QT_VERSION}::Concurrent 53 | opengl32) 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/assets/glsl/shader.frag: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | in highp vec2 texCoord; 3 | uniform sampler2D texType; 4 | 5 | void main(void) 6 | { 7 | gl_FragColor = texture2D(texType, texCoord.st); 8 | } -------------------------------------------------------------------------------- /src/assets/glsl/shader.vert: -------------------------------------------------------------------------------- 1 | #version 330 core 2 | attribute highp vec3 aPos; 3 | attribute highp vec2 aTexCoord; 4 | out highp vec2 texCoord; 5 | 6 | void main(void) 7 | { 8 | texCoord = vec2(aTexCoord.x,aTexCoord.y); 9 | gl_Position = vec4(aPos,1.0); 10 | } -------------------------------------------------------------------------------- /src/assets/image/brain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/brain.png -------------------------------------------------------------------------------- /src/assets/image/caffe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/caffe.png -------------------------------------------------------------------------------- /src/assets/image/camera.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/camera.png -------------------------------------------------------------------------------- /src/assets/image/camera_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/camera_black.png -------------------------------------------------------------------------------- /src/assets/image/cctv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/cctv.png -------------------------------------------------------------------------------- /src/assets/image/cloud_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/cloud_black.png -------------------------------------------------------------------------------- /src/assets/image/data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/data.png -------------------------------------------------------------------------------- /src/assets/image/data_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/data_icon.png -------------------------------------------------------------------------------- /src/assets/image/detected.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/detected.png -------------------------------------------------------------------------------- /src/assets/image/dumbbell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/dumbbell.png -------------------------------------------------------------------------------- /src/assets/image/left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/left-arrow.png -------------------------------------------------------------------------------- /src/assets/image/loadingShutter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/loadingShutter.png -------------------------------------------------------------------------------- /src/assets/image/media.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/media.png -------------------------------------------------------------------------------- /src/assets/image/memory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/memory.png -------------------------------------------------------------------------------- /src/assets/image/moreVert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/moreVert.png -------------------------------------------------------------------------------- /src/assets/image/opencv.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/opencv.png -------------------------------------------------------------------------------- /src/assets/image/opencv_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/opencv_icon.png -------------------------------------------------------------------------------- /src/assets/image/pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/pause.png -------------------------------------------------------------------------------- /src/assets/image/play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/play.png -------------------------------------------------------------------------------- /src/assets/image/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/plus.png -------------------------------------------------------------------------------- /src/assets/image/pngwave.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/pngwave.png -------------------------------------------------------------------------------- /src/assets/image/round_check_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_check_black_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_check_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_check_white_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_close_fullscreen_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_close_fullscreen_white_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_computer_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_computer_black_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_fullscreen_white_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_fullscreen_white_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_insert_photo_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_insert_photo_black_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_link_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_link_black_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_local_movies_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_local_movies_black_48dp.png -------------------------------------------------------------------------------- /src/assets/image/round_wifi_black_48dp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/round_wifi_black_48dp.png -------------------------------------------------------------------------------- /src/assets/image/settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/settings.png -------------------------------------------------------------------------------- /src/assets/image/settings_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/settings_black.png -------------------------------------------------------------------------------- /src/assets/image/show_chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/show_chart.png -------------------------------------------------------------------------------- /src/assets/image/showchart_black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/showchart_black.png -------------------------------------------------------------------------------- /src/assets/image/stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/stop.png -------------------------------------------------------------------------------- /src/assets/image/visibility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chenjiajun1999/qt-object-measure/c8e3a6fb88be4be4a78097e5efc47706fa982035/src/assets/image/visibility.png -------------------------------------------------------------------------------- /src/common/Colors.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | std::vector > globalColorsVec { 6 | { "red", "#f44336"}, 7 | { "red-50", "#ffebee"}, 8 | { "red-100", "#ffcdd2"}, 9 | { "red-200", "#ef9a9a"}, 10 | { "red-300", "#e57373"}, 11 | { "red-400", "#ef5350"}, 12 | { "red-500", "#f44336"}, 13 | { "red-600", "#e53935"}, 14 | { "red-700", "#d32f2f"}, 15 | { "red-800", "#c62828"}, 16 | { "red-900", "#b71c1c"}, 17 | { "red-a100", "#ff8a80"}, 18 | { "red-a200", "#ff5252"}, 19 | { "red-a400", "#ff1744"}, 20 | { "red-a700", "#d50000"}, 21 | 22 | /* Pink */ 23 | { "pink", "#e91e63"}, 24 | { "pink-50", "#fce4ec"}, 25 | { "pink-100", "#f8bbd0"}, 26 | { "pink-200", "#f48fb1"}, 27 | { "pink-300", "#f06292"}, 28 | { "pink-400", "#ec407a"}, 29 | { "pink-500", "#e91e63"}, 30 | { "pink-600", "#d81b60"}, 31 | { "pink-700", "#c2185b"}, 32 | { "pink-800", "#ad1457"}, 33 | { "pink-900", "#880e4f"}, 34 | { "pink-a100", "#ff80ab"}, 35 | { "pink-a200", "#ff4081"}, 36 | { "pink-a400", "#f50057"}, 37 | { "pink-a700", "#c51162"}, 38 | 39 | /* Purple */ 40 | { "purple", "#9c27b0"}, 41 | { "purple-50", "#f3e5f5"}, 42 | { "purple-100", "#e1bee7"}, 43 | { "purple-200", "#ce93d8"}, 44 | { "purple-300", "#ba68c8"}, 45 | { "purple-400", "#ab47bc"}, 46 | { "purple-500", "#9c27b0"}, 47 | { "purple-600", "#8e24aa"}, 48 | { "purple-700", "#7b1fa2"}, 49 | { "purple-800", "#6a1b9a"}, 50 | { "purple-900", "#4a148c"}, 51 | { "purple-a100", "#ea80fc"}, 52 | { "purple-a200", "#e040fb"}, 53 | { "purple-a400", "#d500f9"}, 54 | { "purple-a700", "#aa00ff"}, 55 | 56 | /* Deep Purple */ 57 | { "deep-purple", "#673ab7"}, 58 | { "deep-purple-50", "#ede7f6"}, 59 | { "deep-purple-100", "#d1c4e9"}, 60 | { "deep-purple-200", "#b39ddb"}, 61 | { "deep-purple-300", "#9575cd"}, 62 | { "deep-purple-400", "#7e57c2"}, 63 | { "deep-purple-500", "#673ab7"}, 64 | { "deep-purple-600", "#5e35b1"}, 65 | { "deep-purple-700", "#512da8"}, 66 | { "deep-purple-800", "#4527a0"}, 67 | { "deep-purple-900", "#311b92"}, 68 | { "deep-purple-a100", "#b388ff"}, 69 | { "deep-purple-a200", "#7c4dff"}, 70 | { "deep-purple-a400", "#651fff"}, 71 | { "deep-purple-a700", "#6200ea"}, 72 | 73 | /* Indigo */ 74 | { "indigo", "#3f51b5"}, 75 | { "indigo-50", "#e8eaf6"}, 76 | { "indigo-100", "#c5cae9"}, 77 | { "indigo-200", "#9fa8da"}, 78 | { "indigo-300", "#7986cb"}, 79 | { "indigo-400", "#5c6bc0"}, 80 | { "indigo-500", "#3f51b5"}, 81 | { "indigo-600", "#3949ab"}, 82 | { "indigo-700", "#303f9f"}, 83 | { "indigo-800", "#283593"}, 84 | { "indigo-900", "#1a237e"}, 85 | { "indigo-a100", "#8c9eff"}, 86 | { "indigo-a200", "#536dfe"}, 87 | { "indigo-a400", "#3d5afe"}, 88 | { "indigo-a700", "#304ffe"}, 89 | 90 | /* Blue */ 91 | { "blue", "#2196f3"}, 92 | { "blue-50", "#e3f2fd"}, 93 | { "blue-100", "#bbdefb"}, 94 | { "blue-200", "#90caf9"}, 95 | { "blue-300", "#64b5f6"}, 96 | { "blue-400", "#42a5f5"}, 97 | { "blue-500", "#2196f3"}, 98 | { "blue-600", "#1e88e5"}, 99 | { "blue-700", "#1976d2"}, 100 | { "blue-800", "#1565c0"}, 101 | { "blue-900", "#0d47a1"}, 102 | { "blue-a100", "#82b1ff"}, 103 | { "blue-a200", "#448aff"}, 104 | { "blue-a400", "#2979ff"}, 105 | { "blue-a700", "#2962ff"}, 106 | 107 | /* Light Blue */ 108 | { "light-blue", "#03a9f4"}, 109 | { "light-blue-50", "#e1f5fe"}, 110 | { "light-blue-100", "#b3e5fc"}, 111 | { "light-blue-200", "#81d4fa"}, 112 | { "light-blue-300", "#4fc3f7"}, 113 | { "light-blue-400", "#29b6f6"}, 114 | { "light-blue-500", "#03a9f4"}, 115 | { "light-blue-600", "#039be5"}, 116 | { "light-blue-700", "#0288d1"}, 117 | { "light-blue-800", "#0277bd"}, 118 | { "light-blue-900", "#01579b"}, 119 | { "light-blue-a100", "#80d8ff"}, 120 | { "light-blue-a200", "#40c4ff"}, 121 | { "light-blue-a400", "#00b0ff"}, 122 | { "light-blue-a700", "#0091ea"}, 123 | 124 | /* Cyan */ 125 | { "cyan", "#00bcd4"}, 126 | { "cyan-50", "#e0f7fa"}, 127 | { "cyan-100", "#b2ebf2"}, 128 | { "cyan-200", "#80deea"}, 129 | { "cyan-300", "#4dd0e1"}, 130 | { "cyan-400", "#26c6da"}, 131 | { "cyan-500", "#00bcd4"}, 132 | { "cyan-600", "#00acc1"}, 133 | { "cyan-700", "#0097a7"}, 134 | { "cyan-800", "#00838f"}, 135 | { "cyan-900", "#006064"}, 136 | { "cyan-a100", "#84ffff"}, 137 | { "cyan-a200", "#18ffff"}, 138 | { "cyan-a400", "#00e5ff"}, 139 | { "cyan-a700", "#00b8d4"}, 140 | 141 | /* Teal */ 142 | { "teal", "#009688"}, 143 | { "teal-50", "#e0f2f1"}, 144 | { "teal-100", "#b2dfdb"}, 145 | { "teal-200", "#80cbc4"}, 146 | { "teal-300", "#4db6ac"}, 147 | { "teal-400", "#26a69a"}, 148 | { "teal-500", "#009688"}, 149 | { "teal-600", "#00897b"}, 150 | { "teal-700", "#00796b"}, 151 | { "teal-800", "#00695c"}, 152 | { "teal-900", "#004d40"}, 153 | { "teal-a100", "#a7ffeb"}, 154 | { "teal-a200", "#64ffda"}, 155 | { "teal-a400", "#1de9b6"}, 156 | { "teal-a700", "#00bfa5"}, 157 | 158 | /* Green */ 159 | { "green", "#4caf50"}, 160 | { "green-50", "#e8f5e9"}, 161 | { "green-100", "#c8e6c9"}, 162 | { "green-200", "#a5d6a7"}, 163 | { "green-300", "#81c784"}, 164 | { "green-400", "#66bb6a"}, 165 | { "green-500", "#4caf50"}, 166 | { "green-600", "#43a047"}, 167 | { "green-700", "#388e3c"}, 168 | { "green-800", "#2e7d32"}, 169 | { "green-900", "#1b5e20"}, 170 | { "green-a100", "#b9f6ca"}, 171 | { "green-a200", "#69f0ae"}, 172 | { "green-a400", "#00e676"}, 173 | { "green-a700", "#00c853"}, 174 | 175 | /* Light Green */ 176 | { "light-green", "#8bc34a"}, 177 | { "light-green-50", "#f1f8e9"}, 178 | { "light-green-100", "#dcedc8"}, 179 | { "light-green-200", "#c5e1a5"}, 180 | { "light-green-300", "#aed581"}, 181 | { "light-green-400", "#9ccc65"}, 182 | { "light-green-500", "#8bc34a"}, 183 | { "light-green-600", "#7cb342"}, 184 | { "light-green-700", "#689f38"}, 185 | { "light-green-800", "#558b2f"}, 186 | { "light-green-900", "#33691e"}, 187 | { "light-green-a100", "#ccff90"}, 188 | { "light-green-a200", "#b2ff59"}, 189 | { "light-green-a400", "#76ff03"}, 190 | { "light-green-a700", "#64dd17"}, 191 | 192 | /* Lime */ 193 | { "lime", "#cddc39"}, 194 | { "lime-50", "#f9fbe7"}, 195 | { "lime-100", "#f0f4c3"}, 196 | { "lime-200", "#e6ee9c"}, 197 | { "lime-300", "#dce775"}, 198 | { "lime-400", "#d4e157"}, 199 | { "lime-500", "#cddc39"}, 200 | { "lime-600", "#c0ca33"}, 201 | { "lime-700", "#afb42b"}, 202 | { "lime-800", "#9e9d24"}, 203 | { "lime-900", "#827717"}, 204 | { "lime-a100", "#f4ff81"}, 205 | { "lime-a200", "#eeff41"}, 206 | { "lime-a400", "#c6ff00"}, 207 | { "lime-a700", "#aeea00"}, 208 | 209 | /* Yellow */ 210 | { "yellow", "#ffeb3b"}, 211 | { "yellow-50", "#fffde7"}, 212 | { "yellow-100", "#fff9c4"}, 213 | { "yellow-200", "#fff59d"}, 214 | { "yellow-300", "#fff176"}, 215 | { "yellow-400", "#ffee58"}, 216 | { "yellow-500", "#ffeb3b"}, 217 | { "yellow-600", "#fdd835"}, 218 | { "yellow-700", "#fbc02d"}, 219 | { "yellow-800", "#f9a825"}, 220 | { "yellow-900", "#f57f17"}, 221 | { "yellow-a100", "#ffff8d"}, 222 | { "yellow-a200", "#ffff00"}, 223 | { "yellow-a400", "#ffea00"}, 224 | { "yellow-a700", "#ffd600"}, 225 | 226 | /* Amber */ 227 | { "amber", "#ffc107"}, 228 | { "amber-50", "#fff8e1"}, 229 | { "amber-100", "#ffecb3"}, 230 | { "amber-200", "#ffe082"}, 231 | { "amber-300", "#ffd54f"}, 232 | { "amber-400", "#ffca28"}, 233 | { "amber-500", "#ffc107"}, 234 | { "amber-600", "#ffb300"}, 235 | { "amber-700", "#ffa000"}, 236 | { "amber-800", "#ff8f00"}, 237 | { "amber-900", "#ff6f00"}, 238 | { "amber-a100", "#ffe57f"}, 239 | { "amber-a200", "#ffd740"}, 240 | { "amber-a400", "#ffc400"}, 241 | { "amber-a700", "#ffab00"}, 242 | 243 | /* Orange */ 244 | { "orange", "#ff9800"}, 245 | { "orange-50", "#fff3e0"}, 246 | { "orange-100", "#ffe0b2"}, 247 | { "orange-200", "#ffcc80"}, 248 | { "orange-300", "#ffb74d"}, 249 | { "orange-400", "#ffa726"}, 250 | { "orange-500", "#ff9800"}, 251 | { "orange-600", "#fb8c00"}, 252 | { "orange-700", "#f57c00"}, 253 | { "orange-800", "#ef6c00"}, 254 | { "orange-900", "#e65100"}, 255 | { "orange-a100", "#ffd180"}, 256 | { "orange-a200", "#ffab40"}, 257 | { "orange-a400", "#ff9100"}, 258 | { "orange-a700", "#ff6d00"}, 259 | 260 | /* Deep Orange */ 261 | { "deep-orange", "#ff5722"}, 262 | { "deep-orange-50", "#fbe9e7"}, 263 | { "deep-orange-100", "#ffccbc"}, 264 | { "deep-orange-200", "#ffab91"}, 265 | { "deep-orange-300", "#ff8a65"}, 266 | { "deep-orange-400", "#ff7043"}, 267 | { "deep-orange-500", "#ff5722"}, 268 | { "deep-orange-600", "#f4511e"}, 269 | { "deep-orange-700", "#e64a19"}, 270 | { "deep-orange-800", "#d84315"}, 271 | { "deep-orange-900", "#bf360c"}, 272 | { "deep-orange-a100", "#ff9e80"}, 273 | { "deep-orange-a200", "#ff6e40"}, 274 | { "deep-orange-a400", "#ff3d00"}, 275 | { "deep-orange-a700", "#dd2c00"}, 276 | 277 | /* Brown */ 278 | { "brown", "#795548"}, 279 | { "brown-50", "#efebe9"}, 280 | { "brown-100", "#d7ccc8"}, 281 | { "brown-200", "#bcaaa4"}, 282 | { "brown-300", "#a1887f"}, 283 | { "brown-400", "#8d6e63"}, 284 | { "brown-500", "#795548"}, 285 | { "brown-600", "#6d4c41"}, 286 | { "brown-700", "#5d4037"}, 287 | { "brown-800", "#4e342e"}, 288 | { "brown-900", "#3e2723"}, 289 | 290 | /* Grey */ 291 | { "grey", "#9e9e9e"}, 292 | { "grey-50", "#fafafa"}, 293 | { "grey-100", "#f5f5f5"}, 294 | { "grey-200", "#eeeeee"}, 295 | { "grey-300", "#e0e0e0"}, 296 | { "grey-400", "#bdbdbd"}, 297 | { "grey-500", "#9e9e9e"}, 298 | { "grey-600", "#757575"}, 299 | { "grey-700", "#616161"}, 300 | { "grey-800", "#424242"}, 301 | { "grey-900", "#212121"}, 302 | 303 | /* Blue Grey */ 304 | { "blue-grey", "#607d8b"}, 305 | { "blue-grey-50", "#eceff1"}, 306 | { "blue-grey-100", "#cfd8dc"}, 307 | { "blue-grey-200", "#b0bec5"}, 308 | { "blue-grey-300", "#90a4ae"}, 309 | { "blue-grey-400", "#78909c"}, 310 | { "blue-grey-500", "#607d8b"}, 311 | { "blue-grey-600", "#546e7a"}, 312 | { "blue-grey-700", "#455a64"}, 313 | { "blue-grey-800", "#37474f"}, 314 | { "blue-grey-900", "#263238"}, 315 | 316 | /* White / Black */ 317 | { "white", "#ffffff"}, 318 | { "black", "#000000"} 319 | }; 320 | -------------------------------------------------------------------------------- /src/common/Common.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define OPENVC_DATASET_CATALOG_STRUCUTRE_NEGATIVE_SAMPLES "negative" 7 | #define OPENVC_DATASET_CATALOG_STRUCUTRE_POSITIVE_SAMPLES "positive" 8 | 9 | #define OPEN_CV_DATASETS_CARS "https://cogcomp.seas.upenn.edu/Data/Car/" 10 | 11 | /* 12 | * Type of neural network frameworks 13 | */ 14 | typedef enum NEUR_NET_FRAMEWORK { 15 | OpenCV, 16 | } NEUR_NET_FRAMEWORK; 17 | 18 | /** 19 | * Instead of remove_pointer 20 | */ 21 | template struct remove_ptr { typedef T type; }; 22 | template struct remove_ptr { 23 | typedef typename remove_ptr::type type; 24 | }; 25 | 26 | template > * = nullptr> 28 | bool equal(const T &lhs, const T &rhs) { 29 | return lhs == rhs; 30 | } 31 | 32 | template > * = nullptr> 33 | bool equal(const T &lhs, const T &rhs) { 34 | return qFuzzyCompare(lhs + T(1), rhs + T(1)); 35 | } 36 | 37 | template bool updateIfChanged(T &lhs, const T &rhs) { 38 | if (!equal(lhs, rhs)) { 39 | lhs = rhs; 40 | return true; 41 | } 42 | 43 | return false; 44 | } 45 | 46 | template bool updateIfChanged(QPointer &lhs, T *rhs) { 47 | if (lhs != rhs) { 48 | lhs = rhs; 49 | return true; 50 | } 51 | 52 | return false; 53 | } 54 | 55 | class QMLObjectGetter { 56 | inline static QQuickWindow *rootWindow; 57 | 58 | public: 59 | static const QMLObjectGetter & 60 | getInstance(QQuickWindow *aRootWindow = nullptr) { 61 | static QMLObjectGetter instance; 62 | 63 | if (rootWindow == nullptr) 64 | rootWindow = aRootWindow; 65 | 66 | return instance; 67 | } 68 | 69 | static QObject *getQmlObject(QString objectName) { 70 | QObject *object = rootWindow->findChild(objectName); 71 | if (!object) { 72 | qDebug()<<"Couldn't find "< 79 | static void setPropertyOfQmlObject(QString objectName, const char *property, 80 | T value) { 81 | getQmlObject(objectName)->setProperty(property, value); 82 | } 83 | }; 84 | -------------------------------------------------------------------------------- /src/common/PropertyHelpers.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Common.h" 4 | 5 | #define SHORT_AUTO_PROPERTY(TYPE, NAME) AUTO_PROPERTY(TYPE, NAME, get##NAME, set##NAME, NAME##Changed) 6 | 7 | /** 8 | Internal helper to create member for usage as AUTO_PROPERTY 9 | */ 10 | #define ADD_AUTO_PROPERTY_MEMBERS(type, member_type, name, getter_visibility, getter, setter_visibility, setter, notify) \ 11 | getter_visibility: \ 12 | type getter() const \ 13 | { \ 14 | return name; \ 15 | } \ 16 | setter_visibility Q_SLOTS: \ 17 | void setter(type value) \ 18 | { \ 19 | if (updateIfChanged(name, value)) \ 20 | { \ 21 | emit notify(value); \ 22 | } \ 23 | } \ 24 | Q_SIGNALS: \ 25 | void notify(type name); \ 26 | private: \ 27 | member_type name {}; 28 | 29 | /** 30 | Macro for generating simple properties automatically 31 | 32 | It creates member, getter, setter and notify signal. 33 | 34 | Should be used in private section. 35 | 36 | Q_SIGNALS macro is used instead of signals keyword, because it wasn't working with the latter 37 | */ 38 | #define AUTO_PROPERTY(type, name, getter, setter, notify) \ 39 | Q_PROPERTY(type name READ getter WRITE setter NOTIFY notify) \ 40 | ADD_AUTO_PROPERTY_MEMBERS(type, type, name, public, getter, public, setter, notify) 41 | 42 | #define AUTO_MEMBER_PROPERTY(type, name) \ 43 | Q_PROPERTY(type name MEMBER name) \ 44 | public: \ 45 | type name {}; 46 | 47 | /** 48 | Macro for generating simple read only properties automatically 49 | */ 50 | #define AUTO_READONLY_PROPERTY(type, name, getter, setter, notify) \ 51 | Q_PROPERTY(type name READ getter NOTIFY notify) \ 52 | ADD_AUTO_PROPERTY_MEMBERS(type, type, name, public, getter, public, setter, notify) 53 | 54 | /** 55 | Macro for generating simple write only properties automatically 56 | */ 57 | #define AUTO_WRITEONLY_PROPERTY(type, name, setter, notify) \ 58 | Q_PROPERTY(type name READ name WRITE setter NOTIFY notify) \ 59 | ADD_AUTO_PROPERTY_MEMBERS(type, type, name, protected, name, public, setter, notify) 60 | -------------------------------------------------------------------------------- /src/component/CvMediaPlayer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2021/12/28. 3 | */ 4 | 5 | #include "CvMediaPlayer.h" 6 | 7 | #include 8 | 9 | #include "util/CvImageUtil.h" 10 | #include "service/CvBaseService.h" 11 | 12 | CvMediaPlayer::CvMediaPlayer(QQuickItem *parent) : QQuickFramebufferObject(parent) { 13 | 14 | frameCache = new std::stack(); 15 | timer = new QTimer(this); 16 | connect(timer, SIGNAL(timeout()), this, SLOT(run())); 17 | openCamera(); 18 | 19 | } 20 | 21 | QQuickFramebufferObject::Renderer *CvMediaPlayer::createRenderer() const { 22 | 23 | auto renderer = new CvMediaPlayerRenderer(); 24 | renderer->setWindow(window()); 25 | renderer->setFrameCache(frameCache); 26 | return renderer; 27 | 28 | } 29 | 30 | void CvMediaPlayer::run() { 31 | 32 | updateFrame(); 33 | CvFrameController::getInstance().processImageByService(frame); 34 | cvMat2QImage(frame); 35 | update(); 36 | } 37 | 38 | void CvMediaPlayer::openPicture(QString path) { 39 | 40 | stop(); 41 | 42 | frame = CvImageUtil::loadCvImage(std::move(path)); 43 | srcWidth = frame.cols; 44 | srcHeight = frame.rows; 45 | 46 | CvFrameController::getInstance().processImageByService(frame); 47 | cvMat2QImage(frame); 48 | update(); 49 | } 50 | 51 | void CvMediaPlayer::openWebPicture(const QString &path) { 52 | 53 | stop(); 54 | capture.open(path.toStdString()); 55 | if (!capture.isOpened()) { 56 | qDebug() << "Failed to open video"; 57 | return; 58 | } 59 | initializeCam(); 60 | run(); 61 | } 62 | 63 | 64 | /** 65 | * 差进度条 66 | * @param path 67 | */ 68 | void CvMediaPlayer::openVideo(QString path) { 69 | 70 | stop(); 71 | path.replace("file:///", ""); 72 | 73 | capture.open(path.toStdString()); 74 | if (!capture.isOpened()) { 75 | qDebug() << "Failed to open video"; 76 | return; 77 | } 78 | initializeCam(); 79 | updateFrame(); 80 | cvMat2QImage(frame); 81 | timer->start(TIMER_TIMEOUT); 82 | 83 | } 84 | 85 | 86 | void CvMediaPlayer::openCamera() { 87 | 88 | stop(); 89 | capture.open(0); 90 | 91 | if (!capture.isOpened()) { 92 | qDebug() << "Failed to open camera"; 93 | return; 94 | } 95 | 96 | initializeCam(); 97 | updateFrame(); 98 | cvMat2QImage(frame); 99 | timer->start(TIMER_TIMEOUT); 100 | 101 | } 102 | 103 | void CvMediaPlayer::openWebCam(const QString &ip) { 104 | 105 | stop(); 106 | capture.open(ip.toStdString()); 107 | 108 | if (!capture.isOpened()) { 109 | qDebug() << "Failed to open camera"; 110 | return; 111 | } 112 | 113 | initializeCam(); 114 | updateFrame(); 115 | cvMat2QImage(frame); 116 | timer->start(TIMER_TIMEOUT); 117 | 118 | } 119 | 120 | QString CvMediaPlayer::savePicture(QString path) { 121 | 122 | path.replace("file:///", ""); 123 | 124 | timer->stop(); 125 | 126 | QString uuid = QUuid::createUuid().toString(). 127 | replace("{", ""). 128 | replace("}", ""). 129 | replace("-", ""); 130 | 131 | cv::Mat temp; 132 | 133 | // correct color 134 | cv::cvtColor(frame, temp, cv::COLOR_BGR2RGB); 135 | 136 | cv::imwrite(path.toStdString() + "/" + uuid.toStdString() + ".jpg", temp); 137 | 138 | qDebug() << "success to save " + uuid + ".jpg"; 139 | 140 | timer->start(TIMER_TIMEOUT); 141 | 142 | return uuid + ".jpg"; 143 | } 144 | 145 | 146 | void CvMediaPlayer::stop() { 147 | 148 | timer->stop(); 149 | capture.release(); 150 | 151 | } 152 | 153 | void CvMediaPlayer::initializeCam() { 154 | 155 | 156 | // Try to set 1280 X 720 157 | capture.set(cv::CAP_PROP_FRAME_WIDTH, 1280); 158 | capture.set(cv::CAP_PROP_FRAME_HEIGHT, 720); 159 | capture.set(cv::CAP_PROP_FPS, 10); 160 | 161 | // Retrieve the width and height of the captured frame 162 | srcWidth = (int) capture.get(cv::CAP_PROP_FRAME_WIDTH); 163 | srcHeight = (int) capture.get(cv::CAP_PROP_FRAME_HEIGHT); 164 | 165 | qDebug() << "capture size=" << srcWidth << "x" << srcHeight; 166 | 167 | //Be determined by interrupt timer 168 | fps = 1000 / TIMER_TIMEOUT; 169 | 170 | } 171 | 172 | void CvMediaPlayer::updateFrame() { 173 | 174 | if (!capture.isOpened()) { 175 | qDebug() << "Failed to open camera"; 176 | return; 177 | } 178 | 179 | capture >> frame; 180 | 181 | if (frame.empty()) { 182 | qDebug() << "Failed to retrieve frame"; 183 | return; 184 | } 185 | } 186 | 187 | void CvMediaPlayer::cvMat2QImage(const cv::Mat &src) { 188 | 189 | if (!frameCache) { 190 | delete frameCache->top(); 191 | frameCache->pop(); 192 | } 193 | 194 | QImage *image; 195 | 196 | // fix correct 197 | cv::cvtColor(src, src, cv::COLOR_BGR2RGB); 198 | 199 | // 8-bits unsigned, NO. OF CHANNELS = 1 200 | if (src.type() == CV_8UC1) { 201 | image = new QImage(src.cols, src.rows, QImage::Format_Indexed8); 202 | // Set the color table (used to translate colour indexes to qRgb values) 203 | image->setColorCount(256); 204 | for (int i = 0; i < 256; i++) { 205 | image->setColor(i, qRgb(i, i, i)); 206 | } 207 | // Copy input Mat 208 | uchar *pSrc = src.data; 209 | for (int row = 0; row < src.rows; row++) { 210 | uchar *pDest = image->scanLine(row); 211 | memcpy(pDest, pSrc, src.cols); 212 | pSrc += src.step; 213 | } 214 | } 215 | // 8-bits unsigned, NO. OF CHANNELS = 3 216 | else if (src.type() == CV_8UC3) { 217 | // Copy input Mat 218 | const auto *pSrc = (const uchar *) src.data; 219 | // Create QImage with same dimensions as input Mat 220 | image = new QImage(pSrc, src.cols, src.rows, src.step, QImage::Format_RGB888); 221 | image->rgbSwapped(); 222 | } else if (src.type() == CV_8UC4) { 223 | // Copy input Mat 224 | const auto *pSrc = (const uchar *) src.data; 225 | // Create QImage with same dimensions as input Mat 226 | image = new QImage(pSrc, src.cols, src.rows, src.step, QImage::Format_ARGB32); 227 | image->copy(); 228 | } else { 229 | qDebug() << "ERROR: Mat could not be converted to QImage."; 230 | image = new QImage(); 231 | } 232 | 233 | frameCache->push(image); 234 | } 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | -------------------------------------------------------------------------------- /src/component/CvMediaPlayer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2021/12/28. 3 | */ 4 | #ifndef OBJECT_MEASURE_CAMERA_H 5 | #define OBJECT_MEASURE_CAMERA_H 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | #include 14 | 15 | #include "common/PropertyHelpers.h" 16 | #include "controller/CvFrameController.h" 17 | #include "CvMediaPlayerRenderer.h" 18 | 19 | class CvMediaPlayer : public QQuickFramebufferObject { 20 | 21 | Q_OBJECT 22 | 23 | AUTO_PROPERTY(int, srcWidth, getWidth, setWidth, widthChanged) 24 | AUTO_PROPERTY(int, srcHeight, getHeight, setHeight, heightChanged) 25 | AUTO_PROPERTY(int, fps, getFps, setFps, fpsChanged) 26 | 27 | // video parameters 28 | AUTO_PROPERTY(double, duration, getDuration, setDuration, durationChanged) 29 | AUTO_PROPERTY(double, position, getPosition, setPosition, positionChanged) 30 | public: 31 | explicit CvMediaPlayer(QQuickItem *parent = nullptr); 32 | 33 | Renderer *createRenderer() const override; 34 | 35 | Q_INVOKABLE void openPicture(QString path); 36 | 37 | Q_INVOKABLE void openVideo(QString path); 38 | 39 | Q_INVOKABLE void openWebPicture(const QString &path); 40 | 41 | Q_INVOKABLE void openCamera(); 42 | 43 | Q_INVOKABLE void openWebCam(const QString &ip); 44 | 45 | Q_INVOKABLE QString savePicture(QString path); 46 | 47 | Q_INVOKABLE void stop(); 48 | 49 | 50 | private: 51 | 52 | void updateFrame(); 53 | 54 | void cvMat2QImage(const cv::Mat &src); 55 | 56 | 57 | 58 | void initializeCam(); 59 | 60 | private slots: 61 | 62 | void run(); 63 | 64 | private: 65 | 66 | std::stack *frameCache{}; 67 | cv::Mat frame; 68 | cv::VideoCapture capture; 69 | QTimer *timer{}; 70 | int TIMER_TIMEOUT = 30; 71 | 72 | }; 73 | 74 | #endif -------------------------------------------------------------------------------- /src/component/CvMediaPlayerRenderer.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2021/12/28. 3 | */ 4 | 5 | #include "CvMediaPlayerRenderer.h" 6 | 7 | CvMediaPlayerRenderer::CvMediaPlayerRenderer() { 8 | 9 | initializeOpenGl(); 10 | 11 | } 12 | 13 | CvMediaPlayerRenderer::~CvMediaPlayerRenderer() { 14 | 15 | delete shader; 16 | delete texture; 17 | } 18 | 19 | void CvMediaPlayerRenderer::render() { 20 | 21 | updateTexture(); 22 | renderOpenGl(); 23 | } 24 | 25 | QOpenGLFramebufferObject *CvMediaPlayerRenderer::createFramebufferObject(const QSize &size) { 26 | QOpenGLFramebufferObjectFormat format; 27 | format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); 28 | format.setSamples(4); 29 | return new QOpenGLFramebufferObject(size, format); 30 | } 31 | 32 | /** 33 | * initialize OpenGL Shader Texture 34 | */ 35 | void CvMediaPlayerRenderer::initializeOpenGl() { 36 | 37 | initializeOpenGLFunctions(); 38 | 39 | /** 40 | * 设置着色器程序,绑定数值 41 | */ 42 | 43 | shader = new QOpenGLShaderProgram(); 44 | 45 | if (!shader->addCacheableShaderFromSourceFile( 46 | QOpenGLShader::Vertex, ":/assets/glsl/shader.vert")) { 47 | qDebug() << "Compiler vertex error"; 48 | exit(0); 49 | } 50 | if (!shader->addCacheableShaderFromSourceFile( 51 | QOpenGLShader::Fragment, ":/assets/glsl/shader.frag")) { 52 | qDebug() << "Compiler fragment error"; 53 | exit(0); 54 | } 55 | 56 | shader->link(); 57 | 58 | float vertices[] = { 59 | -1.0f, -1.0f, 0.0f, 60 | 1.0f, -1.0f, 0.0f, 61 | 1.0f, 1.0f, 0.0f, 62 | 1.0f, 1.0f, 0.0f, 63 | -1.0f, 1.0f, 0.0f, 64 | -1.0f, -1.0f, 0.0f, 65 | }; 66 | float texCoords[] = { 67 | 0.0f, 0.0f, 68 | 1.0f, 0.0f, 69 | 1.0f, 1.0f, 70 | 1.0f, 1.0f, 71 | 0.0f, 1.0f, 72 | 0.0f, 0.0f, 73 | }; 74 | 75 | pos = shader->attributeLocation("aPos"); 76 | texCoord = shader->attributeLocation("aTexCoord"); 77 | texType = shader->uniformLocation("texType"); 78 | 79 | 80 | for (int i = 0; i < sizeof(vertices) / sizeof(float); i += 3) 81 | posList.push_back(QVector3D(vertices[i], vertices[i + 1], vertices[i + 2])); 82 | 83 | 84 | for (int i = 0; i < sizeof(texCoords) / sizeof(float); i += 2) 85 | texCoordList.push_back(QVector2D(texCoords[i], texCoords[i + 1])); 86 | 87 | shader->enableAttributeArray(pos); 88 | shader->enableAttributeArray(texCoord); 89 | shader->setAttributeArray(pos, posList.constData()); 90 | shader->setAttributeArray(texCoord, texCoordList.constData()); 91 | shader->disableAttributeArray(pos); 92 | 93 | } 94 | 95 | void CvMediaPlayerRenderer::renderOpenGl() { 96 | 97 | glEnable(GL_DEPTH_TEST); 98 | glEnable(GL_BLEND); 99 | glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); 100 | 101 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 102 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 103 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 104 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 105 | 106 | shader->bind(); 107 | shader->setUniformValue(texType, 1); 108 | texture->bind(1); 109 | shader->enableAttributeArray(pos); 110 | shader->enableAttributeArray(texCoord); 111 | shader->setAttributeArray(pos, posList.constData()); 112 | shader->setAttributeArray(texCoord, texCoordList.constData()); 113 | glDrawArrays(GL_TRIANGLES, 0, posList.size()); 114 | shader->disableAttributeArray(pos); 115 | texture->release(); 116 | shader->release(); 117 | 118 | window->resetOpenGLState(); 119 | } 120 | 121 | /** 122 | * convert cv::Mat to OpenGL texture 123 | */ 124 | void CvMediaPlayerRenderer::updateTexture() { 125 | 126 | if (texture != nullptr) { 127 | texture->destroy(); 128 | delete texture; 129 | } 130 | 131 | if (frameCache->empty()) 132 | return; 133 | 134 | 135 | texture = new QOpenGLTexture(*frameCache->top(), 136 | QOpenGLTexture::GenerateMipMaps); 137 | 138 | if (!texture->isCreated()) { 139 | qDebug() << "Failed to load texture"; 140 | exit(0); 141 | } 142 | 143 | // set the texture wrapping parameters 144 | texture->setWrapMode(QOpenGLTexture::DirectionS, QOpenGLTexture::Repeat); 145 | texture->setWrapMode(QOpenGLTexture::DirectionT, QOpenGLTexture::Repeat); 146 | // set texture filtering parameters 147 | texture->setMinificationFilter(QOpenGLTexture::Linear); 148 | 149 | } 150 | 151 | 152 | -------------------------------------------------------------------------------- /src/component/CvMediaPlayerRenderer.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2021/12/28. 3 | */ 4 | 5 | #ifndef OBJECT_MEASURE_CAMERARENDERER_H 6 | #define OBJECT_MEASURE_CAMERARENDERER_H 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | 19 | /** 20 | * OpenGL 3.3 核心配置文件 21 | */ 22 | 23 | #include 24 | 25 | class CvMediaPlayerRenderer : public QQuickFramebufferObject::Renderer, 26 | protected QOpenGLFunctions_3_3_Core { 27 | public: 28 | CvMediaPlayerRenderer(); 29 | 30 | ~CvMediaPlayerRenderer() override; 31 | 32 | void render() override; 33 | 34 | QOpenGLFramebufferObject *createFramebufferObject(const QSize &size) override; 35 | 36 | void setWindow(QQuickWindow *window) { this->window = window; } 37 | 38 | void setFrameCache(std::stack *cache) { frameCache = cache; } 39 | 40 | private: 41 | 42 | void initializeOpenGl(); 43 | 44 | void renderOpenGl(); 45 | 46 | void updateTexture(); 47 | 48 | 49 | private: 50 | 51 | int pos{}; 52 | int texCoord{}; 53 | int texType{}; 54 | 55 | QVector posList; 56 | QVector texCoordList; 57 | 58 | QOpenGLShaderProgram *shader{nullptr}; 59 | QOpenGLTexture *texture{nullptr}; 60 | QQuickWindow *window{nullptr}; 61 | std::stack *frameCache{}; 62 | 63 | }; 64 | 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /src/controller/CvFrameController.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include "CvFrameController.h" 11 | 12 | CvFrameController::CvFrameController(QObject *parent) : 13 | QObject(parent), 14 | cvFrameServiceList(new ObjectListModel()) { 15 | 16 | initializeService(); 17 | 18 | } 19 | 20 | CvFrameController &CvFrameController::getInstance() { 21 | 22 | static CvFrameController cvFrameController; 23 | return cvFrameController; 24 | 25 | } 26 | 27 | void CvFrameController::initializeService() { 28 | 29 | cvFrameServiceList->append(new WipeOffService()); 30 | cvFrameServiceList->append(new UnDistortService()); 31 | cvFrameServiceList->append(new GrayService()); 32 | cvFrameServiceList->append(new MeasureA4Service()); 33 | cvFrameServiceList->append(new MeasureCoinService()); 34 | 35 | } 36 | 37 | QObject *CvFrameController::getServiceObjectByName(const QString &name) const { 38 | for (auto *serviceObj : cvFrameServiceList->getRawData()) { 39 | auto obj = qobject_cast(serviceObj); 40 | if (obj->getName().toLower() == name.toLower()) 41 | return obj; 42 | } 43 | return nullptr; 44 | } 45 | 46 | void CvFrameController::processImageByService(const cv::Mat &image) const { 47 | 48 | for (auto service : cvFrameServiceList->getRawData()) { 49 | auto obj = qobject_cast(service); 50 | 51 | /** 52 | * CvMediaPlayer is always running. When isActive() is triggered, 53 | * service will process every frame from media. 54 | */ 55 | 56 | if (obj->getIsActive()) { 57 | obj->processImage(image); 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /src/controller/CvFrameController.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_CVFRAMECONTROLLER_H 6 | #define QT_OBJECT_MEASURE_CVFRAMECONTROLLER_H 7 | 8 | #include "common/PropertyHelpers.h" 9 | #include "model/ObjectListModel.h" 10 | 11 | #include 12 | 13 | class CvFrameController : public QObject { 14 | 15 | Q_OBJECT 16 | 17 | AUTO_PROPERTY(ObjectListModel *, cvFrameServiceList, getCvFrameServiceList, setCvFrameServiceList, 18 | cvFrameServiceListChanged) 19 | 20 | private: 21 | explicit CvFrameController(QObject *parent = nullptr); 22 | 23 | public: 24 | ~CvFrameController() override = default; 25 | 26 | static CvFrameController &getInstance(); 27 | 28 | void initializeService(); 29 | 30 | Q_INVOKABLE QObject *getServiceObjectByName(const QString &name) const; 31 | 32 | Q_INVOKABLE void processImageByService(const cv::Mat &image) const; 33 | 34 | }; 35 | 36 | 37 | #endif 38 | -------------------------------------------------------------------------------- /src/controller/MediaController.cpp: -------------------------------------------------------------------------------- 1 | #include "MediaController.h" 2 | 3 | #include 4 | MediaController::MediaController() = default; 5 | 6 | QString MediaController::convertVideoTimeToTimeString(int value) 7 | { 8 | int seconds = (value/1000) % 60; 9 | int minutes = (value/60000) % 60; 10 | int hours = (value/3600000) % 24; 11 | 12 | QTime time(hours, minutes,seconds); 13 | return time.toString(); 14 | } 15 | 16 | MediaController &MediaController::getInstance() 17 | { 18 | static MediaController mediaController; 19 | return mediaController; 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /src/controller/MediaController.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "system/ImageProvider.h" 5 | #include "common/PropertyHelpers.h" 6 | 7 | class MediaController: public QObject 8 | { 9 | Q_OBJECT 10 | MediaController(); 11 | AUTO_PROPERTY(int, imageWidth, getImageWidth,setImageWidth, imageWidthChanged) 12 | AUTO_PROPERTY(int, imageHeigth, getImageHeigth,setImageHeigth, imageHeigthChanged) 13 | 14 | 15 | public: 16 | static MediaController& getInstance(); 17 | 18 | Q_INVOKABLE QString convertVideoTimeToTimeString(int value); 19 | }; 20 | -------------------------------------------------------------------------------- /src/entity/ContourInfo.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/9. 3 | */ 4 | 5 | 6 | #ifndef QT_OBJECT_MEASURE_CONTOURINFO_H 7 | #define QT_OBJECT_MEASURE_CONTOURINFO_H 8 | 9 | struct ContourInfo 10 | { 11 | std::vector finalContours; 12 | int area; 13 | std::vector apex; 14 | ContourInfo(std::vector finalContours,int area,std::vector apex): 15 | finalContours(std::move(finalContours)), 16 | area(area), 17 | apex(std::move(apex)){} 18 | }; 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "component/CvMediaPlayer.h" 6 | 7 | #include "system/HttpManager.h" 8 | #include "system/FileHandler.h" 9 | #include "system/ImageProvider.h" 10 | 11 | #include "controller/MediaController.h" 12 | 13 | 14 | 15 | int main(int argc, char *argv[]) { 16 | 17 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 18 | QGuiApplication app(argc, argv); 19 | QGuiApplication::setOrganizationName("Personal"); 20 | QGuiApplication::setOrganizationDomain("China"); 21 | 22 | qmlRegisterType("online.qom.component", 1, 0, "CvMediaPlayer"); 23 | qmlRegisterType("online.qom.model", 1, 0, "ObjectListModel"); 24 | 25 | QQmlApplicationEngine engine; 26 | 27 | //Register managers 28 | engine.rootContext()->setContextProperty("cvFrameController", &CvFrameController::getInstance()); 29 | engine.rootContext()->setContextProperty("mediaController", &MediaController::getInstance()); 30 | engine.rootContext()->setContextProperty("httpManager", new HttpManager()); 31 | engine.rootContext()->setContextProperty("fileHandler", new FileHandler()); 32 | 33 | 34 | //Register processed video output 35 | engine.rootContext()->engine()->addImageProvider(QLatin1String("imageProvider"), &ImageProvider::getInstance()); 36 | engine.rootContext()->setContextProperty("imageProvider", &MediaController::getInstance()); 37 | 38 | const QUrl url("qrc:/views/main.qml"); 39 | QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, 40 | &app, [url](QObject *obj, const QUrl &objUrl) { 41 | if (!obj && url == objUrl) 42 | QCoreApplication::exit(-1); 43 | }, Qt::QueuedConnection); 44 | engine.load(url); 45 | 46 | return QGuiApplication::exec(); 47 | } -------------------------------------------------------------------------------- /src/model/ObjectListModel.cpp: -------------------------------------------------------------------------------- 1 | #include "ObjectListModel.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | ObjectListModel::ObjectListModel(bool owning, QObject* parent, QByteArray alias_role_name) 9 | : QAbstractListModel(parent) 10 | , mOwning(owning) 11 | , mRoleNames{([&]() 12 | { 13 | QHash roles = {{OBJECT_ROLE, "object"}}; 14 | if (!alias_role_name.isEmpty()) 15 | { 16 | roles.insert(ALIAS_ROLE, alias_role_name); 17 | qDebug()<<"ROLA NAME : "+alias_role_name; 18 | } 19 | return roles; 20 | })()} 21 | { 22 | connect(this, &QAbstractItemModel::rowsInserted, this, &ObjectListModel::notifyCount); 23 | connect(this, &QAbstractItemModel::rowsRemoved, this, &ObjectListModel::notifyCount); 24 | connect(this, &QAbstractItemModel::modelReset, this, &ObjectListModel::notifyCount); 25 | } 26 | 27 | ObjectListModel::ObjectListModel(QObjectList objects, bool owning, QObject* parent, QByteArray alias_role_name) 28 | : ObjectListModel(owning, parent, std::move(alias_role_name)) 29 | { 30 | mObjects = std::move(objects); 31 | attachToAll(); 32 | } 33 | 34 | QObjectList &ObjectListModel::getRawData() 35 | { 36 | return mObjects; 37 | } 38 | 39 | bool ObjectListModel::isOwning() const 40 | { 41 | return mOwning; 42 | } 43 | 44 | int ObjectListModel::rowCount(const QModelIndex&) const 45 | { 46 | return size(); 47 | } 48 | 49 | QVariant ObjectListModel::data(const QModelIndex& index, int role) const 50 | { 51 | // accepting DisplayRole, so it can work through proxies using display role as default 52 | if (!index.isValid() || index.row() >= mObjects.size() 53 | || (role != OBJECT_ROLE && role != ALIAS_ROLE && role != Qt::DisplayRole)) 54 | { 55 | return {}; 56 | } 57 | 58 | return QVariant::fromValue(mObjects[index.row()]); 59 | } 60 | 61 | QHash ObjectListModel::roleNames() const 62 | { 63 | return mRoleNames; 64 | } 65 | 66 | QObject* ObjectListModel::operator[](int index) const 67 | { 68 | return mObjects[index]; 69 | } 70 | 71 | QObject* ObjectListModel::at(int index) const 72 | { 73 | return mObjects.at(index); 74 | } 75 | 76 | QObject* ObjectListModel::first() const 77 | { 78 | return at(0); 79 | } 80 | 81 | QObject* ObjectListModel::last() const 82 | { 83 | return at(size() - 1); 84 | } 85 | 86 | int ObjectListModel::size() const 87 | { 88 | return mObjects.size(); 89 | } 90 | 91 | int ObjectListModel::indexOf(QObject* object) const 92 | { 93 | return mObjects.indexOf(object); 94 | } 95 | 96 | void ObjectListModel::append(QObject* object) 97 | { 98 | insert(size(), object); 99 | } 100 | 101 | void ObjectListModel::insert(int index, QObject* object) 102 | { 103 | if (mObjects.contains(object)) 104 | { 105 | qWarning() << "Model already contains object" << object; 106 | 107 | if (mOwning) 108 | { 109 | // Deleting would fail.. 110 | return; 111 | } 112 | } 113 | 114 | attachTo(object); 115 | 116 | beginInsertRows({}, index, index); 117 | mObjects.insert(index, object); 118 | endInsertRows(); 119 | 120 | emit childrenChanged(); 121 | } 122 | 123 | void ObjectListModel::reset(QObjectList objects) 124 | { 125 | beginResetModel(); 126 | 127 | for (auto object: mObjects) 128 | { 129 | detachFrom(object); 130 | } 131 | 132 | mObjects.swap(objects); 133 | attachToAll(); 134 | 135 | endResetModel(); 136 | 137 | emit childrenChanged(); 138 | 139 | if (mOwning) ///< Delete objects after endResetModel(), so that any dependencies are reset first. 140 | { 141 | qDeleteAll(objects); 142 | } 143 | } 144 | 145 | QObject* ObjectListModel::takeAt(int index) 146 | { 147 | if (index < 0 || index >= size()) 148 | { 149 | return nullptr; 150 | } 151 | 152 | beginRemoveRows({}, index, index); 153 | 154 | auto object = mObjects.takeAt(index); 155 | detachFrom(object); 156 | 157 | endRemoveRows(); 158 | 159 | emit childrenChanged(); 160 | 161 | return object; 162 | } 163 | 164 | QObject* ObjectListModel::take(QObject* object) 165 | { 166 | return takeAt(indexOf(object)); 167 | } 168 | 169 | void ObjectListModel::removeAt(int index) 170 | { 171 | if (auto taken_object = takeAt(index); mOwning) 172 | { 173 | delete taken_object; 174 | } 175 | } 176 | 177 | void ObjectListModel::remove(QObject* object) 178 | { 179 | if (mObjects.contains(object)) 180 | { 181 | removeAt(indexOf(object)); 182 | } 183 | else 184 | { 185 | qWarning() << "Trying to remove non-contained object (" << object << ")"; 186 | } 187 | } 188 | 189 | void ObjectListModel::clearRange(int start_index, int count) 190 | { 191 | if (start_index < 0 || count < 1 || start_index >= size()) 192 | { 193 | return; 194 | } 195 | 196 | count = std::min(count, size() - start_index); 197 | 198 | for (int index = start_index + count - 1; index >= start_index; --index) 199 | { 200 | removeAt(index); 201 | } 202 | } 203 | 204 | void ObjectListModel::notifyCount() const 205 | { 206 | emit countChanged(rowCount()); 207 | } 208 | 209 | void ObjectListModel::propertyChanged() 210 | { 211 | notifyPropertyChanged(sender()); 212 | } 213 | 214 | void ObjectListModel::notifyPropertyChanged(QObject* object) 215 | { 216 | if (int row = indexOf(object); row >= 0) 217 | { 218 | emit dataChanged(index(row), index(row), {PROPERTY_MODIFIED_ROLE}); 219 | } 220 | else 221 | { 222 | qWarning() << "Property change from unknown sender" << object; 223 | } 224 | } 225 | 226 | void ObjectListModel::attachToAll() 227 | { 228 | for (auto object : mObjects) 229 | { 230 | attachTo(object); 231 | } 232 | } 233 | 234 | void ObjectListModel::attachTo(QObject* object) 235 | { 236 | object->installEventFilter(this); 237 | auto meta = object->metaObject(); 238 | auto slot_changed = metaObject()->method(metaObject()->indexOfSlot(QString("propertyChanged()").toUtf8().constData())); 239 | for (int i = 0; i < meta->propertyCount(); ++i) 240 | { 241 | if (auto property_changed = meta->property(i).notifySignal(); property_changed.isValid()) 242 | { 243 | connect(object, property_changed, this, slot_changed, Qt::UniqueConnection); 244 | } 245 | } 246 | 247 | if (mOwning) 248 | { 249 | object->setParent(this); 250 | } 251 | QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); 252 | } 253 | 254 | void ObjectListModel::detachFrom(QObject* object) 255 | { 256 | object->removeEventFilter(this); 257 | object->disconnect(this); 258 | 259 | if (mOwning) 260 | { 261 | object->setParent(nullptr); 262 | } 263 | } 264 | 265 | bool ObjectListModel::eventFilter(QObject* watched, QEvent* event) 266 | { 267 | if (event->type() == QEvent::DynamicPropertyChange) 268 | { 269 | notifyPropertyChanged(watched); 270 | } 271 | return false; 272 | } 273 | 274 | QQmlListProperty ObjectListModel::children() 275 | { 276 | return QQmlListProperty( 277 | this, this, 278 | &ObjectListModel::qmlAppendChild, 279 | &ObjectListModel::qmlChildCount, 280 | &ObjectListModel::qmlChildAt, 281 | &ObjectListModel::qmlClearChildren 282 | ); 283 | } 284 | 285 | void ObjectListModel::qmlAppendChild(QQmlListProperty* list, QObject* child) 286 | { 287 | reinterpret_cast(list->data)->append(child); 288 | } 289 | 290 | int ObjectListModel::qmlChildCount(QQmlListProperty* list) 291 | { 292 | return reinterpret_cast(list->data)->size(); 293 | } 294 | 295 | QObject* ObjectListModel::qmlChildAt(QQmlListProperty* list, int index) 296 | { 297 | return reinterpret_cast(list->data)->at(index); 298 | } 299 | 300 | void ObjectListModel::qmlClearChildren(QQmlListProperty* list) 301 | { 302 | reinterpret_cast(list->data)->reset(); 303 | } 304 | -------------------------------------------------------------------------------- /src/model/ObjectListModel.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | class ObjectListModel : public QAbstractListModel 10 | { 11 | Q_OBJECT 12 | 13 | Q_PROPERTY(int count READ size NOTIFY countChanged) 14 | Q_PROPERTY(QQmlListProperty children READ children NOTIFY childrenChanged) 15 | Q_CLASSINFO("DefaultProperty", "children") 16 | 17 | public: 18 | enum 19 | { 20 | OBJECT_ROLE = Qt::UserRole, 21 | ALIAS_ROLE = OBJECT_ROLE + 1, 22 | PROPERTY_MODIFIED_ROLE = ALIAS_ROLE + 1, 23 | LAST_ROLE = PROPERTY_MODIFIED_ROLE 24 | }; 25 | 26 | explicit ObjectListModel(bool owning = true, QObject* parent = nullptr, QByteArray alias_role_name = {}); 27 | explicit ObjectListModel(QObjectList objects, bool owning = true, QObject* parent = nullptr, QByteArray alias_role_name = {}); 28 | 29 | bool isOwning() const; 30 | int rowCount(const QModelIndex& parent = {}) const override; 31 | QVariant data(const QModelIndex& index, int role) const override; 32 | QHash roleNames() const override; 33 | 34 | QObject* operator[](int index) const; 35 | 36 | public slots: 37 | QObject* at(int index) const; 38 | QObject* first() const; 39 | QObject* last() const; 40 | 41 | QObjectList& getRawData(); 42 | 43 | int size() const; 44 | int indexOf(QObject* object) const; 45 | 46 | void append(QObject* object); 47 | void insert(int index, QObject* object); 48 | 49 | void reset(QObjectList objects = {}); 50 | QObject* takeAt(int index); 51 | QObject* take(QObject* object); 52 | void removeAt(int index); 53 | void remove(QObject* object); 54 | void clearRange(int start_index, int count); 55 | 56 | signals: 57 | void countChanged(int count) const; 58 | void childrenChanged(); 59 | 60 | private slots: 61 | void notifyCount() const; 62 | void propertyChanged(); 63 | void notifyPropertyChanged(QObject* object); 64 | 65 | protected: 66 | void attachToAll(); 67 | virtual void attachTo(QObject* object); 68 | virtual void detachFrom(QObject* object); 69 | 70 | bool eventFilter(QObject* watched, QEvent* event) override; 71 | 72 | QObjectList mObjects; 73 | bool mOwning; 74 | const QHash mRoleNames; 75 | 76 | QQmlListProperty children(); 77 | 78 | static void qmlAppendChild(QQmlListProperty*, QObject*); 79 | static int qmlChildCount(QQmlListProperty*); 80 | static QObject* qmlChildAt(QQmlListProperty*, int); 81 | static void qmlClearChildren(QQmlListProperty*); 82 | }; 83 | -------------------------------------------------------------------------------- /src/qtconfig.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | ./views/main.qml 5 | 6 | ./views/widget/ColorHelper.qml 7 | ./views/widget/MethodsHelpers.qml 8 | ./views/widget/MenuTapBar.qml 9 | ./views/widget/SeparatorLine.qml 10 | ./views/widget/FileLoader.qml 11 | ./views/widget/ExpandingSectionButton.qml 12 | ./views/widget/FolderLoader.qml 13 | ./views/widget/ImageLoaderView.qml 14 | 15 | ./views/screen/MediaScreen/MediaScreen.qml 16 | ./views/screen/MediaScreen/VideoInputSelectPane.qml 17 | ./views/screen/MediaScreen/VideoInputSettingsPane.qml 18 | ./views/screen/MediaScreen/MediaOutputPane.qml 19 | 20 | ./views/screen/ServiceScreen/OpenCV/CalibrateSettings.qml 21 | ./views/screen/ServiceScreen/OpenCV/MeasureSettings.qml 22 | 23 | ./views/screen/ServiceScreen/CommonSettings.qml 24 | ./views/screen/ServiceScreen/ServiceScreen.qml 25 | 26 | 27 | 28 | ./assets/image/data.png 29 | ./assets/image/media.png 30 | ./assets/image/plus.png 31 | ./assets/image/loadingShutter.png 32 | ./assets/image/round_close_fullscreen_white_48dp.png 33 | ./assets/image/round_fullscreen_white_48dp.png 34 | ./assets/image/camera.png 35 | ./assets/image/memory.png 36 | ./assets/image/settings.png 37 | ./assets/image/show_chart.png 38 | ./assets/image/visibility.png 39 | ./assets/image/moreVert.png 40 | ./assets/image/play.png 41 | ./assets/image/stop.png 42 | ./assets/image/detected.png 43 | ./assets/image/opencv_icon.png 44 | ./assets/image/data_icon.png 45 | ./assets/image/pause.png 46 | ./assets/image/pngwave.png 47 | ./assets/image/left-arrow.png 48 | ./assets/image/settings_black.png 49 | ./assets/image/opencv.png 50 | ./assets/image/showchart_black.png 51 | ./assets/image/camera_black.png 52 | ./assets/image/cloud_black.png 53 | ./assets/image/brain.png 54 | ./assets/image/dumbbell.png 55 | ./assets/image/round_check_black_48dp.png 56 | ./assets/image/round_check_white_48dp.png 57 | ./assets/image/round_insert_photo_black_48dp.png 58 | ./assets/image/round_local_movies_black_48dp.png 59 | ./assets/image/round_computer_black_48dp.png 60 | ./assets/image/round_wifi_black_48dp.png 61 | ./assets/image/cctv.png 62 | ./assets/image/round_link_black_48dp.png 63 | 64 | ./assets/glsl/shader.frag 65 | ./assets/glsl/shader.vert 66 | 67 | 68 | ./qtquickcontrols2.conf 69 | 70 | 71 | -------------------------------------------------------------------------------- /src/qtquickcontrols2.conf: -------------------------------------------------------------------------------- 1 | ; This file can be edited to change the style of the application 2 | ; Read "Qt Quick Controls 2 Configuration File" for details: 3 | ; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html 4 | [Controls] 5 | Style=Material 6 | 7 | [Material] 8 | Theme=Light 9 | Accent=Red 10 | Primary=White 11 | Foreground=#696969 12 | Background=White 13 | Font\Family=Microsoft YaHei UI 14 | Font\PixelSize=12 15 | -------------------------------------------------------------------------------- /src/service/CvBaseService.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #include "CvBaseService.h" 6 | 7 | #include 8 | 9 | CvBaseService::CvBaseService(QString name) : 10 | name(std::move(name)), 11 | repoLink("https://github.com/opencv/opencv"), 12 | iconSource("qrc:/assets/image/opencv.png") {} -------------------------------------------------------------------------------- /src/service/CvBaseService.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_CVBASESERVICE_H 6 | #define QT_OBJECT_MEASURE_CVBASESERVICE_H 7 | 8 | #include "common/PropertyHelpers.h" 9 | #include 10 | 11 | class CvBaseService : public QObject { 12 | Q_OBJECT 13 | 14 | AUTO_PROPERTY(QString, name, getName, setName, nameChanged) 15 | AUTO_PROPERTY(QString, repoLink, getRepoLink, setRepoLink, repoLinkChanged) 16 | AUTO_PROPERTY(QString, iconSource, getIconSource, setIconSource, iconSourceChanged) 17 | AUTO_PROPERTY(bool, isActive, getIsActive, setIsActive, isActiveChanged); 18 | 19 | public: 20 | explicit CvBaseService(QString name); 21 | 22 | ~CvBaseService() override = default; 23 | 24 | virtual void processImage(const cv::Mat &image) = 0; 25 | 26 | }; 27 | 28 | 29 | #endif //QT_OBJECT_MEASURE_CVBASESERVICE_H 30 | -------------------------------------------------------------------------------- /src/service/GrayService.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #include "GrayService.h" 6 | 7 | GrayService::GrayService() : CvBaseService("GrayService") {} 8 | 9 | void GrayService::processImage(const cv::Mat &image) { 10 | 11 | cv::Mat gray; 12 | 13 | cvtColor(image, gray, cv::COLOR_BGR2GRAY); 14 | cvtColor(gray, image, cv::COLOR_GRAY2BGR); 15 | 16 | 17 | } 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/service/GrayService.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_GRAYSERVICE_H 6 | #define QT_OBJECT_MEASURE_GRAYSERVICE_H 7 | 8 | #include "CvBaseService.h" 9 | 10 | class GrayService : public CvBaseService { 11 | Q_OBJECT 12 | 13 | public: 14 | explicit GrayService(); 15 | 16 | ~GrayService() override = default; 17 | 18 | void processImage(const cv::Mat &image) override; 19 | }; 20 | 21 | 22 | #endif //QT_OBJECT_MEASURE_GRAYSERVICE_H 23 | -------------------------------------------------------------------------------- /src/service/MeasureA4Service.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/8. 3 | */ 4 | 5 | #include "MeasureA4Service.h" 6 | #include "util/CvPointUtil.h" 7 | 8 | #include 9 | 10 | MeasureA4Service::MeasureA4Service() : CvBaseService("MeasureA4Service") { 11 | 12 | scale = 1; 13 | refWidth = 297 * scale; 14 | refHeight = 210 * scale; 15 | 16 | } 17 | 18 | std::vector 19 | MeasureA4Service::getContours(const cv::Mat &image, std::pair threshold, int minArea = 1000, 20 | int apexFilter = 0, 21 | bool isCircular = false, bool isDraw = false) const { 22 | 23 | cv::Mat gray; 24 | 25 | //convert it to grayscale, and blur it slightly 26 | cvtColor(image, gray, cv::COLOR_BGR2GRAY); 27 | GaussianBlur(gray, gray, cv::Size(5, 5), 1); 28 | 29 | 30 | // find circles 31 | if (isCircular) { 32 | 33 | auto violet = cv::Scalar(238, 130, 238); 34 | auto darkRed = cv::Scalar(220, 20, 60); 35 | std::vector circleList; 36 | HoughCircles(gray, circleList, cv::HOUGH_GRADIENT, 1, 50, 100, 50, 10, 150); 37 | 38 | for (auto &circle : circleList) { 39 | cv::Point center(cvRound(circle[0]), cvRound(circle[1])); 40 | int radius = cvRound(circle[2]); 41 | cv::circle(image, center, 5, violet, -1, 8, 0); 42 | cv::circle(image, center, radius, violet, 3, 8, 0); 43 | 44 | std::vector midPoint; 45 | midPoint.emplace_back(center.x + radius, center.y); 46 | midPoint.emplace_back(center.x - radius, center.y); 47 | midPoint.emplace_back(center.x, center.y - radius); 48 | midPoint.emplace_back(center.x, center.y + radius); 49 | 50 | line(image, midPoint[0], midPoint[1], violet, 2); 51 | line(image, midPoint[2], midPoint[3], violet, 2); 52 | 53 | 54 | //float refer = (((float) refHeight / (float) image.rows) + ((float) refWidth / (float) image.cols)) * 0.5f; 55 | float refer = ((float) refHeight / (float) image.rows); 56 | float r = (float) radius * refer / 10.0f; 57 | 58 | center.x += radius + 10; 59 | center.y += radius + 10; 60 | 61 | cv::putText(image, cv::format("R = %.2f cm", r), center, 62 | cv::FONT_HERSHEY_COMPLEX, 0.8, darkRed); 63 | } 64 | } 65 | 66 | 67 | // perform edge detection, then perform a dilation + erosion to 68 | // close gaps in between object edges 69 | Canny(gray, gray, threshold.first, threshold.second); 70 | 71 | cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(5, 5)); 72 | dilate(gray, gray, element); 73 | erode(gray, gray, element); 74 | 75 | // find contours in the edge map 76 | std::vector> contours; 77 | std::vector hierarchy; 78 | findContours(gray, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); 79 | 80 | std::vector finalContourList; 81 | 82 | for (const auto &c : contours) { 83 | 84 | double area = contourArea(c); 85 | if (area > minArea) { 86 | 87 | // get the area and apex from contours 88 | std::vector approx; 89 | approxPolyDP(c, approx, 0.02 * arcLength(c, true), true); 90 | 91 | if (apexFilter > 0) { 92 | if (approx.size() == apexFilter) { 93 | finalContourList.emplace_back(c, area, approx); 94 | } 95 | } else { 96 | finalContourList.emplace_back(c, area, approx); 97 | } 98 | } 99 | } 100 | std::sort(finalContourList.begin(), finalContourList.end(), [](const ContourInfo &c1, const ContourInfo &c2) { 101 | return c1.area > c2.area; 102 | }); 103 | 104 | 105 | if (isDraw && !finalContourList.empty()) { 106 | std::vector> tempContourList; 107 | for (const auto &c :finalContourList) 108 | tempContourList.emplace_back(c.finalContours); 109 | 110 | drawContours(image, tempContourList, -1, cv::Scalar(0, 0, 255), 3); 111 | } 112 | 113 | return finalContourList; 114 | } 115 | 116 | void 117 | MeasureA4Service::warpImage(const cv::Mat &image, std::vector &pointList, int width, int height, 118 | float partRatio) { 119 | 120 | reorderRectangleApex(pointList); 121 | 122 | std::vector point2fList; 123 | for (auto &p : pointList) 124 | point2fList.emplace_back(p.x, p.y); 125 | 126 | std::vector tempList; 127 | tempList.emplace_back(0, 0); 128 | tempList.emplace_back(width, 0); 129 | tempList.emplace_back(0, height); 130 | tempList.emplace_back(width, height); 131 | 132 | auto matrix = getPerspectiveTransform(point2fList, tempList); 133 | 134 | cv::Mat warp; 135 | warpPerspective(image, warp, matrix, cv::Size(width, height)); 136 | 137 | auto col = (float) warp.cols; 138 | auto row = (float) warp.rows; 139 | 140 | cv::Rect rect(col * (1.0f - partRatio) * 0.5f, row * (1.0f - partRatio) * 0.5f, col * partRatio, row * partRatio); 141 | cv::Mat part = cv::Mat(warp, rect); 142 | 143 | cv::resize(part, warp, image.size()); 144 | warp.copyTo(image); 145 | } 146 | 147 | void MeasureA4Service::reorderRectangleApex(std::vector &pointList) { 148 | 149 | // Index 0 and 3 are determined by sum of x and y 150 | std::sort(pointList.begin(), pointList.end(), [](const cv::Point &p1, const cv::Point &p2) { 151 | return (p1.x + p1.y) < (p2.x + p2.y); 152 | }); 153 | // Index 1 and 2 are determined by division of x and y 154 | std::sort(pointList.begin() + 1, pointList.end() - 1, [](const cv::Point &p1, const cv::Point &p2) { 155 | return ((float) p1.x / (float) p1.y) > ((float) p2.x / (float) p2.y); 156 | }); 157 | } 158 | 159 | void MeasureA4Service::processImage(const cv::Mat &image) { 160 | 161 | // get A4 paper 162 | auto contours = getContours(image, std::pair(100, 100), 50000, 4); 163 | if (contours.empty()) 164 | return; 165 | auto biggest = contours[0].apex; 166 | warpImage(image, biggest, refWidth, refHeight, 0.96f); 167 | 168 | // measure rectangle 169 | contours = getContours(image, std::pair(50, 50), 2000, 4, true); 170 | if (contours.empty()) 171 | return; 172 | 173 | for (auto c:contours) { 174 | 175 | auto violet = cv::Scalar(238, 130, 238); 176 | auto darkRed = cv::Scalar(220, 20, 60); 177 | 178 | cv::polylines(image, c.apex, true, violet, 3); 179 | reorderRectangleApex(c.apex); 180 | 181 | 182 | std::vector midPoint; 183 | midPoint.push_back(CvPointUtil::createMidPoint(c.apex[0], c.apex[1])); 184 | midPoint.push_back(CvPointUtil::createMidPoint(c.apex[1], c.apex[3])); 185 | midPoint.push_back(CvPointUtil::createMidPoint(c.apex[2], c.apex[3])); 186 | midPoint.push_back(CvPointUtil::createMidPoint(c.apex[0], c.apex[2])); 187 | for (const auto &p:midPoint) 188 | circle(image, p, 4, violet); 189 | 190 | line(image, midPoint[0], midPoint[2], violet, 2); 191 | line(image, midPoint[1], midPoint[3], violet, 2); 192 | 193 | float dWidth = 194 | CvPointUtil::getDistance(midPoint[1], midPoint[3]) * (float) refWidth / (float) image.cols / 10.0f; 195 | float dHeight = 196 | CvPointUtil::getDistance(midPoint[0], midPoint[2]) * (float) refHeight / (float) image.rows / 10.0f; 197 | 198 | midPoint[0].x -= 30; 199 | midPoint[0].y -= 30; 200 | midPoint[3].x -= 100; 201 | 202 | cv::putText(image, cv::format("%.2f cm", dWidth), midPoint[0], 203 | cv::FONT_HERSHEY_COMPLEX, 0.8, darkRed); 204 | cv::putText(image, cv::format("%.2f cm", dHeight), midPoint[3], 205 | cv::FONT_HERSHEY_COMPLEX, 0.8, darkRed); 206 | 207 | 208 | } 209 | 210 | } -------------------------------------------------------------------------------- /src/service/MeasureA4Service.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_MEASUREA4SERVICE_H 6 | #define QT_OBJECT_MEASURE_MEASUREA4SERVICE_H 7 | 8 | #include 9 | 10 | #include "CvBaseService.h" 11 | #include "entity/ContourInfo.h" 12 | 13 | class MeasureA4Service : public CvBaseService { 14 | Q_OBJECT 15 | AUTO_PROPERTY(int, refWidth, getRefWidth, setRefWidth, refWidthChanged) 16 | AUTO_PROPERTY(int, refHeight, getRefHeight, setRefHeight, refHeightChanged) 17 | AUTO_PROPERTY(int, scale, getScale, setScale, scaleChanged) 18 | private: 19 | 20 | public: 21 | explicit MeasureA4Service(); 22 | 23 | ~MeasureA4Service() override = default; 24 | 25 | void processImage(const cv::Mat &image) override; 26 | 27 | std::vector getContours(const cv::Mat &image, 28 | std::pair threshold, int minArea, int apexFilter, 29 | bool isCircular, bool isDraw) const; 30 | 31 | static void 32 | warpImage(const cv::Mat &image, std::vector &pointList, int width, int height, float partRatio); 33 | 34 | static void reorderRectangleApex(std::vector &pointList); 35 | 36 | }; 37 | 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /src/service/MeasureCoinService.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | #include "MeasureCoinService.h" 5 | 6 | #include "util/CvPointUtil.h" 7 | 8 | MeasureCoinService::MeasureCoinService() : CvBaseService("MeasureCoinService") { 9 | 10 | pixelsPerMetric = 1.0f; 11 | refer = 25.0f; 12 | 13 | } 14 | 15 | 16 | void MeasureCoinService::processImage(const cv::Mat &image) { 17 | 18 | 19 | cv::Mat gray; 20 | 21 | //convert it to grayscale, and blur it slightly 22 | cvtColor(image, gray, cv::COLOR_BGR2GRAY); 23 | GaussianBlur(gray, gray, cv::Size(7, 7), 0); 24 | 25 | // find circles 26 | std::vector circleList; 27 | HoughCircles(gray, circleList, cv::HOUGH_GRADIENT, 1, 40, 100, 70, 40, 150); 28 | for (auto & circle : circleList) 29 | { 30 | cv::Point center(cvRound(circle[0]), cvRound(circle[1])); 31 | int radius = cvRound(circle[2]); 32 | cv::circle(image, center, 5, cv::Scalar(0, 255, 0), -1, 8, 0); 33 | cv::circle(image, center, radius, cv::Scalar(155, 50, 255), 3, 8, 0); 34 | } 35 | 36 | // perform edge detection, then perform a dilation + erosion to 37 | // close gaps in between object edges 38 | Canny(gray, gray, 50, 100); 39 | 40 | cv::Mat element = getStructuringElement(cv::MORPH_RECT, cv::Size(15, 15)); //隔开物体 41 | dilate(gray, gray, element); 42 | erode(gray, gray, element); 43 | 44 | // find contours in the edge map 45 | std::vector> contours; 46 | std::vector hierarchy; 47 | findContours(gray, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE); 48 | std::sort(contours.begin(), contours.end(), [](std::vector c1, std::vector c2) { 49 | return c1[0].x < c2[0].x; 50 | }); 51 | 52 | 53 | 54 | // loop over the contours individually 55 | bool isRefer = false; 56 | 57 | for (const auto &c:contours) { 58 | 59 | //if the contour is not sufficiently large, ignore it 60 | if (contourArea(c) < 500) continue; 61 | 62 | // compute the rotated bounding box of the contour 63 | cv::RotatedRect box = minAreaRect(c); 64 | cv::Point2f boxPoints[4]; 65 | box.points(boxPoints); 66 | 67 | cv::Point2f pointA = CvPointUtil::createMidPoint(boxPoints[0], boxPoints[1]); 68 | cv::Point2f pointB = CvPointUtil::createMidPoint(boxPoints[1], boxPoints[2]); 69 | cv::Point2f pointC = CvPointUtil::createMidPoint(boxPoints[2], boxPoints[3]); 70 | cv::Point2f pointD = CvPointUtil::createMidPoint(boxPoints[3], boxPoints[0]); 71 | 72 | circle(image, pointA, 2, cv::Scalar(0, 0, 255)); 73 | circle(image, pointB, 2, cv::Scalar(0, 0, 255)); 74 | circle(image, pointC, 2, cv::Scalar(0, 0, 255)); 75 | circle(image, pointD, 2, cv::Scalar(0, 0, 255)); 76 | 77 | line(image, pointA, pointC, cv::Scalar(255, 0, 0),2); 78 | line(image, pointD, pointB, cv::Scalar(255, 0, 0),2); 79 | 80 | double dWidth = CvPointUtil::getDistance(pointA, pointC); 81 | double dHeight = CvPointUtil::getDistance(pointD, pointB); 82 | 83 | // get pixelsPerMetric by the smallest object 84 | if(!isRefer){ 85 | pixelsPerMetric = dWidth / refer; 86 | isRefer = true; 87 | } 88 | 89 | // filter 90 | if(dHeight / pixelsPerMetric <=2.5 || dWidth / pixelsPerMetric<=2.5) 91 | continue; 92 | 93 | // classify round or rectangle 94 | if (abs(dWidth - dHeight) < 2 * pixelsPerMetric ) 95 | { 96 | putText(image, cv::format("R=%.0f mm", dWidth / pixelsPerMetric), boxPoints[1], 97 | cv::FONT_HERSHEY_COMPLEX, 0.8, cv::Scalar(0, 0, 0)); 98 | 99 | }else{ 100 | 101 | pointA.x = pointA.x - 15; 102 | pointA.y = pointA.y - 10; 103 | pointB.x = pointB.x + 10; 104 | 105 | cv::putText(image, cv::format("%.0f mm", dHeight / pixelsPerMetric), pointA, 106 | cv::FONT_HERSHEY_COMPLEX, 0.8,cv::Scalar(0, 0, 0)); 107 | cv::putText(image, cv::format("%.0f mm", dWidth / pixelsPerMetric), pointB, 108 | cv::FONT_HERSHEY_COMPLEX, 0.8,cv::Scalar(0, 0, 0)); 109 | 110 | } 111 | 112 | for (int i = 0; i <= 3; i++) { 113 | line(image, boxPoints[i], boxPoints[(i + 1) % 4], cv::Scalar(0, 255, 0),2); 114 | } 115 | 116 | 117 | 118 | 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/service/MeasureCoinService.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_MEASURECOINSERVICE_H 6 | #define QT_OBJECT_MEASURE_MEASURECOINSERVICE_H 7 | 8 | #include "CvBaseService.h" 9 | 10 | class MeasureCoinService : public CvBaseService { 11 | Q_OBJECT 12 | 13 | AUTO_PROPERTY(double, pixelsPerMetric, getPixelsPerMetric, setPixelsPerMetric, pixelsPerMetricChanged); 14 | AUTO_PROPERTY(double, refer, getRefer, setRefer, referChanged); 15 | 16 | 17 | private: 18 | 19 | 20 | public: 21 | explicit MeasureCoinService(); 22 | 23 | ~MeasureCoinService() override = default; 24 | 25 | void processImage(const cv::Mat &image) override; 26 | 27 | }; 28 | 29 | #endif //QT_OBJECT_MEASURE_MEASURECOINSERVICE_H 30 | -------------------------------------------------------------------------------- /src/service/UnDistortService.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | #include "UnDistortService.h" 5 | 6 | #include "util/CvImageUtil.h" 7 | 8 | UnDistortService::UnDistortService() : CvBaseService("UnDistortService") { 9 | 10 | } 11 | 12 | void UnDistortService::processImage(const cv::Mat &image) { 13 | 14 | if (cameraMatrix.empty() || distCoefficients.empty()) { 15 | qDebug() << "Didn't calibrate the camera"; 16 | return; 17 | } 18 | 19 | cv::undistort(image.clone(), image, cameraMatrix, distCoefficients); 20 | 21 | } 22 | 23 | void UnDistortService::calibrate() { 24 | 25 | if (imagePathSet.empty()) return; 26 | 27 | QVector imageList; 28 | 29 | 30 | for (const auto &path : imagePathSet) { 31 | 32 | cv::Mat src = CvImageUtil::loadCvImage(path); 33 | if (src.empty()) continue; 34 | imageList.append(src); 35 | 36 | } 37 | 38 | cv::Size imageSize; 39 | cv::Size patternSize = cv::Size(4, 8); 40 | 41 | std::vector> cornerList; 42 | 43 | for (const auto &src : imageList) { 44 | 45 | if (imageSize.width == 0) { 46 | imageSize.width = src.cols; 47 | imageSize.height = src.rows; 48 | } 49 | 50 | std::vector cornerBuf; 51 | 52 | if (!findChessboardCorners(src, patternSize, cornerBuf)) { 53 | qDebug() << "can not find chessboard corners!"; 54 | return; 55 | } 56 | 57 | cv::Mat gray; 58 | cv::cvtColor(src, gray, cv::COLOR_RGB2GRAY); 59 | 60 | cv::find4QuadCornerSubpix(gray, cornerBuf, cv::Size(5, 5)); 61 | cornerList.push_back(cornerBuf); 62 | 63 | //cv::drawChessboardCorners(gray, patternSize, cornerBuf, true); 64 | //cv::imshow("corner", gray); 65 | } 66 | 67 | std::vector cornerBuf; 68 | std::vector> objectPointList; 69 | 70 | for (int i = 0; i < patternSize.height; i++) 71 | for (int j = 0; j < patternSize.width; j++) 72 | cornerBuf.emplace_back(j, i , 0); 73 | 74 | for (int i = 0; i < imageList.length(); ++i) 75 | objectPointList.push_back(cornerBuf); 76 | 77 | cameraMatrix = cv::Mat(3, 3, CV_32FC1, cv::Scalar::all(0)); 78 | distCoefficients = cv::Mat(1, 5, CV_32FC1, cv::Scalar::all(0)); 79 | std::vector tvecsMat; 80 | std::vector rvecsMat; 81 | 82 | 83 | cv::calibrateCamera(objectPointList, cornerList, imageSize, 84 | cameraMatrix, distCoefficients, 85 | rvecsMat, tvecsMat, 0); 86 | 87 | std::cout << cameraMatrix << std::endl; 88 | std::cout << distCoefficients << std::endl; 89 | 90 | } 91 | 92 | void UnDistortService::insertImagePath(const QString &path) { 93 | 94 | if (path.isNull()) 95 | return; 96 | 97 | if (!path.contains("file:///")) { 98 | imagePathSet.insert("file:///" + path); 99 | return; 100 | } 101 | 102 | imagePathSet.insert(path); 103 | 104 | } 105 | 106 | void UnDistortService::deleteImagePath(const QString &path) { 107 | 108 | imagePathSet.remove(path); 109 | 110 | } 111 | 112 | bool UnDistortService::isExistImagePath(const QString &path) { 113 | 114 | return imagePathSet.contains(path); 115 | 116 | } 117 | 118 | void UnDistortService::clearImagePath() { 119 | 120 | imagePathSet.clear(); 121 | 122 | } 123 | 124 | QVariantList UnDistortService::getImagePath() { 125 | 126 | QVariantList list; 127 | 128 | for (const auto &path :imagePathSet) 129 | list.append(path); 130 | 131 | return list; 132 | } 133 | -------------------------------------------------------------------------------- /src/service/UnDistortService.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/6. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_UNDISTORTSERVICE_H 6 | #define QT_OBJECT_MEASURE_UNDISTORTSERVICE_H 7 | 8 | #include "CvBaseService.h" 9 | 10 | class UnDistortService : public CvBaseService { 11 | Q_OBJECT 12 | 13 | AUTO_PROPERTY(bool, cornerRow, getCornerRow, setCornerRow, cornerRowChanged); 14 | AUTO_PROPERTY(bool, cornerCol, getCornerCol, setCornerCol, cornerColChanged); 15 | 16 | private: 17 | cv::Mat cameraMatrix{}; 18 | cv::Mat distCoefficients{}; 19 | 20 | QSet imagePathSet; 21 | 22 | public: 23 | explicit UnDistortService(); 24 | 25 | ~UnDistortService() override = default; 26 | 27 | void processImage(const cv::Mat &image) override; 28 | 29 | Q_INVOKABLE void calibrate(); 30 | Q_INVOKABLE void insertImagePath(const QString& path); 31 | Q_INVOKABLE void deleteImagePath(const QString& path); 32 | Q_INVOKABLE bool isExistImagePath(const QString& path); 33 | Q_INVOKABLE void clearImagePath(); 34 | Q_INVOKABLE QVariantList getImagePath(); 35 | }; 36 | 37 | 38 | 39 | #endif //QT_OBJECT_MEASURE_UNDISTORTSERVICE_H 40 | -------------------------------------------------------------------------------- /src/service/WipeOffService.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/8. 3 | */ 4 | #include "WipeOffService.h" 5 | WipeOffService::WipeOffService() : CvBaseService("WipeOffService") { 6 | 7 | //setIsActive(true); 8 | 9 | } 10 | /** 11 | * remove the watermark from DroidCam 12 | * @param image 13 | */ 14 | void WipeOffService::processImage(const cv::Mat &image){ 15 | 16 | auto col = (float)image.cols; 17 | auto row = (float)image.rows; 18 | 19 | cv::Rect rect(col*0.03f, row*0.03f, col*0.97f, row*0.97f); 20 | cv::Mat part = cv::Mat(image, rect); 21 | 22 | cv::Mat temp; 23 | cv::resize(part,temp,cv::Size(col,row)); 24 | temp.copyTo(image); 25 | } -------------------------------------------------------------------------------- /src/service/WipeOffService.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/8. 3 | */ 4 | #include "CvBaseService.h" 5 | 6 | #ifndef QT_OBJECT_MEASURE_WIPEOFFSERVICE_H 7 | #define QT_OBJECT_MEASURE_WIPEOFFSERVICE_H 8 | 9 | 10 | class WipeOffService : public CvBaseService{ 11 | 12 | public: 13 | explicit WipeOffService(); 14 | 15 | ~WipeOffService() override = default; 16 | 17 | void processImage(const cv::Mat &image) override; 18 | 19 | }; 20 | 21 | 22 | #endif //QT_OBJECT_MEASURE_WIPEOFFSERVICE_H 23 | -------------------------------------------------------------------------------- /src/system/FileHandler.cpp: -------------------------------------------------------------------------------- 1 | #include "FileHandler.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "ImageProvider.h" 10 | 11 | bool FileHandler::validateUrl(const QUrl &url) 12 | { 13 | if (url.isEmpty()) { 14 | qDebug()< (1024 * 1024 * 1024)) 93 | return QString::asprintf("%.2fGB", byte / (1024.0 * 1024.0 * 1024.0)); 94 | else if (byte > (1024 * 1024)) 95 | return QString::asprintf("%.2fMB", byte / (1024.0 * 1024.0)); 96 | else if (byte > (1024)) 97 | return QString::asprintf("%.2fKB", byte / (1024.0)); 98 | else return QString::asprintf("%d bytes", int(byte)); 99 | } -------------------------------------------------------------------------------- /src/system/FileHandler.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/1. 3 | */ 4 | 5 | #pragma once 6 | 7 | #include 8 | #include 9 | 10 | class FileHandler : public QObject 11 | { 12 | Q_OBJECT 13 | public: 14 | explicit FileHandler()= default; 15 | 16 | Q_INVOKABLE static bool validateUrl(const QUrl &url) ; 17 | Q_INVOKABLE static QString getCurrentPath() ; 18 | Q_INVOKABLE static QString cdUp(const QString& dir) ; 19 | Q_INVOKABLE static QString convertPathToName(const QString& dir); 20 | Q_INVOKABLE static QString convertPathToType(const QString& dir); 21 | Q_INVOKABLE static bool isImage(const QString& dir); 22 | 23 | Q_INVOKABLE static QString fileToIcon(const QUrl &url); 24 | Q_INVOKABLE static QString imageToIcon(const QUrl &url); 25 | Q_INVOKABLE static QString fileName(const QUrl &url); 26 | Q_INVOKABLE static QString convertByte(int byte); 27 | 28 | signals: 29 | 30 | }; 31 | -------------------------------------------------------------------------------- /src/system/HttpManager.cpp: -------------------------------------------------------------------------------- 1 | #include "HttpManager.h" 2 | #include 3 | #include "controller/MediaController.h" 4 | 5 | HttpManager::HttpManager(QObject *parent) : 6 | QObject(parent) { 7 | 8 | //Check ssl support 9 | // qDebug() << "SupportsSSL: " << QSslSocket::supportsSsl(); 10 | // qDebug() << QSslSocket::sslLibraryBuildVersionString(); 11 | // qDebug() << QSslSocket::sslLibraryVersionString(); 12 | 13 | } 14 | 15 | void HttpManager::processNetworkImage(const QString &url) { 16 | if (currentProcessedUrl == url) { 17 | //Image cached, no need to download 18 | if (networkImage != nullptr) { 19 | //FrameworkManager::getInstance().processImageByFrameworks(*networkImage); 20 | } 21 | } else { 22 | currentProcessedUrl = url; 23 | networkImage = nullptr; 24 | 25 | auto *networkAccessManager = new QNetworkAccessManager(this); 26 | connect(networkAccessManager, SIGNAL(finished(QNetworkReply * )), this, 27 | SLOT(processImageLinkAnswer(QNetworkReply * ))); 28 | connect(networkAccessManager, &QNetworkAccessManager::finished, networkAccessManager, 29 | &QNetworkAccessManager::deleteLater); 30 | networkAccessManager->get(QNetworkRequest{QUrl(url)}); 31 | } 32 | } 33 | 34 | void HttpManager::processImageLinkAnswer(QNetworkReply *reply) { 35 | QImage img; 36 | img.load(reply, nullptr); 37 | img = img.convertToFormat(QImage::Format_RGB888); 38 | img = img.scaledToHeight(MediaController::getInstance().getImageHeigth()); 39 | 40 | setNetworkImage(new QImage{img}); 41 | //FrameworkManager::getInstance().processImageByFrameworks(*networkImage); 42 | } -------------------------------------------------------------------------------- /src/system/HttpManager.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "common/PropertyHelpers.h" 10 | #include "model/ObjectListModel.h" 11 | 12 | class HttpManager : public QObject { 13 | Q_OBJECT 14 | 15 | QString currentProcessedUrl; 16 | 17 | AUTO_PROPERTY(QImage*, networkImage, getNetworkImage, setNetworkImage, networkImageChanged); 18 | 19 | public: 20 | explicit HttpManager(QObject *parent = nullptr); 21 | 22 | Q_INVOKABLE void processNetworkImage(const QString &url); 23 | 24 | private slots: 25 | 26 | void processImageLinkAnswer(QNetworkReply *reply); 27 | }; 28 | -------------------------------------------------------------------------------- /src/system/ImageProvider.cpp: -------------------------------------------------------------------------------- 1 | #include "ImageProvider.h" 2 | 3 | #include 4 | 5 | ImageProvider &ImageProvider::getInstance() 6 | { 7 | static ImageProvider imageProvider; 8 | return imageProvider; 9 | } 10 | 11 | QString ImageProvider::push(const QImage &src) { 12 | 13 | QString key = QUuid::createUuid().toString(). 14 | replace("{", ""). 15 | replace("}", ""). 16 | replace("-", ""); 17 | 18 | imageMap.insert(key,src); 19 | return key; 20 | } 21 | 22 | QImage ImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) { 23 | 24 | return imageMap[id]; 25 | 26 | } 27 | 28 | QPixmap ImageProvider::requestPixmap(const QString &id, QSize *size, const QSize &requestedSize) { 29 | 30 | return QPixmap::fromImage(imageMap[id]); 31 | 32 | } 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/system/ImageProvider.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | class ImageProvider : public QObject, public QQuickImageProvider 8 | { 9 | Q_OBJECT 10 | private: 11 | QHash imageMap; 12 | private: 13 | ImageProvider(): QQuickImageProvider(QQuickImageProvider::Image){} 14 | ~ImageProvider() override = default; 15 | public: 16 | static ImageProvider &getInstance(); 17 | 18 | QString push(const QImage &src); 19 | 20 | QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; 21 | QPixmap requestPixmap(const QString &id, QSize *size, const QSize& requestedSize) override; 22 | 23 | }; 24 | -------------------------------------------------------------------------------- /src/util/CvImageUtil.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2021/1/7. 3 | */ 4 | #include "CvImageUtil.h" 5 | 6 | #include 7 | 8 | 9 | cv::Mat CvImageUtil::loadCvImage(QString path) { 10 | 11 | path.replace("file:///", ""); 12 | 13 | QImage image(path); 14 | if (image.isNull()) { 15 | qDebug() << "Failed to open picture"; 16 | return cv::Mat(100, 100, CV_8UC1, cv::Scalar::all(0)); 17 | } 18 | 19 | return qImage2CvMat(image); 20 | } 21 | 22 | cv::Mat CvImageUtil::qImage2CvMat(const QImage &image) { 23 | switch (image.format()) { 24 | // 8-bit, 4 channel 25 | case QImage::Format_ARGB32: 26 | break; 27 | case QImage::Format_ARGB32_Premultiplied: { 28 | cv::Mat mat(image.height(), image.width(), 29 | CV_8UC4, 30 | (void *) image.constBits(), 31 | image.bytesPerLine()); 32 | return mat.clone(); 33 | } 34 | 35 | // 8-bit, 3 channel 36 | case QImage::Format_RGB32: { 37 | cv::Mat mat(image.height(), image.width(), 38 | CV_8UC4, 39 | (void *) image.constBits(), 40 | image.bytesPerLine()); 41 | 42 | // drop the all-white alpha channel 43 | cv::cvtColor(mat, mat, cv::COLOR_BGRA2BGR); 44 | return mat.clone(); 45 | } 46 | case QImage::Format_RGB888: { 47 | QImage swapped = image.rgbSwapped(); 48 | cv::Mat mat(swapped.height(), swapped.width(), 49 | CV_8UC3, 50 | (void *) image.constBits(), 51 | image.bytesPerLine()); 52 | return mat.clone(); 53 | } 54 | 55 | // 8-bit, 1 channel 56 | case QImage::Format_Indexed8: { 57 | cv::Mat mat(image.height(), image.width(), 58 | CV_8UC1, 59 | (void *) image.constBits(), 60 | image.bytesPerLine()); 61 | return mat.clone(); 62 | } 63 | 64 | // wrong 65 | default: 66 | qDebug() << "ERROR: QImage could not be converted to Mat."; 67 | break; 68 | } 69 | return cv::Mat(); 70 | } -------------------------------------------------------------------------------- /src/util/CvImageUtil.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2021/1/7. 3 | */ 4 | 5 | #ifndef QT_OBJECT_MEASURE_CVIMAGEUTIL_H 6 | #define QT_OBJECT_MEASURE_CVIMAGEUTIL_H 7 | 8 | #include 9 | 10 | #include 11 | 12 | class CvImageUtil { 13 | 14 | public: 15 | 16 | static cv::Mat loadCvImage(QString path); 17 | static cv::Mat qImage2CvMat(const QImage &image); 18 | 19 | }; 20 | 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /src/util/CvLineUtil.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/8. 3 | */ 4 | 5 | #include "CvLineUtil.h" 6 | 7 | void CvLineUtil::drawVirtualLine(cv::Mat img, const cv::Point2f& p1, const cv::Point2f& p2, cv::Scalar color, int thickness) { 8 | float n = 5; 9 | float w = p2.x - p1.x, h = p2.y - p1.y; 10 | float l = sqrtf(w * w + h * h); 11 | 12 | int m = l / n; 13 | m = m % 2 ? m : m + 1; 14 | n = l / m; 15 | 16 | circle(img, p1, 1, color, thickness); 17 | circle(img, p2, 1, color, thickness); 18 | // 画中间点 19 | if (p1.y == p2.y) 20 | { 21 | float x1 = fmin(p1.x, p2.x); 22 | float x2 = fmax(p1.x, p2.x); 23 | for (float x = x1, n1 = 2 * n; x < x2; x = x + n1) 24 | line(img, cv::Point2f(x, p1.y), cv::Point2f(x + n, p1.y), color, thickness); 25 | } 26 | else if (p1.x == p2.x) 27 | { 28 | //垂直线, x = m 29 | float y1 = fmin(p1.y, p2.y); 30 | float y2 = fmax(p1.y, p2.y); 31 | for (float y = y1, n1 = 2 * n; y < y2; y = y + n1) 32 | line(img, cv::Point2f(p1.x, y), cv::Point2f(p1.x, y + n), color, thickness); 33 | } 34 | else 35 | { 36 | // 直线方程的两点式:(y-y1)/(y2-y1)=(x-x1)/(x2-x1) -> y = (y2-y1)*(x-x1)/(x2-x1)+y1 37 | float n1 = n * abs(w) / l; 38 | float k = h / w; 39 | float x1 = fmin(p1.x, p2.x); 40 | float x2 = fmax(p1.x, p2.x); 41 | for (float x = x1, n2 = 2 * n1; x < x2; x = x + n2) 42 | { 43 | cv::Point p3 = cv::Point2f(x, k * (x - p1.x) + p1.y); 44 | cv::Point p4 = cv::Point2f(x + n1, k * (x + n1 - p1.x) + p1.y); 45 | line(img, p3, p4, color, thickness); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /src/util/CvLineUtil.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Alberta on 2022/1/9. 3 | // 4 | 5 | #ifndef QT_OBJECT_MEASURE_CVLINEUTIL_H 6 | #define QT_OBJECT_MEASURE_CVLINEUTIL_H 7 | 8 | #include 9 | 10 | 11 | class CvLineUtil { 12 | 13 | public: 14 | static void drawVirtualLine(cv::Mat img, const cv::Point2f& p1, const cv::Point2f& p2, cv::Scalar color, int thickness); 15 | 16 | }; 17 | 18 | 19 | #endif //QT_OBJECT_MEASURE_CVLINEUTIL_H 20 | -------------------------------------------------------------------------------- /src/util/CvPointUtil.cpp: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/8. 3 | */ 4 | 5 | 6 | #include "CvPointUtil.h" 7 | cv::Point2f CvPointUtil::createMidPoint(cv::Point2f &pointA, cv::Point2f &pointB) { 8 | 9 | cv::Point2f point; 10 | point.x = (pointA.x + pointB.x) * 0.5; 11 | point.y = (pointA.y + pointB.y) * 0.5; 12 | return point; 13 | 14 | } 15 | 16 | float CvPointUtil::getDistance(const cv::Point2f &pointA, const cv::Point2f &pointB) { 17 | 18 | float distance; 19 | distance = powf((pointA.x - pointB.x), 2) + powf((pointA.y - pointB.y), 2); 20 | distance = sqrtf(distance); 21 | return distance; 22 | 23 | } 24 | 25 | cv::Point CvPointUtil::createMidPoint(cv::Point &pointA, cv::Point &pointB) { 26 | 27 | cv::Point point; 28 | point.x = (pointA.x + pointB.x) /2; 29 | point.y = (pointA.y + pointB.y) /2; 30 | return point; 31 | 32 | } 33 | 34 | double CvPointUtil::getDistance(const cv::Point &pointA, const cv::Point &pointB) { 35 | 36 | double distance; 37 | distance = pow((pointA.x - pointB.x), 2) + pow((pointA.y - pointB.y), 2); 38 | distance = sqrt(distance); 39 | return distance; 40 | 41 | } -------------------------------------------------------------------------------- /src/util/CvPointUtil.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jiajun Chen on 2022/1/9. 3 | */ 4 | #ifndef QT_OBJECT_MEASURE_CVPOINTUTIL_H 5 | #define QT_OBJECT_MEASURE_CVPOINTUTIL_H 6 | 7 | #include 8 | 9 | class CvPointUtil { 10 | 11 | public: 12 | 13 | static float getDistance(const cv::Point2f &pointA, const cv::Point2f &pointB); 14 | 15 | static cv::Point2f createMidPoint(cv::Point2f &pointA, cv::Point2f &pointB); 16 | 17 | static double getDistance(const cv::Point &pointA, const cv::Point &pointB); 18 | 19 | static cv::Point createMidPoint(cv::Point &pointA, cv::Point &pointB); 20 | 21 | }; 22 | 23 | 24 | #endif //QT_OBJECT_MEASURE_CVPOINTUTIL_H 25 | -------------------------------------------------------------------------------- /src/views/main.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Window 2.12 3 | import QtQuick.Controls 2.2 4 | import QtMultimedia 5.12 5 | import QtQuick.Controls.Material 2.12 6 | import QtGraphicalEffects 1.12 7 | 8 | 9 | import "qrc:/views/screen" as Screens 10 | import "qrc:/views/widget" as Widgets 11 | 12 | import "qrc:/views/screen/MediaScreen" as MediaScreen 13 | import "qrc:/views/screen/ServiceScreen" as ServiceScreen 14 | 15 | 16 | ApplicationWindow 17 | { 18 | id: application 19 | 20 | property color detectionRectanglesColor: "white" 21 | 22 | visible: true 23 | property double screenFactor: 0.7 24 | width: Screen.width * screenFactor 25 | height: Screen.height * screenFactor 26 | title: qsTr("Qt Object Measure") 27 | 28 | 29 | Timer { 30 | id: loadImageTimer 31 | property var url; 32 | interval: 1000; running: false; repeat: true 33 | onTriggered: { 34 | httpManager.processNetworkImage(url) 35 | } 36 | } 37 | 38 | MediaScreen.MediaOutputPane{ 39 | id: mainMediaPanel 40 | visible: false 41 | anchors{ 42 | top: parent.top 43 | left: parent.left 44 | } 45 | height: parent.height 46 | width: parent.width 47 | } 48 | 49 | Component { 50 | id: mediaScreen 51 | MediaScreen.MediaScreen{ 52 | anchors.margins: 5 53 | } 54 | } 55 | 56 | Component { 57 | id: serviceScreen 58 | ServiceScreen.ServiceScreen{ 59 | anchors.margins: 5 60 | } 61 | } 62 | 63 | StackView { 64 | id: stackView 65 | anchors.fill: parent 66 | initialItem: mediaScreen 67 | } 68 | 69 | footer: 70 | Widgets.MenuTapBar{ 71 | id: footer 72 | visible: true 73 | } 74 | } -------------------------------------------------------------------------------- /src/views/screen/MediaScreen/MediaOutputPane.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.2 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | 9 | import "qrc:/views/widget" as Widgets 10 | 11 | import online.qom.component 1.0 12 | 13 | Item { 14 | id: rootPanel 15 | 16 | property var viewfinder: mediaPlayer 17 | property var mediaUrl: "" 18 | property var mediaSource: "camera" 19 | property var imageSaveUrl: "" 20 | property bool framevorkView: false 21 | property var urlMediaStatePlay 22 | 23 | state: "small" 24 | width: parent.width 25 | height: parent.height 26 | 27 | onMediaSourceChanged: { 28 | } 29 | 30 | function getCurrentVideoOutput(){ 31 | return mediaSource 32 | } 33 | 34 | 35 | function play(){ 36 | 37 | switch(mediaSource) 38 | { 39 | 40 | case "image": 41 | mediaPlayer.openPicture(mediaUrl) 42 | //cameraButton.visible = false 43 | mediaControls.visible = false 44 | break; 45 | case "link": 46 | mediaPlayer.openWebPicture(mediaUrl) 47 | //cameraButton.visible = false 48 | mediaControls.visible = false 49 | break; 50 | case "video": 51 | mediaPlayer.openVideo(mediaUrl) 52 | //cameraButton.visible = false 53 | mediaControls.visible = true 54 | break; 55 | case "camera": 56 | mediaPlayer.openCamera() 57 | //cameraButton.visible = true 58 | mediaControls.visible = false 59 | break; 60 | 61 | case "rstp": 62 | mediaPlayer.openWebCam(mediaUrl) 63 | cameraButton.visible = true 64 | mediaControls.visible = false 65 | break; 66 | } 67 | } 68 | 69 | function stop(){ 70 | mediaPlayer.stop() 71 | } 72 | 73 | CvMediaPlayer{ 74 | 75 | id: mediaPlayer 76 | anchors.fill: parent 77 | } 78 | 79 | SequentialAnimation { 80 | id: seqanimation 81 | running: false 82 | loops: 1 83 | NumberAnimation { 84 | target: mediaPlayer 85 | property: "opacity" 86 | duration: 150 87 | to: 0.2 88 | easing.type: Easing.InOutQuad 89 | } 90 | 91 | NumberAnimation { 92 | target: mediaPlayer 93 | property: "opacity" 94 | duration: 150 95 | to: 1.0 96 | easing.type: Easing.InOutQuad 97 | } 98 | } 99 | 100 | Image{ 101 | id: cameraButton 102 | anchors{ 103 | verticalCenter: rootPanel.verticalCenter 104 | right: rootPanel.right 105 | margins: 15 106 | } 107 | 108 | sourceSize.width: 40 109 | sourceSize.height: 40 110 | visible: mediaSource == "rstp" || mediaSource == "camera" 111 | 112 | source: "qrc:/assets/image/camera.png" 113 | MouseArea{ 114 | anchors.fill: parent 115 | onClicked: 116 | { 117 | if(imageSaveUrl=="") 118 | imageSaveUrl = fileHandler.getCurrentPath() 119 | var name = mediaPlayer.savePicture(imageSaveUrl) 120 | cvFrameController.getServiceObjectByName("UnDistortService").insertImagePath(imageSaveUrl+"/"+ name); 121 | seqanimation.start(); 122 | } 123 | } 124 | } 125 | 126 | 127 | anchors{ 128 | top: parent.top 129 | } 130 | 131 | Image{ 132 | id: fulscreenButton 133 | anchors{ 134 | top: rootPanel.top 135 | right: rootPanel.right 136 | margins: 10 137 | } 138 | 139 | sourceSize.width: 50 140 | sourceSize.height: 50 141 | visible: (rootPanel.state == "fullscreen" || rootPanel.state == "small" ) 142 | 143 | source: "qrc:/assets/image/round_fullscreen_white_48dp.png" 144 | MouseArea{ 145 | anchors.fill: parent 146 | onClicked: 147 | { 148 | 149 | switch (rootPanel.state){ 150 | 151 | case "fullscreen": 152 | fulscreenButton.state = "small" 153 | rootPanel.state = "small" 154 | break; 155 | case "small": 156 | rootPanel.state = "fullscreen" 157 | fulscreenButton.state = "fullscreen" 158 | break; 159 | 160 | } 161 | } 162 | } 163 | 164 | states: [ 165 | State { 166 | name: "small" 167 | PropertyChanges { 168 | target: fulscreenButton 169 | source: "qrc:/assets/image/round_fullscreen_white_48dp.png" 170 | } 171 | }, 172 | State { 173 | name: "fullscreen" 174 | PropertyChanges { 175 | target: fulscreenButton 176 | source: "qrc:/assets/image/round_close_fullscreen_white_48dp.png" 177 | } 178 | } 179 | ] 180 | } 181 | 182 | Label{ 183 | text: "FPS: " + mediaPlayer.fps.toFixed(2) 184 | color: "white" 185 | anchors{ 186 | top: fulscreenButton.bottom 187 | right: rootPanel.right 188 | margins: 10 189 | } 190 | } 191 | 192 | Rectangle{ 193 | radius: 4 194 | color: "white" 195 | opacity: 0.5 196 | height: 40 197 | id: mediaControls 198 | visible: false 199 | anchors{ 200 | bottom: parent.bottom 201 | left: parent.left 202 | right: parent.right 203 | margins: 10 204 | } 205 | 206 | Image { 207 | width:height 208 | states: [ 209 | State { 210 | name: "play" 211 | PropertyChanges { 212 | target: playButton 213 | source: "qrc:/assets/image/pause.png" 214 | } 215 | }, 216 | State { 217 | name: "stop" 218 | PropertyChanges { 219 | target: playButton 220 | source: "qrc:/assets/image/stop.png" 221 | } 222 | }, 223 | State { 224 | name: "pause" 225 | PropertyChanges { 226 | target: playButton 227 | source: "qrc:/assets/image/play.png" 228 | } 229 | } 230 | ] 231 | id: playButton 232 | state: "play" 233 | source: "qrc:/assets/image/play.png" 234 | anchors{ 235 | top: parent.top 236 | left: parent.left 237 | bottom: parent.bottom 238 | margins: 5 239 | } 240 | 241 | MouseArea{ 242 | anchors.fill:parent 243 | onClicked: { 244 | 245 | switch(playButton.state){ 246 | case "play": 247 | mediaPlayer.pause() 248 | playButton.state = "pause" 249 | break; 250 | case "stop": 251 | mediaPlayer.play() 252 | playButton.state = "stop" 253 | break; 254 | case "pause": 255 | mediaPlayer.play() 256 | playButton.state = "play" 257 | break; 258 | } 259 | } 260 | } 261 | } 262 | 263 | Label{ 264 | id: durationLabel 265 | text: { 266 | return mediaController.convertVideoTimeToTimeString(mediaPlayer.position) 267 | +" / "+ 268 | mediaController.convertVideoTimeToTimeString(mediaPlayer.duration) 269 | } 270 | anchors{ 271 | left: playButton.right 272 | verticalCenter: parent.verticalCenter 273 | margins: 5 274 | } 275 | onTextChanged: { 276 | durationSLider.changeValue(mediaPlayer.position) 277 | } 278 | } 279 | 280 | Slider{ 281 | anchors{ 282 | top: parent.top 283 | left: durationLabel.right 284 | right: parent.right 285 | bottom: parent.bottom 286 | margins: 5 287 | } 288 | 289 | function changeValue(value){ 290 | if(value!= durationSLider.value) 291 | durationSLider.value = value; 292 | } 293 | 294 | onValueChanged: mediaPlayer.seek(value) 295 | 296 | id: durationSLider 297 | from: 0 298 | to: mediaPlayer.duration 299 | } 300 | } 301 | 302 | Behavior on height { 303 | NumberAnimation{duration: 300} 304 | } 305 | states: [ 306 | State { 307 | name: "fullscreen" 308 | PropertyChanges { 309 | target: rootPanel 310 | height: application.height 311 | width: application.width 312 | 313 | } 314 | }, 315 | State { 316 | name: "small" 317 | 318 | PropertyChanges { 319 | target: rootPanel 320 | height: application.height/2 321 | width: application.width/2 322 | } 323 | }, 324 | State { 325 | name: "tile" 326 | 327 | PropertyChanges { 328 | target: rootPanel 329 | height: application.height/8 330 | width: application.width/8 331 | } 332 | }, 333 | State { 334 | name: "hidden" 335 | 336 | PropertyChanges { 337 | target: rootPanel 338 | height: 0 339 | width: 0 340 | } 341 | } 342 | ] 343 | } 344 | -------------------------------------------------------------------------------- /src/views/screen/MediaScreen/MediaScreen.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.2 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | 9 | import "qrc:/views/widget" as Widgets 10 | 11 | Item { 12 | id: cameraScreenItem 13 | 14 | Component.onCompleted: { 15 | footer.visible = true 16 | mainMediaPanel.visible = true 17 | mainMediaPanel.state = "small" 18 | } 19 | 20 | Rectangle { 21 | id: background 22 | anchors.fill: parent 23 | color: "#F4DCD6" //pastel orange 24 | RadialGradient { 25 | anchors.fill: parent 26 | gradient: Gradient { 27 | GradientStop { position: 0.0; color: "white" } 28 | GradientStop { position: 0.5; color: "transparent" } 29 | } 30 | } 31 | } 32 | 33 | Item 34 | { 35 | id: mediaPanel 36 | anchors{ 37 | top: parent.top 38 | left: parent.left 39 | } 40 | height: mainMediaPanel.height 41 | width: mainMediaPanel.width 42 | OpacityMask { 43 | anchors.fill: mediaPanel 44 | source: mainMediaPanel 45 | maskSource: background 46 | } 47 | } 48 | 49 | VideoInputSelectPane{ 50 | id: cameraSelectorWidget 51 | anchors{ 52 | top: parent.top 53 | bottom: parent.bottom 54 | right: parent.right 55 | left: mediaPanel.right 56 | 57 | leftMargin: 5 58 | bottomMargin: 5 59 | } 60 | } 61 | 62 | VideoInputSettingsPane{ 63 | id: videoPropertiesPanel 64 | anchors{ 65 | top: mediaPanel.bottom 66 | left: parent.left 67 | right: mediaPanel.right 68 | bottom: parent.bottom 69 | 70 | topMargin: 5 71 | bottomMargin: 5 72 | } 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/views/screen/MediaScreen/VideoInputSelectPane.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.2 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | import QtQuick.Controls.Styles 1.4 9 | 10 | 11 | import "qrc:/views/widget" as Widgets 12 | Item { 13 | Widgets.ColorHelper{id: colorHelper} 14 | Pane{ 15 | id: root 16 | anchors.fill: parent 17 | 18 | Material.elevation: 6 19 | Material.background: Material.White 20 | 21 | function setMediaButtonState(playing){ 22 | if(playing===false){ 23 | mediaStateButton.icon.source = "qrc:/assets/image/play.png" 24 | mediaStateButton.text = qsTr("Play media") 25 | mainMediaPanel.stop() 26 | 27 | }else{ 28 | mediaStateButton.icon.source = "qrc:/assets/image/stop.png" 29 | mediaStateButton.text = qsTr("Stop media") 30 | mainMediaPanel.play() 31 | } 32 | } 33 | 34 | ColumnLayout{ 35 | ButtonGroup { id: mediaSelectorRadioButtons } 36 | 37 | ColumnLayout { 38 | RowLayout{ 39 | opacity: 0.5 40 | Image { 41 | sourceSize.width: 40 42 | source: "qrc:/assets/image/round_computer_black_48dp.png" 43 | } 44 | Label{ 45 | text: "LOCAL MEDIA" 46 | } 47 | } 48 | 49 | Widgets.SeparatorLine{} 50 | 51 | RowLayout{ 52 | Layout.leftMargin: 5 53 | Image { 54 | opacity: 0.5 55 | sourceSize.width: 30 56 | source: "qrc:/assets/image/round_insert_photo_black_48dp.png" 57 | MouseArea{ 58 | anchors.fill: parent 59 | onClicked: videoMediaSelectionRadiobutton_localImage.checked = !videoMediaSelectionRadiobutton_localImage.checked 60 | } 61 | } 62 | RadioButton { 63 | id: videoMediaSelectionRadiobutton_localImage 64 | ButtonGroup.group: mediaSelectorRadioButtons 65 | text: qsTr("Image") 66 | checked: mainMediaPanel.mediaSource === "image" 67 | onCheckedChanged: { 68 | if(checked){ 69 | //loadImageTimer.start() 70 | mediaStateButton.visible = false; 71 | lastSeparatorLine.visible = false; 72 | } 73 | else{ 74 | //loadImageTimer.stop() 75 | mediaStateButton.visible = true; 76 | lastSeparatorLine.visible = true; 77 | } 78 | } 79 | 80 | onClicked: { 81 | root.setMediaButtonState(false) 82 | 83 | mainMediaPanel.mediaSource = "image" 84 | mainMediaPanel.mediaUrl = imageLoader.fileText 85 | loadImageTimer.url = imageLoader.fileText 86 | mainMediaPanel.play() 87 | } 88 | } 89 | Widgets.FileLoader{ 90 | id: imageLoader 91 | labelText: "" 92 | Layout.fillWidth: true 93 | fileText: fileHandler.getCurrentPath() 94 | nameFilters: ["*.*"] 95 | function onLoaded(){ 96 | fileText=file 97 | } 98 | function onEnterPressedFnc(){ 99 | videoMediaSelectionRadiobutton_localImage.clicked() 100 | root.setMediaButtonState(true) 101 | } 102 | onFileChoosen: onLoaded 103 | onEnterPressed: onEnterPressedFnc 104 | } 105 | } 106 | 107 | RowLayout{ 108 | Layout.leftMargin: 5 109 | Image { 110 | opacity: 0.5 111 | sourceSize.width: 30 112 | source: "qrc:/assets/image/round_local_movies_black_48dp.png" 113 | MouseArea{ 114 | anchors.fill: parent 115 | onClicked: videoMediaSelectionRadiobutton_localVideo.checked = !videoMediaSelectionRadiobutton_localVideo.checked 116 | } 117 | } 118 | RadioButton { 119 | id: videoMediaSelectionRadiobutton_localVideo 120 | ButtonGroup.group: mediaSelectorRadioButtons 121 | text: qsTr("Video ") 122 | checked: mainMediaPanel.mediaSource === "video" 123 | onClicked: { 124 | root.setMediaButtonState(false) 125 | mainMediaPanel.mediaSource = "video" 126 | mainMediaPanel.mediaUrl = videoLoader.fileText 127 | } 128 | } 129 | Widgets.FileLoader{ 130 | id: videoLoader 131 | labelText: "" 132 | fileText: fileHandler.getCurrentPath() 133 | Layout.fillWidth: true 134 | nameFilters: ["*.*"] 135 | function onLoaded(){ 136 | fileText=file 137 | } 138 | function onEnterPressedFnc(){ 139 | videoMediaSelectionRadiobutton_localVideo.clicked() 140 | root.setMediaButtonState(true) 141 | } 142 | onFileChoosen: onLoaded 143 | onEnterPressed: onEnterPressedFnc 144 | } 145 | } 146 | RowLayout{ 147 | Layout.leftMargin: 5 148 | Image { 149 | opacity: 0.5 150 | sourceSize.width: 30 151 | source: "qrc:/assets/image/camera_black.png" 152 | MouseArea{ 153 | anchors.fill: parent 154 | onClicked: videoMediaSelectionRadioButton_Camera.checked = !videoMediaSelectionRadioButton_Camera.checked 155 | } 156 | } 157 | RadioButton { 158 | id: videoMediaSelectionRadioButton_Camera 159 | ButtonGroup.group: mediaSelectorRadioButtons 160 | text: qsTr("Device camera") 161 | checked: mainMediaPanel.mediaSource === "camera" 162 | onClicked: { 163 | root.setMediaButtonState(false) 164 | mainMediaPanel.mediaSource = "camera" 165 | } 166 | } 167 | ComboBox{ 168 | Material.accent: Material.Orange 169 | Material.foreground: Material.BlueGrey 170 | model: ListModel{ 171 | ListElement { 172 | displayName: "USB2.0 HD UVC WebCam" 173 | deviceId: 0 174 | } 175 | } 176 | textRole: "displayName" 177 | onActivated:{} 178 | } 179 | } 180 | 181 | 182 | RowLayout{ 183 | Layout.topMargin: 20 184 | opacity: 0.5 185 | Image { 186 | sourceSize.width: 50 187 | source: "qrc:/assets/image/cloud_black.png" 188 | } 189 | Label{ 190 | text: "REMOTE MEDIA" 191 | } 192 | } 193 | 194 | Widgets.SeparatorLine{} 195 | 196 | RowLayout{ 197 | Layout.leftMargin: 5 198 | Image { 199 | opacity: 0.5 200 | sourceSize.width: 30 201 | source: "qrc:/assets/image/round_link_black_48dp.png" 202 | MouseArea{ 203 | anchors.fill: parent 204 | onClicked: videoMediaSelectionRadiobutton_urlUrl.checked = !videoMediaSelectionRadiobutton_urlUrl.checked 205 | } 206 | } 207 | RadioButton { 208 | id: videoMediaSelectionRadiobutton_urlUrl 209 | ButtonGroup.group: mediaSelectorRadioButtons 210 | text: qsTr("Link") 211 | checked: mainMediaPanel.mediaSource === "link" 212 | onCheckedChanged: { 213 | if(checked){ 214 | loadImageTimer.start() 215 | mediaStateButton.visible = false; 216 | lastSeparatorLine.visible = false; 217 | } 218 | else{ 219 | loadImageTimer.stop() 220 | mediaStateButton.visible = true; 221 | lastSeparatorLine.visible = true; 222 | } 223 | } 224 | 225 | onClicked: { 226 | root.setMediaButtonState(false) 227 | 228 | mainMediaPanel.mediaSource = "link" 229 | mainMediaPanel.mediaUrl = urlLoader.fileText 230 | loadImageTimer.url = urlLoader.fileText 231 | 232 | mainMediaPanel.play() 233 | } 234 | } 235 | Widgets.FileLoader{ 236 | id: urlLoader 237 | labelText: "" 238 | Layout.fillWidth: true 239 | fileText: "https://mymodernmet.com/wp/wp-content/uploads/2019/09/100000-ai-faces-thumbnail.jpg" 240 | nameFilters: ["*.*"] 241 | moreButtonVisibility: false 242 | function onLoaded(){ 243 | fileText = file 244 | } 245 | function onEnterPressedFnc(){ 246 | videoMediaSelectionRadiobutton_urlUrl.clicked() 247 | root.setMediaButtonState(true) 248 | } 249 | onFileChoosen: onLoaded 250 | onEnterPressed: onEnterPressedFnc 251 | } 252 | } 253 | 254 | 255 | 256 | RowLayout{ 257 | Layout.leftMargin: 5 258 | Image { 259 | opacity: 0.5 260 | sourceSize.width: 30 261 | source: "qrc:/assets/image/cctv.png" 262 | MouseArea{ 263 | anchors.fill: parent 264 | onClicked: videoMediaSelectionRadiobutton_RSTP.checked = !videoMediaSelectionRadiobutton_RSTP.checked 265 | } 266 | } 267 | RadioButton { 268 | id: videoMediaSelectionRadiobutton_RSTP 269 | ButtonGroup.group: mediaSelectorRadioButtons 270 | text: qsTr("Rtsp stream") 271 | checked: mainMediaPanel.mediaSource === "rstp" 272 | onClicked: { 273 | root.setMediaButtonState(false) 274 | mainMediaPanel.mediaSource = "rstp" 275 | mainMediaPanel.mediaUrl = te.text 276 | } 277 | } 278 | TextEdit{ 279 | id: te 280 | clip: true 281 | Layout.fillWidth: true 282 | font.pixelSize: 16 283 | focus: videoMediaSelectionRadiobutton_RSTP.checked 284 | //text: "rtsp://admin:admin@192.168.31.115:8554/live" 285 | text: "http://192.168.31.115:4747/video" 286 | 287 | Rectangle{ 288 | opacity: 0.2 289 | anchors.fill: parent 290 | border.color: "gray" 291 | color: "lightgray" 292 | } 293 | Keys.onEnterPressed: { 294 | videoMediaSelectionRadiobutton_RSTP.clicked() 295 | root.setMediaButtonState(true) 296 | } 297 | } 298 | } 299 | 300 | 301 | 302 | 303 | Item{ 304 | Layout.fillHeight: true 305 | } 306 | 307 | Widgets.SeparatorLine{ 308 | id: lastSeparatorLine 309 | } 310 | ToolButton{ 311 | visible: true 312 | 313 | id: mediaStateButton 314 | Layout.alignment: Qt.AlignCenter 315 | 316 | text: qsTr("Stop media") 317 | icon.source: "qrc:/assets/image/stop.png" 318 | font.family: "Helvetica" 319 | antialiasing: true 320 | Layout.fillWidth: true 321 | onClicked: { 322 | root.setMediaButtonState(text==="Play media"?true:false) 323 | } 324 | } 325 | } 326 | anchors.fill: parent 327 | } 328 | } 329 | } 330 | -------------------------------------------------------------------------------- /src/views/screen/MediaScreen/VideoInputSettingsPane.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | 9 | import "qrc:/views/widget" as Widgets 10 | Item { 11 | id: root 12 | property var mediaSource: mainMediaPanel.mediaSource 13 | 14 | onMediaSourceChanged: { 15 | switch(mediaSource) 16 | { 17 | case "camera": 18 | videoPropertiesLoader.sourceComponent = cameraComponent 19 | break; 20 | case "rstp": 21 | videoPropertiesLoader.sourceComponent = rstpComponent 22 | break; 23 | } 24 | } 25 | 26 | 27 | Pane{ 28 | anchors.fill: parent 29 | Material.elevation: 6 30 | Material.background: Material.White 31 | } 32 | Flickable{ 33 | clip: true 34 | anchors.fill: parent 35 | contentHeight: layout.implicitHeight 36 | ColumnLayout{ 37 | id: layout 38 | anchors.fill: parent 39 | anchors.margins: 5 40 | spacing: 5 41 | 42 | RowLayout{ 43 | Image { 44 | sourceSize.width: 40 45 | source: "qrc:/assets/image/media.png" 46 | } 47 | Text { 48 | font.pixelSize: 15 49 | text: qsTr( "Properties "+root.mediaSource) 50 | opacity: 0.6 51 | } 52 | } 53 | 54 | Widgets.SeparatorLine{} 55 | 56 | Loader{ 57 | id: videoPropertiesLoader 58 | sourceComponent: cameraComponent 59 | } 60 | 61 | Item{ 62 | Layout.fillHeight: true 63 | } 64 | 65 | Component{ 66 | id: cameraComponent 67 | ColumnLayout{ 68 | Item{ 69 | Layout.leftMargin: 5 70 | Label{ 71 | text:"Resolution: "+ mainMediaPanel.viewfinder.srcWidth + "x"+ mainMediaPanel.viewfinder.srcHeight 72 | } 73 | } 74 | } 75 | } 76 | 77 | Component{ 78 | id: rstpComponent 79 | ColumnLayout{ 80 | Item{ 81 | Layout.leftMargin: 5 82 | Label{ 83 | text:"RSTP properties" 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/views/screen/ServiceScreen/CommonSettings.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Controls.Styles 1.2 4 | import QtQuick.Layouts 1.3 5 | import QtQuick.Dialogs 1.0 6 | import QtMultimedia 5.12 7 | import QtGraphicalEffects 1.12 8 | import QtQuick.Controls.Material 2.12 9 | import QtQuick.Controls.Material.impl 2.12 10 | 11 | import "qrc:/views/widget" as Widgets 12 | Pane{ 13 | 14 | Component.onCompleted: { 15 | wipeOffCheckBox.checked = cvFrameController.getServiceObjectByName("WipeOffService").isActive 16 | } 17 | 18 | id: root 19 | property var serviceName: "WipeOffService" 20 | Material.elevation: 1 21 | Material.background: Material.White 22 | 23 | Layout.fillWidth: true 24 | 25 | ColumnLayout{ 26 | anchors.fill:parent 27 | Widgets.ExpandingSectionButton{ 28 | id: expandingSectionButton 29 | imageLogoSource: "qrc:/assets/image/settings_black.png" 30 | text: qsTr("General") 31 | } 32 | 33 | ColumnLayout{ 34 | Layout.leftMargin: 20 35 | spacing: 5 36 | id: settings 37 | visible: expandingSectionButton.visibility 38 | opacity: visible?1:0 39 | Behavior on opacity { 40 | NumberAnimation{duration: 500} 41 | } 42 | 43 | RowLayout{ 44 | Layout.fillWidth: true 45 | Label{ 46 | text: "Video rescale:" 47 | } 48 | ComboBox { 49 | id: displaySizeCombobox 50 | Layout.fillWidth: true 51 | model: ["Orginal", "4/3", "3/2", "Manual"] 52 | onCurrentTextChanged: { 53 | if(currentText === "Orginal") 54 | { 55 | //frameworkManager.detectionResolution = Qt.point(1, 1) 56 | }else if(currentText!== "Manual"){ 57 | resolutionSlider.value = resolutionSlider.value 58 | } 59 | } 60 | } 61 | } 62 | 63 | RowLayout{ 64 | visible: displaySizeCombobox.currentText === "4/3" || displaySizeCombobox.currentText === "3/2" 65 | opacity: visible?1:0 66 | Behavior on opacity { 67 | NumberAnimation{duration: 500} 68 | } 69 | 70 | Label{ 71 | text: "Resolution: "+ resolutionSlider.getXRes() + "x" + resolutionSlider.getYRes() 72 | } 73 | Slider{ 74 | function factorial(m,n){ 75 | return (n===0) || (n===1) ? m : factorial(m*2,n-1); 76 | } 77 | function getXRes(){return (displaySizeCombobox.currentText === "4/3"?40:30)*resolutionSlider.value.toFixed(0)} 78 | function getYRes(){return (displaySizeCombobox.currentText === "4/3"?30:20)*resolutionSlider.value.toFixed(0)} 79 | //default resolution is multiplication of 4:3 format aka 40:30 80 | 81 | id: resolutionSlider 82 | Layout.fillWidth: true 83 | value: 8 84 | from: 2 85 | to:24 86 | 87 | onValueChanged: { 88 | //frameworkManager.detectionResolution = Qt.point(getXRes(), getYRes()) 89 | } 90 | } 91 | } 92 | 93 | RowLayout{ 94 | visible: displaySizeCombobox.currentText === "Manual" 95 | opacity: visible?1:0 96 | Behavior on opacity { 97 | NumberAnimation{duration: 500} 98 | } 99 | Label{ 100 | text: " Width: " 101 | } 102 | TextEdit{ 103 | id: widthEditText 104 | text: "300" 105 | color: "gray" 106 | font.bold: true 107 | font.pixelSize: 15 108 | } 109 | Label{ 110 | text: ", Height: " 111 | } 112 | TextEdit{ 113 | id: heightEditText 114 | text: "300" 115 | color: "gray" 116 | font.bold: true 117 | font.pixelSize: 15 118 | } 119 | ToolButton{ 120 | id: applyMean 121 | text: qsTr("Apply resolution") 122 | font.pixelSize: 15 123 | antialiasing: true 124 | contentItem: 125 | 126 | RowLayout{ 127 | anchors.centerIn: parent 128 | Text { 129 | text: applyMean.text 130 | font: applyMean.font 131 | opacity: enabled ? 1.0 : 0.9 132 | color: "gray" 133 | horizontalAlignment: Text.AlignHCenter 134 | verticalAlignment: Text.AlignVCenter 135 | elide: Text.ElideRight 136 | } 137 | Image{ 138 | source: "qrc:/assets/image/round_check_black_48dp.png" 139 | sourceSize.height: 20 140 | sourceSize.width: 20 141 | opacity: 0.4 142 | } 143 | 144 | } 145 | onClicked: { 146 | //frameworkManager.detectionResolution = Qt.point(parseInt(widthEditText.text), parseInt(widthEditText.text)) 147 | } 148 | } 149 | } 150 | 151 | Widgets.SeparatorLine{} 152 | 153 | // Switch{ 154 | // id: detectionColoring 155 | // text: "Separate color for each detection class" 156 | // } 157 | 158 | RowLayout{ 159 | Layout.topMargin: 5 160 | Layout.bottomMargin: 5 161 | Layout.leftMargin: -10 162 | 163 | CheckBox{ 164 | id: wipeOffCheckBox 165 | checked: false 166 | text: "Wipe Off active" 167 | onCheckStateChanged: 168 | { 169 | cvFrameController.getServiceObjectByName(serviceName).isActive = checked 170 | } 171 | } 172 | Label{ 173 | text: "Detection rectangle color " 174 | } 175 | Rectangle{ 176 | id: colorRect 177 | border.color: "black" 178 | border.width: 1 179 | width:50 180 | height: 20 181 | color: "white" 182 | MouseArea{ 183 | anchors.fill: parent 184 | onClicked: colorDialog.visible = true 185 | } 186 | } 187 | 188 | ColorDialog { 189 | id: colorDialog 190 | title: "Detection rectangle color" 191 | onAccepted: { 192 | application.detectionRectanglesColor = colorDialog.color 193 | colorRect.color = colorDialog.color 194 | } 195 | } 196 | } 197 | 198 | Widgets.SeparatorLine{} 199 | 200 | RowLayout{ 201 | Layout.leftMargin: 0 202 | 203 | Widgets.FolderLoader{ 204 | id: imageSaveLoader 205 | Layout.fillWidth: true 206 | labelText: "Photo Folder:" 207 | folderText: fileHandler.getCurrentPath() 208 | function onLoaded(){ 209 | folderText = folder 210 | mainMediaPanel.imageSaveUrl = imageSaveLoader.folderText 211 | } 212 | onFolderChoosen: onLoaded 213 | 214 | } 215 | } 216 | 217 | } 218 | } 219 | } 220 | -------------------------------------------------------------------------------- /src/views/screen/ServiceScreen/OpenCV/CalibrateSettings.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | import QtQuick.Dialogs 1.3 9 | 10 | import "qrc:/views/widget" as Widgets 11 | 12 | Pane{ 13 | 14 | Component.onCompleted: { 15 | undistortCheckBox.checked = cvFrameController.getServiceObjectByName("UnDistortService").isActive 16 | } 17 | 18 | id: root 19 | property var serviceName: "UnDistortService" 20 | property var serviceObj: cvFrameController.getServiceObjectByName(root.serviceName) 21 | property var imageView: fileView 22 | Material.elevation: 1 23 | Material.background: Material.White 24 | Layout.fillWidth: true 25 | 26 | Widgets.ColorHelper{id: colorHelper} 27 | 28 | ColumnLayout{ 29 | anchors.fill:parent 30 | 31 | Widgets.ExpandingSectionButton{ 32 | id: expandingSectionButton 33 | imageLogoSource: serviceObj.iconSource 34 | text: "Calibration Settings" 35 | } 36 | 37 | 38 | ColumnLayout{ 39 | Layout.leftMargin: 20 40 | id: settings 41 | visible: expandingSectionButton.visibility 42 | opacity: visible?1:0 43 | Behavior on opacity { 44 | NumberAnimation{duration: 500} 45 | } 46 | 47 | RowLayout{ 48 | Layout.topMargin: 5 49 | Layout.leftMargin: -15 50 | ToolButton{ 51 | 52 | id: moreButton 53 | icon.source: "qrc:/assets/image/plus.png" 54 | Material.background: Material.BlueGrey 55 | onClicked: imageDialog.open() 56 | } 57 | Label{ 58 | Layout.alignment: Qt.AlignRight 59 | text: "Put in the picture to be calibrated (Can be dragged in)" 60 | } 61 | 62 | FileDialog{ 63 | id:imageDialog 64 | folder: "StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]" 65 | nameFilters: ["*.jpg","*.jpeg","*.png"] 66 | selectMultiple: true 67 | onAccepted: { 68 | for(var i = 0; i < fileUrls.length; i++) { 69 | fileView.addFile(fileUrls[i]); 70 | } 71 | } 72 | } 73 | 74 | } 75 | RowLayout{ 76 | Layout.topMargin: 0 77 | 78 | Rectangle { 79 | id: imageInputRectangle 80 | Layout.fillWidth: true 81 | height: 80 82 | radius: 1 83 | 84 | DropArea { 85 | anchors.fill: parent; 86 | onDropped: { 87 | if(drop.hasUrls) { 88 | for(var i = 0; i < drop.urls.length; i++) { 89 | console.log(drop.urls[i]); 90 | fileView.addFile(drop.urls[i]); 91 | } 92 | } 93 | } 94 | } 95 | Rectangle 96 | { 97 | width: parent.width 98 | height: parent.height 99 | id:rect 100 | anchors.centerIn: parent 101 | color:"#f7f7f7" 102 | } 103 | 104 | DropShadow 105 | { 106 | anchors.fill: rect 107 | radius: 8.0 108 | samples: 16 109 | color: "#a9a9a9" 110 | source: rect 111 | } 112 | 113 | Widgets.ImageLoaderView { 114 | id: fileView 115 | service: serviceObj 116 | anchors.fill: parent 117 | } 118 | } 119 | } 120 | 121 | 122 | Button{ 123 | id: calibrationButtion 124 | text: "Calibration" 125 | font.pixelSize: 10 126 | Layout.fillWidth: true 127 | onClicked: { 128 | //fileView.cleanUp(); 129 | serviceObj.calibrate() 130 | } 131 | Material.background: "white" 132 | } 133 | 134 | Widgets.SeparatorLine{} 135 | 136 | RowLayout{ 137 | Layout.leftMargin: -10 138 | CheckBox{ 139 | id: undistortCheckBox 140 | checked: false 141 | text: "Undistort active" 142 | onCheckStateChanged: 143 | { 144 | serviceObj.isActive = checked 145 | } 146 | } 147 | } 148 | } 149 | } 150 | } 151 | 152 | -------------------------------------------------------------------------------- /src/views/screen/ServiceScreen/OpenCV/MeasureSettings.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | import Qt.labs.platform 1.1 9 | 10 | import "qrc:/views/widget" as Widgets 11 | 12 | 13 | 14 | Pane{ 15 | 16 | Component.onCompleted: { 17 | measureCoinCheckBox.checked = cvFrameController.getServiceObjectByName("MeasureCoinService").isActive 18 | measureA4CheckBox.checked = cvFrameController.getServiceObjectByName("MeasureA4Service").isActive 19 | grayCheckBox.checked = cvFrameController.getServiceObjectByName("GrayService").isActive 20 | } 21 | 22 | id: root 23 | property var serviceName: "MeasureCoinService" 24 | property var serviceObj: cvFrameController.getServiceObjectByName(root.serviceName) 25 | Material.elevation: 1 26 | Material.background: Material.White 27 | Layout.fillWidth: true 28 | 29 | ColumnLayout{ 30 | anchors.fill:parent 31 | Widgets.ExpandingSectionButton{ 32 | id: expandingSectionButton 33 | imageLogoSource: serviceObj.iconSource 34 | text: "Detection Settings" 35 | } 36 | 37 | ColumnLayout{ 38 | spacing: 0 39 | Layout.leftMargin: 20 40 | visible: expandingSectionButton.visibility 41 | opacity: visible?1:0 42 | Behavior on opacity { 43 | NumberAnimation{duration: 500} 44 | } 45 | 46 | RowLayout{ 47 | Layout.leftMargin: -10 48 | CheckBox{ 49 | id: measureA4CheckBox 50 | checked: false 51 | text: "Measure by A4 paper active" 52 | onCheckStateChanged: 53 | { 54 | cvFrameController.getServiceObjectByName("MeasureA4Service").isActive = checked 55 | } 56 | } 57 | CheckBox{ 58 | id :grayCheckBox 59 | Layout.leftMargin: -10 60 | checked: false 61 | text: "Use gray scale conversion" 62 | onCheckStateChanged: 63 | { 64 | cvFrameController.getServiceObjectByName("GrayService").isActive = checked 65 | } 66 | } 67 | } 68 | RowLayout{ 69 | Layout.leftMargin: -10 70 | CheckBox{ 71 | id: measureCoinCheckBox 72 | checked: false 73 | text: "Measure by coin active" 74 | onCheckStateChanged: 75 | { 76 | serviceObj.isActive = checked 77 | } 78 | } 79 | 80 | } 81 | RowLayout{ 82 | Label{ 83 | text: "Confidence lvl: "+ confidenceSlider.value.toFixed(2) 84 | } 85 | Slider{ 86 | id: confidenceSlider 87 | Layout.fillWidth: true 88 | value: 0 89 | from: 0 90 | to: 5 91 | onValueChanged: { 92 | 93 | } 94 | } 95 | } 96 | } 97 | } 98 | } 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /src/views/screen/ServiceScreen/ServiceScreen.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | 9 | import "qrc:/views/widget" as Widgets 10 | 11 | import "qrc:/views/screen/ServiceScreen/OpenCV" as OpenCv 12 | 13 | Item { 14 | Component.onCompleted: { 15 | mainMediaPanel.state = "small" 16 | } 17 | 18 | Widgets.ColorHelper{id: colorHelper} 19 | Rectangle { 20 | id: background 21 | anchors.fill: parent 22 | color: colorHelper.darkPasteBlue 23 | RadialGradient { 24 | anchors.fill: parent 25 | gradient: Gradient { 26 | GradientStop { position: 0.0; color: "white" } 27 | GradientStop { position: 0.5; color: "transparent" } 28 | } 29 | } 30 | } 31 | 32 | Item 33 | { 34 | id: mediaPanel 35 | anchors{ 36 | top: parent.top 37 | left: parent.left 38 | } 39 | height: mainMediaPanel.height 40 | width: mainMediaPanel.width 41 | 42 | OpacityMask { 43 | anchors.fill: mediaPanel 44 | source: mainMediaPanel 45 | maskSource: background 46 | } 47 | } 48 | 49 | Pane{ 50 | id: configPane 51 | anchors{ 52 | left: mediaPanel.right 53 | top: parent.top 54 | right: parent.right 55 | bottom: parent.bottom 56 | //rightMargin: 5 57 | bottomMargin: 5 58 | leftMargin: 5 59 | } 60 | Material.elevation: 6 61 | Material.background: Material.White 62 | 63 | Flickable{ 64 | clip: true 65 | anchors.fill: parent 66 | contentHeight: settingsLayout.implicitHeight 67 | ColumnLayout{ 68 | id: settingsLayout 69 | anchors.fill: parent 70 | spacing: 0 71 | anchors.margins: 1 72 | 73 | CommonSettings{} 74 | 75 | Label{ 76 | Layout.topMargin: 30 77 | opacity: 0.4 78 | text: "CURRENT MEDIA PROCESSING:" 79 | } 80 | 81 | Component{ 82 | id: calibrateSettings 83 | OpenCv.CalibrateSettings{} 84 | } 85 | 86 | Loader{ 87 | Layout.fillWidth: true 88 | id: calibrateSettingsLoader 89 | sourceComponent: calibrateSettings 90 | } 91 | 92 | //Models 93 | Label{ 94 | Layout.topMargin: 30 95 | opacity: 0.4 96 | text: "AVAILABLE MEDIA PROCESSING:" 97 | } 98 | OpenCv.MeasureSettings{ 99 | visible: true 100 | opacity: visible?1:0 101 | Behavior on opacity { 102 | NumberAnimation{duration: 500} 103 | } 104 | } 105 | 106 | 107 | } 108 | } 109 | } 110 | 111 | Pane{ 112 | id: statsPane 113 | anchors{ 114 | left: parent.left 115 | top: mediaPanel.bottom 116 | right: mediaPanel.right 117 | bottom: parent.bottom 118 | bottomMargin: 5 119 | topMargin: 5 120 | } 121 | Material.elevation: 6 122 | Material.background: Material.White 123 | 124 | Flickable{ 125 | clip: true 126 | anchors.fill: parent 127 | contentHeight: settingsLayout.implicitHeight 128 | ColumnLayout{ 129 | id: statsLayout 130 | anchors.fill: parent 131 | RowLayout{ 132 | Image { 133 | sourceSize.width: 40 134 | source: "qrc:/assets/image/data.png" 135 | } 136 | Text { 137 | font.pixelSize: 15 138 | text: qsTr("Objects measurement statistics") 139 | opacity: 0.6 140 | } 141 | } 142 | 143 | 144 | Widgets.SeparatorLine{Layout.fillWidth: true} 145 | 146 | ColumnLayout{ 147 | spacing: 5 148 | Label{ 149 | id: detectedObjectsCount 150 | text: "Measured objects: " 151 | } 152 | Label{ 153 | text: "Measurement time : " + " ms" 154 | } 155 | } 156 | 157 | Item { 158 | Layout.fillHeight: true 159 | } 160 | } 161 | } 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /src/views/widget/ColorHelper.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | 3 | QtObject { 4 | property color darkPastelOrange: "#DFC7C1" 5 | property color lightPasteOrange: "#F4DCD6" 6 | property color lightestPasteBlue: "#B2D9EA" 7 | property color lightPasteBlue: "#84B4C8" 8 | property color darkPasteBlue: "#619196" 9 | } 10 | -------------------------------------------------------------------------------- /src/views/widget/ExpandingSectionButton.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | 9 | import "qrc:/views/widget" as Widgets 10 | 11 | RowLayout{ 12 | id: root 13 | 14 | property alias imageLogoSource: imageLogo.source 15 | property alias text : text.text 16 | property var visibility: false 17 | 18 | function onClick(){ 19 | visibility = !visibility; 20 | image.rotation = image.rotation===180?-90:180 21 | } 22 | 23 | Image{ 24 | id: image 25 | source: "qrc:/assets/image/left-arrow.png" 26 | sourceSize.width: 15 27 | rotation: 180 28 | Behavior on rotation{ 29 | NumberAnimation { duration: 50} 30 | } 31 | MouseArea{ 32 | anchors.fill: parent 33 | onClicked: root.onClick() 34 | } 35 | } 36 | 37 | Text { 38 | id: text 39 | font.pixelSize: 18 40 | clip: true 41 | color: "#7a7a7a" 42 | 43 | MouseArea{ 44 | anchors.fill: parent 45 | onClicked: root.onClick() 46 | } 47 | } 48 | 49 | Item{ 50 | Layout.fillWidth: true 51 | Layout.fillHeight: true 52 | MouseArea{ 53 | anchors.fill: parent 54 | onClicked: root.onClick() 55 | } 56 | } 57 | 58 | Image{ 59 | id: imageLogo 60 | sourceSize.width: 20 61 | Behavior on rotation{ 62 | NumberAnimation { duration: 50} 63 | } 64 | 65 | MouseArea{ 66 | anchors.fill: parent 67 | onClicked: root.onClick() 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/views/widget/FileLoader.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | import Qt.labs.platform 1.1 9 | 10 | 11 | 12 | RowLayout{ 13 | property alias fileText: fileEditText.text 14 | property alias labelText: label.text 15 | property alias file: fileDialog.file 16 | property alias nameFilters: fileDialog.nameFilters 17 | property alias moreButtonVisibility: moreButton.visible 18 | property var onFileChoosen 19 | property var onEnterPressed 20 | 21 | id: root 22 | spacing: 5 23 | Label{ 24 | id: label 25 | } 26 | 27 | TextInput{ 28 | height:moreButton.height 29 | id: fileEditText 30 | clip: true 31 | Layout.fillWidth: true 32 | font.pixelSize: 15 33 | 34 | Rectangle{ 35 | opacity: 0.2 36 | anchors.fill: parent 37 | border.color: "gray" 38 | color: "lightgray" 39 | } 40 | Keys.onEnterPressed: { 41 | root.onEnterPressed() 42 | } 43 | } 44 | ToolButton{ 45 | id: moreButton 46 | icon.source: "qrc:/assets/image/moreVert.png" 47 | Material.background: Material.BlueGrey 48 | onClicked: fileDialog.open() 49 | } 50 | 51 | FileDialog{ 52 | id:fileDialog 53 | folder: "StandardPaths.standardLocations(StandardPaths.PicturesLocation)[0]" 54 | onAccepted: root.onFileChoosen(file) 55 | } 56 | } 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /src/views/widget/FolderLoader.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.3 3 | import QtQuick.Layouts 1.3 4 | import QtMultimedia 5.12 5 | import QtGraphicalEffects 1.12 6 | import QtQuick.Controls.Material 2.12 7 | import QtQuick.Controls.Material.impl 2.12 8 | import Qt.labs.platform 1.1 9 | 10 | 11 | 12 | RowLayout{ 13 | property alias folderText: folderEditText.text 14 | property alias labelText: label.text 15 | property alias folder: folderDialog.folder 16 | property var onFolderChoosen 17 | id: root 18 | spacing: 5 19 | Label{ 20 | id: label 21 | } 22 | 23 | TextInput{ 24 | height:moreButton.height 25 | id: folderEditText 26 | clip: true 27 | Layout.fillWidth: true 28 | font.pixelSize: 15 29 | 30 | Rectangle{ 31 | anchors{ 32 | topMargin: -5 33 | bottomMargin: -5 34 | } 35 | 36 | opacity: 0.2 37 | anchors.fill: parent 38 | border.color: "gray" 39 | color: "lightgray" 40 | 41 | } 42 | } 43 | ToolButton{ 44 | id: moreButton 45 | icon.source: "qrc:/assets/image/moreVert.png" 46 | Material.background: Material.BlueGrey 47 | onClicked: folderDialog.open() 48 | } 49 | 50 | FolderDialog{ 51 | id:folderDialog 52 | onAccepted: root.onFolderChoosen(folder) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/views/widget/ImageLoaderView.qml: -------------------------------------------------------------------------------- 1 | import QtQuick 2.12 2 | import QtQuick.Controls 2.12 3 | import QtQml.Models 2.1 4 | 5 | Item { 6 | 7 | Component.onCompleted: { 8 | root.reload() 9 | } 10 | 11 | id: root 12 | 13 | property alias fileUrls: myModel 14 | property var service 15 | 16 | function reload(){ 17 | 18 | myModel.clear() 19 | var list = service.getImagePath() 20 | 21 | for(var i = 0;i