├── .gitignore
├── License
├── README.md
├── README_EN.md
├── docs
├── Download_and_usage.md
├── Download_and_usage_en.md
├── Keyboard_shortcuts.md
├── Keyboard_shortcuts_en.md
├── Usage_tutorial.md
└── Usage_tutorial_en.md
└── labcd
├── .editorconfig
├── CMakeLists.txt
├── LabCD.pri
├── LabCD.pro
├── LabCD.sln
├── LabCD.vcxproj
├── LabCD.vcxproj.filters
├── configs
└── colormap.txt
├── i18n
├── English.qm
└── English.ts
├── labcd.cpp
├── labcd.h
├── labcd.qrc
├── main.cpp
├── resources
├── AddLabel.png
├── CantDelete.png
├── Chinese.png
├── ClearMask.png
├── Color.png
├── Convert.png
├── Delete.png
├── DeleteAllPolygons.png
├── DeletePolygon.png
├── English.png
├── Enlarge.png
├── Folder.png
├── Full.png
├── Github.png
├── Help.png
├── Icon.png
├── Last.png
├── Merge.png
├── Narrow.png
├── Next.png
├── Reference.png
├── Save.png
└── Split.png
├── utils
├── colormap.cpp
├── colormap.h
├── fileworker.cpp
├── fileworker.h
├── imgpress.cpp
├── imgpress.h
├── label.cpp
└── label.h
└── widgets
├── annotationscence.cpp
├── annotationscence.h
├── annotationview.cpp
├── annotationview.h
├── canvas.cpp
├── canvas.h
├── filelist.cpp
├── filelist.h
├── labeltable.cpp
├── labeltable.h
├── labgrid.cpp
├── labgrid.h
├── labline.cpp
├── labline.h
├── labpolygon.cpp
├── labpolygon.h
├── multcanvas.cpp
├── multcanvas.h
└── opttypes.h
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 | ##
4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
5 |
6 | # User-specific files
7 | *.rsuser
8 | *.suo
9 | *.user
10 | *.userosscache
11 | *.sln.docstates
12 |
13 | # User-specific files (MonoDevelop/Xamarin Studio)
14 | *.userprefs
15 |
16 | # Mono auto generated files
17 | mono_crash.*
18 |
19 | # Build results
20 | [Dd]ebug/
21 | [Dd]ebugPublic/
22 | [Rr]elease/
23 | [Rr]eleases/
24 | x64/
25 | x86/
26 | [Ww][Ii][Nn]32/
27 | [Aa][Rr][Mm]/
28 | [Aa][Rr][Mm]64/
29 | bld/
30 | [Bb]in/
31 | [Oo]bj/
32 | [Oo]ut/
33 | [Ll]og/
34 | [Ll]ogs/
35 |
36 | # Visual Studio 2015/2017 cache/options directory
37 | .vs/
38 | # Uncomment if you have tasks that create the project's static files in wwwroot
39 | #wwwroot/
40 |
41 | # Visual Studio 2017 auto generated files
42 | Generated\ Files/
43 |
44 | # MSTest test Results
45 | [Tt]est[Rr]esult*/
46 | [Bb]uild[Ll]og.*
47 |
48 | # NUnit
49 | *.VisualState.xml
50 | TestResult.xml
51 | nunit-*.xml
52 |
53 | # Build Results of an ATL Project
54 | [Dd]ebugPS/
55 | [Rr]eleasePS/
56 | dlldata.c
57 |
58 | # Benchmark Results
59 | BenchmarkDotNet.Artifacts/
60 |
61 | # .NET Core
62 | project.lock.json
63 | project.fragment.lock.json
64 | artifacts/
65 |
66 | # ASP.NET Scaffolding
67 | ScaffoldingReadMe.txt
68 |
69 | # StyleCop
70 | StyleCopReport.xml
71 |
72 | # Files built by Visual Studio
73 | *_i.c
74 | *_p.c
75 | *_h.h
76 | *.ilk
77 | *.meta
78 | *.obj
79 | *.iobj
80 | *.pch
81 | *.pdb
82 | *.ipdb
83 | *.pgc
84 | *.pgd
85 | *.rsp
86 | *.sbr
87 | *.tlb
88 | *.tli
89 | *.tlh
90 | *.tmp
91 | *.tmp_proj
92 | *_wpftmp.csproj
93 | *.log
94 | *.vspscc
95 | *.vssscc
96 | .builds
97 | *.pidb
98 | *.svclog
99 | *.scc
100 |
101 | # Chutzpah Test files
102 | _Chutzpah*
103 |
104 | # Visual C++ cache files
105 | ipch/
106 | *.aps
107 | *.ncb
108 | *.opendb
109 | *.opensdf
110 | *.sdf
111 | *.cachefile
112 | *.VC.db
113 | *.VC.VC.opendb
114 |
115 | # Visual Studio profiler
116 | *.psess
117 | *.vsp
118 | *.vspx
119 | *.sap
120 |
121 | # Visual Studio Trace Files
122 | *.e2e
123 |
124 | # TFS 2012 Local Workspace
125 | $tf/
126 |
127 | # Guidance Automation Toolkit
128 | *.gpState
129 |
130 | # ReSharper is a .NET coding add-in
131 | _ReSharper*/
132 | *.[Rr]e[Ss]harper
133 | *.DotSettings.user
134 |
135 | # TeamCity is a build add-in
136 | _TeamCity*
137 |
138 | # DotCover is a Code Coverage Tool
139 | *.dotCover
140 |
141 | # AxoCover is a Code Coverage Tool
142 | .axoCover/*
143 | !.axoCover/settings.json
144 |
145 | # Coverlet is a free, cross platform Code Coverage Tool
146 | coverage*.json
147 | coverage*.xml
148 | coverage*.info
149 |
150 | # Visual Studio code coverage results
151 | *.coverage
152 | *.coveragexml
153 |
154 | # NCrunch
155 | _NCrunch_*
156 | .*crunch*.local.xml
157 | nCrunchTemp_*
158 |
159 | # MightyMoose
160 | *.mm.*
161 | AutoTest.Net/
162 |
163 | # Web workbench (sass)
164 | .sass-cache/
165 |
166 | # Installshield output folder
167 | [Ee]xpress/
168 |
169 | # DocProject is a documentation generator add-in
170 | DocProject/buildhelp/
171 | DocProject/Help/*.HxT
172 | DocProject/Help/*.HxC
173 | DocProject/Help/*.hhc
174 | DocProject/Help/*.hhk
175 | DocProject/Help/*.hhp
176 | DocProject/Help/Html2
177 | DocProject/Help/html
178 |
179 | # Click-Once directory
180 | publish/
181 |
182 | # Publish Web Output
183 | *.[Pp]ublish.xml
184 | *.azurePubxml
185 | # Note: Comment the next line if you want to checkin your web deploy settings,
186 | # but database connection strings (with potential passwords) will be unencrypted
187 | *.pubxml
188 | *.publishproj
189 |
190 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
191 | # checkin your Azure Web App publish settings, but sensitive information contained
192 | # in these scripts will be unencrypted
193 | PublishScripts/
194 |
195 | # NuGet Packages
196 | *.nupkg
197 | # NuGet Symbol Packages
198 | *.snupkg
199 | # The packages folder can be ignored because of Package Restore
200 | **/[Pp]ackages/*
201 | # except build/, which is used as an MSBuild target.
202 | !**/[Pp]ackages/build/
203 | # Uncomment if necessary however generally it will be regenerated when needed
204 | #!**/[Pp]ackages/repositories.config
205 | # NuGet v3's project.json files produces more ignorable files
206 | *.nuget.props
207 | *.nuget.targets
208 |
209 | # Microsoft Azure Build Output
210 | csx/
211 | *.build.csdef
212 |
213 | # Microsoft Azure Emulator
214 | ecf/
215 | rcf/
216 |
217 | # Windows Store app package directories and files
218 | AppPackages/
219 | BundleArtifacts/
220 | Package.StoreAssociation.xml
221 | _pkginfo.txt
222 | *.appx
223 | *.appxbundle
224 | *.appxupload
225 |
226 | # Visual Studio cache files
227 | # files ending in .cache can be ignored
228 | *.[Cc]ache
229 | # but keep track of directories ending in .cache
230 | !?*.[Cc]ache/
231 |
232 | # Others
233 | ClientBin/
234 | ~$*
235 | *~
236 | *.dbmdl
237 | *.dbproj.schemaview
238 | *.jfm
239 | *.pfx
240 | *.publishsettings
241 | orleans.codegen.cs
242 |
243 | # Including strong name files can present a security risk
244 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
245 | #*.snk
246 |
247 | # Since there are multiple workflows, uncomment next line to ignore bower_components
248 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
249 | #bower_components/
250 |
251 | # RIA/Silverlight projects
252 | Generated_Code/
253 |
254 | # Backup & report files from converting an old project file
255 | # to a newer Visual Studio version. Backup files are not needed,
256 | # because we have git ;-)
257 | _UpgradeReport_Files/
258 | Backup*/
259 | UpgradeLog*.XML
260 | UpgradeLog*.htm
261 | ServiceFabricBackup/
262 | *.rptproj.bak
263 |
264 | # SQL Server files
265 | *.mdf
266 | *.ldf
267 | *.ndf
268 |
269 | # Business Intelligence projects
270 | *.rdl.data
271 | *.bim.layout
272 | *.bim_*.settings
273 | *.rptproj.rsuser
274 | *- [Bb]ackup.rdl
275 | *- [Bb]ackup ([0-9]).rdl
276 | *- [Bb]ackup ([0-9][0-9]).rdl
277 |
278 | # Microsoft Fakes
279 | FakesAssemblies/
280 |
281 | # GhostDoc plugin setting file
282 | *.GhostDoc.xml
283 |
284 | # Node.js Tools for Visual Studio
285 | .ntvs_analysis.dat
286 | node_modules/
287 |
288 | # Visual Studio 6 build log
289 | *.plg
290 |
291 | # Visual Studio 6 workspace options file
292 | *.opt
293 |
294 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
295 | *.vbw
296 |
297 | # Visual Studio LightSwitch build output
298 | **/*.HTMLClient/GeneratedArtifacts
299 | **/*.DesktopClient/GeneratedArtifacts
300 | **/*.DesktopClient/ModelManifest.xml
301 | **/*.Server/GeneratedArtifacts
302 | **/*.Server/ModelManifest.xml
303 | _Pvt_Extensions
304 |
305 | # Paket dependency manager
306 | .paket/paket.exe
307 | paket-files/
308 |
309 | # FAKE - F# Make
310 | .fake/
311 |
312 | # CodeRush personal settings
313 | .cr/personal
314 |
315 | # Python Tools for Visual Studio (PTVS)
316 | __pycache__/
317 | *.pyc
318 |
319 | # Cake - Uncomment if you are using it
320 | # tools/**
321 | # !tools/packages.config
322 |
323 | # Tabs Studio
324 | *.tss
325 |
326 | # Telerik's JustMock configuration file
327 | *.jmconfig
328 |
329 | # BizTalk build output
330 | *.btp.cs
331 | *.btm.cs
332 | *.odx.cs
333 | *.xsd.cs
334 |
335 | # OpenCover UI analysis results
336 | OpenCover/
337 |
338 | # Azure Stream Analytics local run output
339 | ASALocalRun/
340 |
341 | # MSBuild Binary and Structured Log
342 | *.binlog
343 |
344 | # NVidia Nsight GPU debugger configuration file
345 | *.nvuser
346 |
347 | # MFractors (Xamarin productivity tool) working folder
348 | .mfractor/
349 |
350 | # Local History for Visual Studio
351 | .localhistory/
352 |
353 | # BeatPulse healthcheck temp database
354 | healthchecksdb
355 |
356 | # Backup folder for Package Reference Convert tool in Visual Studio 2017
357 | MigrationBackup/
358 |
359 | # Ionide (cross platform F# VS Code tools) working folder
360 | .ionide/
361 |
362 | # Fody - auto-generated XML schema
363 | FodyWeavers.xsd
364 |
365 | # Test
366 | testimgs/
367 |
368 | # Qt build
369 | build-LabCD-*-Debug/
370 | build-LabCD-*-Release/
371 |
372 | # Setup
373 | setup/
374 |
375 | # Depends
376 | labcd/depends/
377 |
378 | # Config
379 | labcd/configs/setting.ini
380 |
381 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # LabCD
2 |
3 | 简体中文 | [English](./README_EN.md)
4 |
5 | [](https://github.com/geoyee/LabCD/releases) [](LICENSE) 
6 |
7 | https://user-images.githubusercontent.com/71769312/216638896-bd1d5e40-6512-478d-b7b0-927c142aece6.mp4
8 |
9 | 遥感变化检测标注工具,设计参考[EISeg](https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.6/EISeg),可以分别在左右两张画布中标注两个时段影像的变化信息,将自动同步在另一画布进行显示。
10 |
11 | ## 更新
12 |
13 | 1. 增加第一次加载多光谱数据计算最佳波段组合,后续加载计算所得的波段
14 | 2. 增加切分图像块可以设置大小,并不会舍去不满一个块的部分
15 | 4. 增加合并标签图像块
16 | 6. 增加过滤空标签和图像和加载推理好的掩膜
17 |
18 | ## 依赖
19 |
20 | - Qt 6.4.0
21 |
22 | - OpenCV 4.5.5
23 |
24 | - JsonCpp 1.9.5
25 |
26 | - GDAL 3.5.3
27 |
28 | - Eigen 3.4.0
29 |
30 | ## 教程与文档
31 |
32 | - [下载与使用](./docs/Download_and_usage.md)
33 | - [快捷键](./docs/Keyboard_shortcuts.md)
34 | - [使用教程](./docs/Usage_tutorial.md)
35 |
36 | ## 资源
37 |
38 | - 部分图标:[SuperMap GIS产品彩色系功能图标库](https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=32519)
39 | - 标签预定义颜色:[色板 | AntV](https://antv.vision/zh/docs/specification/language/palette)
40 |
--------------------------------------------------------------------------------
/README_EN.md:
--------------------------------------------------------------------------------
1 | # LabCD
2 |
3 | Englis | [简体中文](./README.md)
4 |
5 | [](https://github.com/geoyee/LabCD/releases) [](LICENSE) 
6 |
7 | https://user-images.githubusercontent.com/71769312/216638896-bd1d5e40-6512-478d-b7b0-927c142aece6.mp4
8 |
9 | Remote sensing change detection annotation tool, it was designed with reference to [EISeg](https://github.com/PaddlePaddle/PaddleSeg/tree/release/2.6/EISeg). It can mark the change information of images in two periods in the left and right canvases respectively, and automatically display it in the other canvas synchronously.
10 |
11 | ## News
12 |
13 | 1. Add calculation of OIF for the first time loading multi-spectral data, and subsequent loading of calculated bands.
14 | 2. Add the ability to set the size of the split image blocks, without discarding any portions that do not fill a complete block.
15 | 3. Add merging of labeled image blocks.
16 | 4. Add filtering of empty labels and images, and loading of pre-inferred masks.
17 |
18 | ## Dependences
19 |
20 | - Qt 6.4.0
21 |
22 | - OpenCV 4.5.5
23 |
24 | - JsonCpp 1.9.5
25 |
26 | - GDAL 3.5.3
27 |
28 | - Eigen 3.4.0
29 |
30 | ## Tutorials and Documents
31 |
32 | - [Download and usage](./docs/Download_and_usage_en.md)
33 |
34 | - [Keyboard shortcuts](./docs/Keyboard_shortcuts_en.md)
35 | - [Usage tutorial](./docs/Usage_tutorial_en.md)
36 |
37 | ## Resources
38 |
39 | - Icon:[SuperMap GIS Product color system function icon library](https://www.iconfont.cn/collections/detail?spm=a313x.7781069.1998910419.d9df05512&cid=32519&lang=en-us)
40 | - Color:[Color Palette | AntV](https://antv.vision/en/docs/specification/language/palette)
41 |
--------------------------------------------------------------------------------
/docs/Download_and_usage.md:
--------------------------------------------------------------------------------
1 | # 下载和使用
2 |
3 | ## 直接使用
4 |
5 | 提供x64版本的Windows exe[下载](https://github.com/geoyee/LabCD/releases/download/0.3/LabCD-0.3-x64-setup.exe),下载后按照流程安装后即可。打包过程由[Inno Setup](https://jrsoftware.org/)提供。
6 |
7 | ## 构建使用
8 |
9 | 1. 克隆项目到本地:
10 |
11 | ```shell
12 | git clone https://github.com/geoyee/LabCD
13 | ```
14 |
15 | 2. 根据系统下载[Qt 6.4.0](https://www.qt.io/download-qt-installer?hsCtaTracking=99d9dd4f-5681-48d2-b096-470725510d34%7C074ddad0-fdef-4e53-8aa8-5e8a876d6ab4)版本并安装。Windows用户如需要使用Visual Studio需要下载VS扩展:Qt Visual Studio Tools。
16 |
17 | 3. 下载所需的依赖包。目前需要的第三方包有`OpenCV 4.5.5`,`JsonCpp 1.9.5`,`GDAL 3.5.3`和`Eigen 3.4.0`。
18 |
19 | 1. `OpenCV 4.5.5`可从官方github仓库进行[下载](https://github.com/opencv/opencv/releases/tag/4.5.5)并使用cmake进行编译。
20 | 2. `JsonCpp 1.9.5`可从官方github仓库进行[下载](https://github.com/open-source-parsers/jsoncpp/releases/tag/1.9.5)并使用cmake进行编译。
21 | 3. `GDAL 3.5.3`可从官方github仓库进行[下载](https://github.com/OSGeo/gdal/releases/tag/v3.5.3)并使用cmake进行编译。
22 | 4. `Eigen 3.4.0`可从官方gitlab仓库直接进行[下载](https://gitlab.com/libeigen/eigen/-/tree/3.4.0)。
23 |
24 | 准备好之后需要在`labcd`的文件夹下新建`depends`文件夹,其中包括`include`和`lib`文件夹。将Opencv、JsonCpp和GDAL编译完成对应的include和lib文件放入,将Eigen源代码直接放到`depends`下。完成后的`depends`文件夹结构如下:
25 |
26 | ```
27 | depends
28 | ├-- include
29 | | ├-- gdal
30 | | ├-- json
31 | | └-- opencv2
32 | ├-- lib
33 | | ├-- gdal_i.lib
34 | | ├-- jsoncpp_static.lib
35 | | ├-- jsoncpp_staticd.lib
36 | | ├-- opencv_world455.lib
37 | | └-- opencv_world455d.lib
38 | └-- Eigen
39 | ```
40 |
41 | 4. 完成后可使用Visual Studio打开对应的`sln`文件构建或其他平台使用Qt Creator打开`pro`文件,建立qmake后构建,即可使用。
42 | 5. CMakeLists由[qmake2cmake](https://www.qt.io/blog/introducing-qmake2cmake)生成,如第三方依赖的路径发生变化,无论是Visual Studio还是Qt Creator或是Cmake都需要对依赖的路径进行调整。
--------------------------------------------------------------------------------
/docs/Download_and_usage_en.md:
--------------------------------------------------------------------------------
1 | # Download and Usage
2 |
3 | ## Direct Use
4 |
5 | The x64 version of Windows exe is available for direct use, which can be downloaded [here](https://github.com/geoyee/LabCD/releases/download/0.3/LabCD-0.3-x64-setup.exe). After downloading, follow the process to install. The packaging process is provided by [Inno Setup](https://jrsoftware.org/).
6 |
7 | ## Building and Use
8 |
9 | 1. Clone the project to your local machine:
10 |
11 | ```shell
12 | git clone https://github.com/geoyee/LabCD
13 | ```
14 |
15 | 1. Download and install [Qt 6.4.0](https://www.qt.io/download-qt-installer?hsCtaTracking=99d9dd4f-5681-48d2-b096-470725510d34|074ddad0-fdef-4e53-8aa8-5e8a876d6ab4) version according to your system. Windows users who want to use Visual Studio need to download the VS extension: Qt Visual Studio Tools.
16 |
17 | 2. Download the required dependencies. Currently, the required third-party packages are `OpenCV 4.5.5`, `JsonCpp 1.9.5`, `GDAL 3.5.3`, and `Eigen 3.4.0`.
18 |
19 | 1. `OpenCV 4.5.5` can be downloaded from the official GitHub repository [here](https://github.com/opencv/opencv/releases/tag/4.5.5) and compiled using cmake.
20 | 2. `JsonCpp 1.9.5` can be downloaded from the official GitHub repository [here](https://github.com/open-source-parsers/jsoncpp/releases/tag/1.9.5) and compiled using cmake.
21 | 3. `GDAL 3.5.3` can be downloaded from the official GitHub repository [here](https://github.com/OSGeo/gdal/releases/tag/v3.5.3) and compiled using cmake.
22 | 4. `Eigen 3.4.0` can be downloaded directly from the official gitlab repository [here](https://gitlab.com/libeigen/eigen/-/tree/3.4.0).
23 |
24 | After preparing, you need to create a `depends` folder under the `labcd` folder, which includes `include` and `lib` folders. Put the completed include and lib files corresponding to Opencv, JsonCpp, and GDAL into the `depends` folder, and put the Eigen source code directly into `depends`. The `depends` folder structure after completion is as follows:
25 |
26 | ```
27 | luaCopy codedepends
28 | ├-- include
29 | | ├-- gdal
30 | | ├-- json
31 | | └-- opencv2
32 | ├-- lib
33 | | ├-- gdal_i.lib
34 | | ├-- jsoncpp_static.lib
35 | | ├-- jsoncpp_staticd.lib
36 | | ├-- opencv_world455.lib
37 | | └-- opencv_world455d.lib
38 | └-- Eigen
39 | ```
40 |
41 | 3. After completion, you can use Visual Studio to open the corresponding `sln` file for building or use Qt Creator to open the `pro` file, establish qmake and build, then you can use it.
42 |
43 | 4. The CMakeLists is generated by [qmake2cmake](https://www.qt.io/blog/introducing-qmake2cmake). If the paths of third-party dependencies change, whether it is Visual Studio, Qt Creator, or CMake, you need to adjust the paths of the dependencies.
--------------------------------------------------------------------------------
/docs/Keyboard_shortcuts.md:
--------------------------------------------------------------------------------
1 | # 快捷键列表
2 |
3 | | 操作 | 快捷键 |
4 | | -------------- | ------------------- |
5 | | 画布移动 | 按住鼠标中键移动 |
6 | | 画布缩放 | Ctrl + 鼠标滚轮滚动 |
7 | | 画点 | 鼠标左键 |
8 | | 结束画点 | 鼠标右键 |
9 | | 打开文件夹 | Ctrl + O |
10 | | 切分大图 | Ctrl + B |
11 | | 合并大图 | Ctrl + M |
12 | | 打开快捷键帮助 | Ctrl + H |
13 | | 保存 | Ctrl + S |
14 | | 切换上一张 | S |
15 | | 切换下一张 | F |
16 | | 图像全屏 | Ctrl + F |
17 | | 删除多边形 | Backspace |
18 | | 删除所有多边形 | Delete |
--------------------------------------------------------------------------------
/docs/Keyboard_shortcuts_en.md:
--------------------------------------------------------------------------------
1 | # Keyboard shortcut list
2 |
3 | | Operation | Shortcut |
4 | | ------------------------ | --------------------------------- |
5 | | Move canvas | Hold middle mouse button and move |
6 | | Zoom canvas | Ctrl + Scroll mouse wheel |
7 | | Draw point | Left mouse button |
8 | | End point drawing | Right mouse button |
9 | | Open folder | Ctrl + O |
10 | | Split image | Ctrl + B |
11 | | Merge image | Ctrl + M |
12 | | Open shortcut help | Ctrl + H |
13 | | Save | Ctrl + S |
14 | | Switch to previous image | S |
15 | | Switch to next image | F |
16 | | Image fullscreen | Ctrl + F |
17 | | Delete polygon | Backspace |
18 | | Delete all polygons | Delete |
--------------------------------------------------------------------------------
/docs/Usage_tutorial.md:
--------------------------------------------------------------------------------
1 | # 使用教程
2 |
3 | 在使用前,需要对文件的组织形式进行了解。当未标注时,文件可只有`A`和`B`两个文件夹,其中保存的两时期影像的文件名相同。文件组织如下:
4 |
5 | ```
6 | testimgs
7 | ├-- A # 用于存放时段一的图像
8 | | └-- 01.jpg # 时段一图像
9 | ├-- B # 用于存放时段二的图像
10 | | └-- 01.jpg # 时段二图像
11 | ├-- GT # 自动生成,用于存放标注结果
12 | | ├-- 01_pseudo.png # 伪彩色图
13 | | ├-- 01.bmp # 单波段标注图
14 | | └-- 01.json # 自定义格式标注,用于恢复多边形
15 | └-- label.json # 自动生成,用于自动标签导入
16 | ```
17 |
18 | ## 切分/合并大图
19 |
20 | 在“文件”中使用“切分大图”,需要选择大图路径和切分大小,可将一张大的遥感图像切分为多张小图。其中小图的命名中带有该图在大图像左上角的坐标,因此如果需要使用“合并大图”操作将该文件夹内的小图合并,则不能修改图像的名字。
21 |
22 | ## 清理空白标签
23 |
24 | 在“文件”中使用“清理空白标签”,需要选择符合文件组织的文件夹。该操作会遍历`GT`中的标签,将没有内容的标签对应的两时期影像、json文件等一并删除。
25 |
26 | ## 从标签建立标注
27 |
28 | 在“文件”中使用“从标签建立标注”,需要选择一个仅存放图像掩模的文件夹,该操作会为文件夹中的掩膜生成对应的json文件,使LabCD打开时绘制多边形以用于进行调整。
29 |
30 | ## 变化检测标注
31 |
32 | 核心功能,该功能使用时,在“文件”中使用“打开文件夹”,选择符合文件组织的文件夹,即可进行标注。
33 |
34 | 1. 标注时需要先在“标签列表”中“添加标签”,设置对应的颜色和文字内容。
35 | 2. 可以通过上/下翻页或直接双击“数据列表”中的文件名,切换到需要标注的文件。
36 | 3. 点击一个标签,再在图像中进行标注,鼠标左边新建点,右键完成当前多边形标注。
37 | 4. 完成后切换图像或点击保存都会保存结果,结果包括一个掩膜文件、一个多边形的json文件和一个伪彩色文件。
38 |
39 | *最好每次绘制完一个多边形都在画布上点一下鼠标右键,若突然点击左键无效,可以切换一下标签,重新点击。*
40 |
41 | 其余功能包括:
42 |
43 | - 设置十字丝的颜色。
44 | - 加载一个小图像,该图像为两时期影像计算的变化强度图像。
45 | - 增加第一次加载多光谱数据计算最佳波段组合,后续加载计算所得的波段。
46 | - 删除指定多边形和清空多边形。
47 | - 双击”点“可以删除该点,双击”线“可以在该线处新建一个点。
--------------------------------------------------------------------------------
/docs/Usage_tutorial_en.md:
--------------------------------------------------------------------------------
1 | # User Guide
2 |
3 | Before using the software, it is necessary to understand the organization of files. When not specified, there may only be two folders, `A` and `B`, containing images from two different periods with the same file name. The file organization is as follows:
4 |
5 | ```
6 | testimgs
7 | ├-- A # Used to store images from period one
8 | | └-- 01.jpg # Image from period one
9 | ├-- B # Used to store images from period two
10 | | └-- 01.jpg # Image from period two
11 | ├-- GT # Automatically generated folder used to store annotation results
12 | | ├-- 01_pseudo.png # Pseudo-colored image
13 | | ├-- 01.bmp # Single-band annotated image
14 | | └-- 01.json # Custom format annotation file for restoring polygons
15 | └-- label.json # Automatically generated file used for importing auto-labels
16 | ```
17 |
18 | ## Large image split/merge
19 |
20 | To use the "Large image split" feature under the "Files" menu, select the path of the large image and the splitting size, which can split a large remote sensing image into multiple smaller images. The names of the smaller images contain the coordinates of the upper-left corner of the image in the large image. Therefore, if you need to use the "Large image merge" operation to merge the small images in this folder, you cannot modify the names of the images.
21 |
22 | ## Clear empty mask
23 |
24 | To use the "Clear empty mask" feature under the "Files" menu, select the folder that meets the file organization requirements. This operation will traverse the labels in `GT` and delete the corresponding two-period images, json files, and other files that have no content.
25 |
26 | ## Create Annotation from mask
27 |
28 | To use the "Create Annotation from mask" feature under the "Files" menu, select a folder that contains only image masks. This operation will generate the corresponding json file for the masks in the folder, allowing LabCD to draw polygons for adjustment.
29 |
30 | ## Change Detection Annotation
31 |
32 | The core function of the software is change detection annotation. To use this feature, select a folder that meets the file organization requirements under the "Files" menu, and annotation can be performed.
33 |
34 | 1. To annotate, first "Add Label" in the "Label List" and set the corresponding color and text content.
35 | 2. You can switch to the file that needs to be annotated by paging up/down or directly double-clicking the file name in the "Data List".
36 | 3. Click a label, and then annotate in the image. Left-click to create a new point, and right-click to complete the current polygon annotation.
37 | 4. After completion, switching to another image or clicking "Save" will save the results, including a mask file, a polygon json file, and a pseudo-colored image.
38 |
39 | *It is recommended to right-click the mouse after drawing each polygon on the canvas. If left-clicking suddenly stops working, you can switch to another label and click again.*
40 |
41 | Other features include:
42 |
43 | - Setting the color of the crosshairs.
44 | - Loading a small image, which is the change intensity image calculated from the two periods of images.
45 | - Adding the function to calculate the best band combination for the first loading of multi-spectral data, and loading the calculated bands for subsequent loading.
46 | - Deleting a specified polygon and clearing all polygons.
47 | - Double-clicking a "point" to delete the point, and double-clicking a "line" to create a new point on the line.
--------------------------------------------------------------------------------
/labcd/.editorconfig:
--------------------------------------------------------------------------------
1 | # Visual Studio 生成了具有 C++ 设置的 .editorconfig 文件。
2 | root = true
3 |
4 | [*.{c++,cc,cpp,cppm,cu,cuh,cxx,h,h++,hh,hpp,hxx,inl,ipp,ixx,tlh,tli}]
5 |
6 | # 文件编码方式
7 | charset = utf-8-bom
8 |
9 | # Visual C++ 代码样式设置
10 |
11 | cpp_generate_documentation_comments = xml
12 |
13 | # Visual C++ 格式设置
14 |
15 | cpp_indent_braces = false
16 | cpp_indent_multi_line_relative_to = innermost_parenthesis
17 | cpp_indent_within_parentheses = indent
18 | cpp_indent_preserve_within_parentheses = true
19 | cpp_indent_case_contents = true
20 | cpp_indent_case_labels = false
21 | cpp_indent_case_contents_when_block = false
22 | cpp_indent_lambda_braces_when_parameter = true
23 | cpp_indent_goto_labels = one_left
24 | cpp_indent_preprocessor = leftmost_column
25 | cpp_indent_access_specifiers = false
26 | cpp_indent_namespace_contents = true
27 | cpp_indent_preserve_comments = false
28 | cpp_new_line_before_open_brace_namespace = ignore
29 | cpp_new_line_before_open_brace_type = ignore
30 | cpp_new_line_before_open_brace_function = ignore
31 | cpp_new_line_before_open_brace_block = ignore
32 | cpp_new_line_before_open_brace_lambda = ignore
33 | cpp_new_line_scope_braces_on_separate_lines = false
34 | cpp_new_line_close_brace_same_line_empty_type = false
35 | cpp_new_line_close_brace_same_line_empty_function = false
36 | cpp_new_line_before_catch = true
37 | cpp_new_line_before_else = true
38 | cpp_new_line_before_while_in_do_while = false
39 | cpp_space_before_function_open_parenthesis = remove
40 | cpp_space_within_parameter_list_parentheses = false
41 | cpp_space_between_empty_parameter_list_parentheses = false
42 | cpp_space_after_keywords_in_control_flow_statements = true
43 | cpp_space_within_control_flow_statement_parentheses = false
44 | cpp_space_before_lambda_open_parenthesis = false
45 | cpp_space_within_cast_parentheses = false
46 | cpp_space_after_cast_close_parenthesis = false
47 | cpp_space_within_expression_parentheses = false
48 | cpp_space_before_block_open_brace = true
49 | cpp_space_between_empty_braces = false
50 | cpp_space_before_initializer_list_open_brace = false
51 | cpp_space_within_initializer_list_braces = true
52 | cpp_space_preserve_in_initializer_list = true
53 | cpp_space_before_open_square_bracket = false
54 | cpp_space_within_square_brackets = false
55 | cpp_space_before_empty_square_brackets = false
56 | cpp_space_between_empty_square_brackets = false
57 | cpp_space_group_square_brackets = true
58 | cpp_space_within_lambda_brackets = false
59 | cpp_space_between_empty_lambda_brackets = false
60 | cpp_space_before_comma = false
61 | cpp_space_after_comma = true
62 | cpp_space_remove_around_member_operators = true
63 | cpp_space_before_inheritance_colon = true
64 | cpp_space_before_constructor_colon = true
65 | cpp_space_remove_before_semicolon = true
66 | cpp_space_after_semicolon = true
67 | cpp_space_remove_around_unary_operator = true
68 | cpp_space_around_binary_operator = insert
69 | cpp_space_around_assignment_operator = insert
70 | cpp_space_pointer_reference_alignment = left
71 | cpp_space_around_ternary_operator = insert
72 | cpp_wrap_preserve_blocks = one_liners
73 |
--------------------------------------------------------------------------------
/labcd/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.16)
2 | project(LabCD VERSION 1.0 LANGUAGES CXX)
3 |
4 | set(CMAKE_INCLUDE_CURRENT_DIR ON)
5 |
6 | find_package(QT NAMES Qt5 Qt6 REQUIRED COMPONENTS Core)
7 | find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Gui Widgets)
8 |
9 | qt_standard_project_setup()
10 |
11 | qt_add_executable(LabCD WIN32 MACOSX_BUNDLE
12 | labcd.cpp labcd.h
13 | main.cpp
14 | utils/colormap.cpp utils/colormap.h
15 | utils/fileworker.cpp utils/fileworker.h
16 | utils/imgpress.cpp utils/imgpress.h
17 | utils/label.cpp utils/label.h
18 | widgets/annotationscence.cpp widgets/annotationscence.h
19 | widgets/annotationview.cpp widgets/annotationview.h
20 | widgets/canvas.cpp widgets/canvas.h
21 | widgets/filelist.cpp widgets/filelist.h
22 | widgets/labeltable.cpp widgets/labeltable.h
23 | widgets/labgrid.cpp widgets/labgrid.h
24 | widgets/labline.cpp widgets/labline.h
25 | widgets/labpolygon.cpp widgets/labpolygon.h
26 | widgets/multcanvas.cpp widgets/multcanvas.h
27 | widgets/opttypes.h
28 | )
29 | target_include_directories(LabCD PRIVATE
30 | depends
31 | depends/include
32 | )
33 |
34 | target_link_libraries(LabCD PRIVATE
35 | # Remove: L${CMAKE_CURRENT_SOURCE_DIR}/depends/lib/
36 | Qt::Core
37 | Qt::Gui
38 | Qt::Widgets
39 | gdal_i
40 | )
41 |
42 |
43 | # Resources:
44 | set(labcd_resource_files
45 | "resources/Icon.png"
46 | )
47 |
48 | qt_add_resources(LabCD "labcd"
49 | PREFIX
50 | "/main"
51 | FILES
52 | ${labcd_resource_files}
53 | )
54 | set(labcd1_resource_files
55 | "resources/Chinese.png"
56 | "resources/ClearMask.png"
57 | "resources/Convert.png"
58 | "resources/English.png"
59 | "resources/Folder.png"
60 | "resources/Github.png"
61 | "resources/Help.png"
62 | "resources/Merge.png"
63 | "resources/Split.png"
64 | )
65 |
66 | qt_add_resources(LabCD "labcd1"
67 | PREFIX
68 | "/menu"
69 | FILES
70 | ${labcd1_resource_files}
71 | )
72 | set(labcd2_resource_files
73 | "resources/Color.png"
74 | "resources/DeleteAllPolygons.png"
75 | "resources/DeletePolygon.png"
76 | "resources/Enlarge.png"
77 | "resources/Full.png"
78 | "resources/Last.png"
79 | "resources/Narrow.png"
80 | "resources/Next.png"
81 | "resources/Reference.png"
82 | "resources/Save.png"
83 | )
84 |
85 | qt_add_resources(LabCD "labcd2"
86 | PREFIX
87 | "/tools"
88 | FILES
89 | ${labcd2_resource_files}
90 | )
91 | set(labcd3_resource_files
92 | "resources/AddLabel.png"
93 | "resources/CantDelete.png"
94 | "resources/Delete.png"
95 | )
96 |
97 | qt_add_resources(LabCD "labcd3"
98 | PREFIX
99 | "/docks"
100 | FILES
101 | ${labcd3_resource_files}
102 | )
103 | set(labcd4_resource_files
104 | "configs/colormap.txt"
105 | )
106 |
107 | qt_add_resources(LabCD "labcd4"
108 | PREFIX
109 | "/configs"
110 | FILES
111 | ${labcd4_resource_files}
112 | )
113 | set(labcd5_resource_files
114 | "i18n/English.qm"
115 | )
116 |
117 | qt_add_resources(LabCD "labcd5"
118 | PREFIX
119 | "/translate"
120 | FILES
121 | ${labcd5_resource_files}
122 | )
123 |
124 | if(WIN32 AND CONFIG(release,debug OR release))
125 | target_link_libraries(LabCD PRIVATE
126 | # Remove: L${CMAKE_CURRENT_SOURCE_DIR}/depends/lib/
127 | jsoncpp_static
128 | opencv_world455
129 | )
130 | endif()
131 |
132 | if((NOT (WIN32 AND CONFIG(release,debug OR release))) AND (WIN32 AND CONFIG(debug,debug OR release)))
133 | target_link_libraries(LabCD PRIVATE
134 | # Remove: L${CMAKE_CURRENT_SOURCE_DIR}/depends/lib/
135 | jsoncpp_staticd
136 | opencv_world455d
137 | )
138 | endif()
139 |
140 | if(((NOT (WIN32 AND CONFIG(release,debug OR release))) AND (NOT (WIN32 AND CONFIG(debug,debug OR release)))) AND (UNIX))
141 | target_link_libraries(LabCD PRIVATE
142 | # Remove: L${CMAKE_CURRENT_SOURCE_DIR}/depends/lib/
143 | jsoncpp_static
144 | opencv_world455
145 | )
146 | endif()
147 |
148 | install(TARGETS LabCD
149 | BUNDLE DESTINATION .
150 | RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
151 | )
152 |
153 | qt_generate_deploy_app_script(
154 | TARGET LabCD
155 | FILENAME_VARIABLE deploy_script
156 | NO_UNSUPPORTED_PLATFORM_ERROR
157 | )
158 | install(SCRIPT ${deploy_script})
159 |
--------------------------------------------------------------------------------
/labcd/LabCD.pri:
--------------------------------------------------------------------------------
1 | # ----------------------------------------------------
2 | # This file is generated by the Qt Visual Studio Tools.
3 | # ------------------------------------------------------
4 |
5 | HEADERS += ./utils/colormap.h \
6 | ./utils/fileworker.h \
7 | ./utils/imgpress.h \
8 | ./utils/label.h \
9 | ./widgets/labgrid.h \
10 | ./widgets/labline.h \
11 | ./widgets/labpolygon.h \
12 | ./widgets/opttypes.h \
13 | ./labcd.h \
14 | ./widgets/annotationscence.h \
15 | ./widgets/annotationview.h \
16 | ./widgets/canvas.h \
17 | ./widgets/multcanvas.h \
18 | ./widgets/filelist.h \
19 | ./widgets/labeltable.h
20 | SOURCES += ./utils/colormap.cpp \
21 | ./utils/fileworker.cpp \
22 | ./utils/imgpress.cpp \
23 | ./utils/label.cpp \
24 | ./widgets/annotationscence.cpp \
25 | ./widgets/annotationview.cpp \
26 | ./widgets/canvas.cpp \
27 | ./widgets/labgrid.cpp \
28 | ./widgets/labline.cpp \
29 | ./widgets/labpolygon.cpp \
30 | ./widgets/multcanvas.cpp \
31 | ./widgets/filelist.cpp \
32 | ./widgets/labeltable.cpp \
33 | ./labcd.cpp \
34 | ./main.cpp
35 | TRANSLATIONS += ./i18n/English.ts
36 | RESOURCES += labcd.qrc
37 |
--------------------------------------------------------------------------------
/labcd/LabCD.pro:
--------------------------------------------------------------------------------
1 | # qt
2 | QT += core gui widgets
3 | TEMPLATE = app
4 | TARGET = LabCD
5 | DESTDIR = ./x64/Release
6 | CONFIG += release
7 | MOC_DIR += GeneratedFiles/$(ConfigurationName)
8 | OBJECTS_DIR += release
9 | UI_DIR += GeneratedFiles
10 | RCC_DIR += GeneratedFiles
11 | include(LabCD.pri)
12 | TRANSLATIONS += i18n/English.ts
13 |
14 | # third-party dependency
15 | unix|win32: LIBS += -L$$PWD/depends/lib/ -lgdal_i
16 | win32:CONFIG(release, debug|release): LIBS += -L$$PWD/depends/lib/ -ljsoncpp_static
17 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/depends/lib/ -ljsoncpp_staticd
18 | else:unix: LIBS += -L$$PWD/depends/lib/ -ljsoncpp_static
19 | win32:CONFIG(release, debug|release): LIBS += -L$$PWD/depends/lib/ -lopencv_world455
20 | else:win32:CONFIG(debug, debug|release): LIBS += -L$$PWD/depends/lib/ -lopencv_world455d
21 | else:unix: LIBS += -L$$PWD/depends/lib/ -lopencv_world455
22 | INCLUDEPATH += $$PWD/depends
23 | INCLUDEPATH += $$PWD/depends/include
24 | DEPENDPATH += $$PWD/depends/include
25 |
--------------------------------------------------------------------------------
/labcd/LabCD.sln:
--------------------------------------------------------------------------------
1 | Microsoft Visual Studio Solution File, Format Version 12.00
2 | # Visual Studio Version 16
3 | VisualStudioVersion = 16.0.32802.440
4 | MinimumVisualStudioVersion = 10.0.40219.1
5 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LabCD", "LabCD.vcxproj", "{4714941A-16FB-42A4-B244-878CD5638731}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|x64 = Debug|x64
10 | Release|x64 = Release|x64
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {4714941A-16FB-42A4-B244-878CD5638731}.Debug|x64.ActiveCfg = Debug|x64
14 | {4714941A-16FB-42A4-B244-878CD5638731}.Debug|x64.Build.0 = Debug|x64
15 | {4714941A-16FB-42A4-B244-878CD5638731}.Release|x64.ActiveCfg = Release|x64
16 | {4714941A-16FB-42A4-B244-878CD5638731}.Release|x64.Build.0 = Release|x64
17 | EndGlobalSection
18 | GlobalSection(SolutionProperties) = preSolution
19 | HideSolutionNode = FALSE
20 | EndGlobalSection
21 | GlobalSection(ExtensibilityGlobals) = postSolution
22 | SolutionGuid = {63D7EF12-74CD-4329-8754-3601FEC935BC}
23 | EndGlobalSection
24 | EndGlobal
25 |
--------------------------------------------------------------------------------
/labcd/LabCD.vcxproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | x64
7 |
8 |
9 | Release
10 | x64
11 |
12 |
13 |
14 | {4714941A-16FB-42A4-B244-878CD5638731}
15 | QtVS_v304
16 | 10.0.19041.0
17 | 10.0.19041.0
18 | $(MSBuildProjectDirectory)\QtMsBuild
19 |
20 |
21 |
22 | Application
23 | v142
24 |
25 |
26 | Application
27 | v142
28 |
29 |
30 |
31 |
32 |
33 |
34 | 6.4.0_msvc2019_64
35 | core;gui;widgets
36 | debug
37 |
38 |
39 | 6.4.0_msvc2019_64
40 | core;gui;widgets
41 | release
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | $(ProjectDir)depends\include;$(IncludePath)
59 | $(ProjectDir)depends\lib;$(LibraryPath)
60 |
61 |
62 | $(ProjectDir)depends\include;$(IncludePath)
63 | $(ProjectDir)depends\lib;$(LibraryPath)
64 |
65 |
66 |
67 | /NATVIS:"$(IntDir)\qt.natvis" %(AdditionalOptions)
68 | gdal_i.lib;opencv_world455d.lib;jsoncpp_staticd.lib;%(AdditionalDependencies)
69 |
70 |
71 |
72 |
73 | stdc17
74 | $(ProjectDir)depends;%(AdditionalIncludeDirectories)
75 |
76 |
77 |
78 |
79 | /utf-8 /NATVIS:"$(IntDir)\qt.natvis" %(AdditionalOptions)
80 | gdal_i.lib;opencv_world455.lib;jsoncpp_static.lib;%(AdditionalDependencies)
81 |
82 |
83 |
84 |
85 | stdc17
86 | $(ProjectDir)depends;%(AdditionalIncludeDirectories)
87 |
88 |
89 |
90 |
91 | true
92 | true
93 | ProgramDatabase
94 | Disabled
95 | MultiThreadedDebugDLL
96 |
97 |
98 | Windows
99 | true
100 |
101 |
102 |
103 |
104 | true
105 | true
106 | None
107 | MaxSpeed
108 | MultiThreadedDLL
109 |
110 |
111 | Windows
112 | false
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
--------------------------------------------------------------------------------
/labcd/LabCD.vcxproj.filters:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
6 | qml;cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
7 |
8 |
9 | {93995380-89BD-4b04-88EB-625FBE52EBFB}
10 | h;hh;hpp;hxx;hm;inl;inc;xsd
11 |
12 |
13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
14 | qrc;rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
15 |
16 |
17 | {99349809-55BA-4b9d-BF79-8FDBB0286EB3}
18 | ui
19 |
20 |
21 | {639EADAA-A684-42e4-A9AD-28FC9BCB8F7C}
22 | ts
23 |
24 |
25 |
26 |
27 | Resource Files
28 |
29 |
30 | Header Files
31 |
32 |
33 | Source Files
34 |
35 |
36 |
37 |
38 | Source Files
39 |
40 |
41 | Source Files
42 |
43 |
44 | Source Files
45 |
46 |
47 | Source Files
48 |
49 |
50 | Source Files
51 |
52 |
53 | Source Files
54 |
55 |
56 | Source Files
57 |
58 |
59 | Source Files
60 |
61 |
62 | Source Files
63 |
64 |
65 | Source Files
66 |
67 |
68 | Source Files
69 |
70 |
71 | Source Files
72 |
73 |
74 | Source Files
75 |
76 |
77 | Source Files
78 |
79 |
80 |
81 |
82 | Resource Files
83 |
84 |
85 | Resource Files
86 |
87 |
88 | Resource Files
89 |
90 |
91 | Resource Files
92 |
93 |
94 | Resource Files
95 |
96 |
97 | Resource Files
98 |
99 |
100 | Resource Files
101 |
102 |
103 | Resource Files
104 |
105 |
106 | Resource Files
107 |
108 |
109 | Resource Files
110 |
111 |
112 | Resource Files
113 |
114 |
115 | Resource Files
116 |
117 |
118 | Resource Files
119 |
120 |
121 | Resource Files
122 |
123 |
124 | Resource Files
125 |
126 |
127 | Resource Files
128 |
129 |
130 | Resource Files
131 |
132 |
133 | Resource Files
134 |
135 |
136 | Resource Files
137 |
138 |
139 | Resource Files
140 |
141 |
142 | Resource Files
143 |
144 |
145 | Resource Files
146 |
147 |
148 | Resource Files
149 |
150 |
151 |
152 |
153 | Header Files
154 |
155 |
156 | Header Files
157 |
158 |
159 | Header Files
160 |
161 |
162 | Header Files
163 |
164 |
165 | Header Files
166 |
167 |
168 | Header Files
169 |
170 |
171 | Header Files
172 |
173 |
174 | Header Files
175 |
176 |
177 |
178 |
179 | Header Files
180 |
181 |
182 | Header Files
183 |
184 |
185 | Header Files
186 |
187 |
188 | Header Files
189 |
190 |
191 | Header Files
192 |
193 |
194 | Header Files
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 | Translation Files
210 |
211 |
212 |
--------------------------------------------------------------------------------
/labcd/configs/colormap.txt:
--------------------------------------------------------------------------------
1 | 244,108,59
2 | 99,102,129
3 | 249,193,0
4 | 160,180,0
5 | 115,82,59
6 | 217,213,180
7 | 51,142,137
8 | 218,147,70
9 | 234,132,163
10 | 61,127,236
11 | 81,202,147
12 | 178,171,245
13 | 42,67,196
14 | 64,158,210
15 | 206,77,194
16 | 45,86,51
17 | 139,140,69
18 | 151,218,229
19 | 135,41,60
20 | 118,33,203
--------------------------------------------------------------------------------
/labcd/i18n/English.qm:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/i18n/English.qm
--------------------------------------------------------------------------------
/labcd/i18n/English.ts:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | LabCD
6 |
7 |
8 | 文件
9 | File
10 |
11 |
12 |
13 | 打开文件夹
14 | Open image folder
15 |
16 |
17 |
18 | 切分大图
19 | Large image split
20 |
21 |
22 |
23 | 合并大图
24 | Large image merge
25 |
26 |
27 |
28 | 清理空白标签
29 | Clear empty mask
30 |
31 |
32 |
33 | 从标签建立标注
34 | Create annotation from mask
35 |
36 |
37 |
38 | 关于
39 | About
40 |
41 |
42 |
43 | github主页
44 | Homepage of github
45 |
46 |
47 |
48 | 使用帮助
49 | Help
50 |
51 |
52 |
53 | 语言
54 | Language
55 |
56 |
57 |
58 |
59 | 英文
60 | English
61 |
62 |
63 |
64 |
65 | 中文
66 | Chinese
67 |
68 |
69 |
70 |
71 | 当前坐标:
72 | Location:
73 |
74 |
75 |
76 | 数据列表
77 | Data List
78 |
79 |
80 |
81 | 加载图像:
82 | Loaded:
83 |
84 |
85 |
86 | 标签列表
87 | Label List
88 |
89 |
90 |
91 | 当前标签:[
92 | Label: [
93 |
94 |
95 |
96 | 保存
97 | Save
98 |
99 |
100 |
101 | 上一张
102 | Last image
103 |
104 |
105 |
106 | 下一张
107 | Next image
108 |
109 |
110 |
111 | 放大
112 | Enlarge
113 |
114 |
115 |
116 | 缩小
117 | Narrow
118 |
119 |
120 |
121 | 全幅缩放
122 | Full
123 |
124 |
125 |
126 | 删除多边形
127 | Delete polygon
128 |
129 |
130 |
131 | 删除所有多边形
132 | Delete all polygons
133 |
134 |
135 |
136 |
137 | 设置十字丝颜色
138 | Set cross color
139 |
140 |
141 |
142 | 打开变化参考图
143 | Open reference image
144 |
145 |
146 |
147 | 光谱变化向量强度参考图
148 | CVA intensity reference
149 |
150 |
151 |
152 | LabCD - 遥感变化检测标注工具
153 | LabCD - Remote sensing change detection annotation tool
154 |
155 |
156 |
157 | 打开大图像
158 | Open large image
159 |
160 |
161 |
162 | 栅格图像文件 (*.tif *.tiff)
163 | Raster File (*.tif *.tiff)
164 |
165 |
166 |
167 | 设置
168 | Setting
169 |
170 |
171 |
172 | 设置切块大小
173 | Set block size
174 |
175 |
176 |
177 | 切分完成,保存至:
178 | Split finished, save to:
179 |
180 |
181 |
182 | 切分失败,可能是不支持的类型或超出范围的切块大小
183 | Split failed, it may be an unsupported type or an out-of-range block size
184 |
185 |
186 |
187 | 合并完成,保存至:
188 | Merge finished, save to:
189 |
190 |
191 |
192 | 合并失败
193 | Merge failed
194 |
195 |
196 |
197 | 清理完成
198 | Clear finished
199 |
200 |
201 |
202 | 转换完成
203 | Convert finished
204 |
205 |
206 |
207 | 保存图像:
208 | Saved:
209 |
210 |
211 |
212 | LabelTable
213 |
214 |
215 | 添加标签
216 | Add label
217 |
218 |
219 |
220 | 标签颜色选择
221 | Label color selection
222 |
223 |
224 |
225 | 背景
226 | Background
227 |
228 |
229 |
230 | MultCanvas
231 |
232 |
233 |
234 | 错误
235 | Error
236 |
237 |
238 |
239 | 无法打开图像文件。
240 | Unable to open image file.
241 |
242 |
243 |
244 | 两个时段的数据大小不一致。
245 | The data size of the two periods is inconsistent.
246 |
247 |
248 |
249 | QObject
250 |
251 |
252 |
253 | 打开图像文件夹
254 | Open image folder
255 |
256 |
257 |
258 |
259 | 错误
260 | Error
261 |
262 |
263 |
264 | 文件组织格式错误,请确保数据文件夹下仅存在两个(或三个)子文件夹,分别为A、B(以及GT)。
265 | The file organization format is incorrect. Please ensure that there are only two (or three) subfolders under the data folder, namely A, B (and GT).
266 |
267 |
268 |
269 | 时段一数据和时段二数据数量不相等。
270 | The number of data in period 1 and period 2 is not equal.
271 |
272 |
273 |
274 | 提示
275 | Infomation
276 |
277 |
278 |
279 | 重启软件后更新语言设置。
280 | Update the language settings after restarting the software.
281 |
282 |
283 |
284 | 打开标签文件夹
285 | Open mask folder
286 |
287 |
288 |
289 |
--------------------------------------------------------------------------------
/labcd/labcd.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include
18 | #include "labcd.h"
19 | #include "utils/fileworker.h"
20 | #include "utils/imgpress.h"
21 | #include "widgets/annotationview.h"
22 |
23 | LabCD::LabCD(QWidget* parent)
24 | : QMainWindow(parent)
25 | {
26 | setting = new QSettings("./configs/setting.ini", QSettings::IniFormat);
27 | isCN = setting->value("language").toString() != "EN";
28 |
29 | /* 状态栏 */
30 | QStatusBar* lcdStatusBar = statusBar();
31 | messageState = new QLabel("", this); // 用于显示消息
32 | QLabel* messageLocal = new QLabel("", this); // 用于显示坐标
33 | lcdStatusBar->addWidget(messageState);
34 | lcdStatusBar->addPermanentWidget(messageLocal);
35 |
36 | /* 菜单栏 */
37 | QMenuBar* lcdMenuBar = menuBar();
38 | QMenu* fileMenu = new QMenu(tr("文件"), this);
39 | QAction* opensAct = fileMenu->addAction(
40 | QIcon(":/menu/resources/Folder.png"), tr("打开文件夹"));
41 | opensAct->setShortcut(QKeySequence("Ctrl+O"));
42 | connect(opensAct, &QAction::triggered, this, &LabCD::openDir);
43 | fileMenu->addSeparator();
44 | QAction* splitAct = fileMenu->addAction(
45 | QIcon(":/menu/resources/Split.png"), tr("切分大图"));
46 | splitAct->setShortcut(QKeySequence("Ctrl+B"));
47 | connect(splitAct, &QAction::triggered, this, &LabCD::openBigImageFile);
48 | QAction* mergeAct = fileMenu->addAction(
49 | QIcon(":/menu/resources/Merge.png"), tr("合并大图"));
50 | mergeAct->setShortcut(QKeySequence("Ctrl+M"));
51 | connect(mergeAct, &QAction::triggered, this, &LabCD::mergeBigImage);
52 | QAction* clearAct = fileMenu->addAction(
53 | QIcon(":/menu/resources/ClearMask.png"), tr("清理空白标签"));
54 | connect(clearAct, &QAction::triggered, this, &LabCD::clearEmptyMask);
55 | QAction* convertAct = fileMenu->addAction(
56 | QIcon(":/menu/resources/Convert.png"), tr("从标签建立标注"));
57 | connect(convertAct, &QAction::triggered, this, &LabCD::convertMask2Json);
58 | lcdMenuBar->addMenu(fileMenu);
59 | QMenu* aboutMenu = new QMenu(tr("关于"), this);
60 | QAction* githubAct = aboutMenu->addAction(
61 | QIcon(":/menu/resources/Github.png"), tr("github主页"));
62 | connect(githubAct, &QAction::triggered, [=]() {
63 | QDesktopServices::openUrl(QUrl("https://github.com/geoyee/LabCD"));
64 | });
65 | QAction* helpAct = aboutMenu->addAction(
66 | QIcon(":/menu/resources/Help.png"), tr("使用帮助"));
67 | connect(helpAct, &QAction::triggered, [=]() {
68 | QString tutorial = "https://github.com/geoyee/LabCD/tree/develop/docs/Usage_tutorial.md";
69 | if (!isCN)
70 | tutorial = "https://github.com/geoyee/LabCD/tree/develop/docs/Usage_tutorial_en.md";
71 | QDesktopServices::openUrl(QUrl(tutorial));
72 | });
73 | helpAct->setShortcut(QKeySequence("Ctrl+H"));
74 | lcdMenuBar->addMenu(aboutMenu);
75 | QMenu* languMenu = new QMenu(tr("语言"), this);
76 | QAction* setLangeAct;
77 | if (isCN)
78 | setLangeAct = languMenu->addAction(
79 | QIcon(":/menu/resources/English.png"), tr("英文"));
80 | else
81 | setLangeAct = languMenu->addAction(
82 | QIcon(":/menu/resources/Chinese.png"), tr("中文"));
83 | connect(setLangeAct, &QAction::triggered, [=]() {
84 | if (isCN)
85 | {
86 | setLangeAct->setIcon(QIcon(":/menu/resources/Chinese.png"));
87 | setLangeAct->setText(tr("中文"));
88 | setting->setValue("language", "EN");
89 | }
90 | else
91 | {
92 | setLangeAct->setIcon(QIcon(":/menu/resources/English.png"));
93 | setLangeAct->setText(tr("英文"));
94 | setting->setValue("language", "CN");
95 | }
96 | isCN = !isCN;
97 | QMessageBox::information(
98 | parent,
99 | QObject::tr("提示"),
100 | QObject::tr("重启软件后更新语言设置。")
101 | );
102 | });
103 | lcdMenuBar->addMenu(languMenu);
104 |
105 | /* 绘图界面 */
106 | drawCanvas = new MultCanvas(this);
107 | connect(drawCanvas->t1Canva->aView, &AnnotationView::mousePosChanged,
108 | [=](double x, double y) {
109 | messageLocal->setText(
110 | tr("当前坐标:") + \
111 | QString::fromStdString(std::to_string(x)) + ", " + \
112 | QString::fromStdString(std::to_string(y)));
113 | });
114 | connect(drawCanvas->t2Canva->aView, &AnnotationView::mousePosChanged,
115 | [=](double x, double y) {
116 | messageLocal->setText(
117 | tr("当前坐标:") + \
118 | QString::fromStdString(std::to_string(x)) + ", " + \
119 | QString::fromStdString(std::to_string(y)));
120 | });
121 | setCentralWidget(drawCanvas);
122 |
123 | /* 图像文件列表 */
124 | QDockWidget* filesDock = new QDockWidget(tr("数据列表"), this);
125 | filesDock->setMinimumWidth(200);
126 | filesDock->setAllowedAreas(Qt::RightDockWidgetArea);
127 | fListWidget = new FileList(this);
128 | // 保存图像
129 | connect(fListWidget, &FileList::saveLastFileRequest, this, &LabCD::save);
130 | // 加载图像
131 | connect(fListWidget, &FileList::FileClickRequest,
132 | [=](QString t1Path, QString t2Path, QString jsonPath) {
133 | drawCanvas->loadImages(t1Path, t2Path, jsonPath);
134 | updatePolysColor();
135 | QFileInfo fileInfo(t1Path);
136 | fileName = fileInfo.fileName();
137 | messageState->setText(tr("加载图像:") + t1Path);
138 | });
139 | filesDock->setWidget(fListWidget);
140 | addDockWidget(Qt::RightDockWidgetArea, filesDock);
141 |
142 | /* 标签列表 */
143 | QDockWidget* labelsDock = new QDockWidget(tr("标签列表"), this);
144 | labelsDock->setMinimumWidth(200);
145 | labelsDock->setAllowedAreas(Qt::RightDockWidgetArea);
146 | labTableWidget = new LabelTable(this);
147 | labelsDock->setWidget(labTableWidget);
148 | connect(labTableWidget, &LabelTable::labelSelected, [=](Label* nowLabel) {
149 | drawCanvas->labelSelected(nowLabel);
150 | messageState->setText(tr("当前标签:[") + \
151 | QString::fromStdString(std::to_string(nowLabel->getIndex())) + \
152 | "] " + nowLabel->getName());
153 | });
154 | connect(labTableWidget, &LabelTable::colorChanged,
155 | [=](int labelIndex, QColor newColor) {
156 | // 更新界面上的多边形颜色
157 | for (int i = 0; i < drawCanvas->t1Canva->aScene->polygonItems.count(); ++i)
158 | {
159 | if (drawCanvas->t1Canva->aScene->polygonItems[i]->labelIndex == \
160 | labelIndex)
161 | {
162 | drawCanvas->t1Canva->aScene->polygonItems[i]->setColor(
163 | newColor, newColor);
164 | drawCanvas->t2Canva->aScene->polygonItems[i]->setColor(
165 | newColor, newColor);
166 | }
167 | drawCanvas->t1Canva->aScene->setColor(newColor, newColor);
168 | drawCanvas->t2Canva->aScene->setColor(newColor, newColor);
169 | }
170 | });
171 | // 同步Json的加载
172 | connect(drawCanvas->t1Canva, &Canvas::addJsonPoly, \
173 | labTableWidget, &LabelTable::changeLabelDuotoAddPolyJson);
174 | addDockWidget(Qt::RightDockWidgetArea, labelsDock);
175 |
176 | /* 工具栏 */
177 | QToolBar* lcdToolBar = new QToolBar(this);
178 | QAction* saveAct = lcdToolBar->addAction(
179 | QIcon(":/tools/resources/Save.png"), tr("保存"));
180 | saveAct->setShortcut(QKeySequence("Ctrl+S"));
181 | connect(saveAct, &QAction::triggered, this, &LabCD::save);
182 | lcdToolBar->addSeparator();
183 | QAction* lastAct = lcdToolBar->addAction(
184 | QIcon(":/tools/resources/Last.png"), tr("上一张"));
185 | connect(lastAct, &QAction::triggered, [=]() {
186 | save();
187 | fListWidget->gotoLastItem();
188 | });
189 | lastAct->setShortcut(QKeySequence("S"));
190 | QAction* nextAct = lcdToolBar->addAction(
191 | QIcon(":/tools/resources/Next.png"), tr("下一张"));
192 | nextAct->setShortcut(QKeySequence("F"));
193 | connect(nextAct, &QAction::triggered, [=]() {
194 | save();
195 | fListWidget->gotoNextItem();
196 | });
197 | lcdToolBar->addSeparator();
198 | QAction* enlargeAct = lcdToolBar->addAction(
199 | QIcon(":/tools/resources/Enlarge.png"), tr("放大"));
200 | connect(enlargeAct, &QAction::triggered, [=]() {
201 | drawCanvas->t1Canva->aView->scaleZoom(1.1); // 自动同步t2
202 | });
203 | QAction* narrowAct = lcdToolBar->addAction(
204 | QIcon(":/tools/resources/Narrow.png"), tr("缩小"));
205 | connect(narrowAct, &QAction::triggered, [=]() {
206 | drawCanvas->t1Canva->aView->scaleZoom(0.9); // 自动同步t2
207 | });
208 | QAction* fullAct = lcdToolBar->addAction(
209 | QIcon(":/tools/resources/Full.png"), tr("全幅缩放"));
210 | fullAct->setShortcut(QKeySequence("Ctrl+F"));
211 | connect(fullAct, &QAction::triggered, [=]() {
212 | if (drawCanvas->imageWidth != 0 && drawCanvas->imageHeight != 0)
213 | drawCanvas->t1Canva->resetZoom(
214 | drawCanvas->imageWidth, drawCanvas->imageHeight); // 自动同步t2
215 | });
216 | lcdToolBar->addSeparator();
217 | QAction* delPolyAct = lcdToolBar->addAction(
218 | QIcon(":/tools/resources/DeletePolygon.png"), tr("删除多边形"));
219 | connect(delPolyAct, &QAction::triggered, [=]() {
220 | int f1Index = drawCanvas->t1Canva->aScene->findFocusPolygon();
221 | int f2Index = drawCanvas->t2Canva->aScene->findFocusPolygon();
222 | int delIndex = f1Index > f2Index ? f1Index : f2Index;
223 | drawCanvas->t1Canva->aScene->delPoly(delIndex);
224 | drawCanvas->t2Canva->aScene->delPoly(delIndex);
225 | });
226 | delPolyAct->setShortcut(QKeySequence("Backspace"));
227 | QAction* delAllPolysAct = lcdToolBar->addAction(
228 | QIcon(":/tools/resources/DeleteAllPolygons.png"), tr("删除所有多边形"));
229 | connect(delAllPolysAct, &QAction::triggered, [=]() {
230 | drawCanvas->t1Canva->aScene->removeAllPolygons();
231 | drawCanvas->t2Canva->aScene->removeAllPolygons();
232 | });
233 | delAllPolysAct->setShortcut(QKeySequence("Delete"));
234 | lcdToolBar->addSeparator();
235 | QAction* crossColorAct = lcdToolBar->addAction(
236 | QIcon(":/tools/resources/Color.png"), tr("设置十字丝颜色"));
237 | connect(crossColorAct, &QAction::triggered, this, &LabCD::setCrossPenColor);
238 | lcdToolBar->addSeparator();
239 | QAction* isCVAAct = lcdToolBar->addAction(
240 | QIcon(":/tools/resources/Reference.png"), tr("打开变化参考图"));
241 | isCVAAct->setCheckable(true);
242 | // 完成
243 | lcdToolBar->setMovable(false);
244 | addToolBar(Qt::LeftToolBarArea, lcdToolBar);
245 |
246 | /* 变化参考图 */
247 | QDockWidget* refDock = new QDockWidget(tr("光谱变化向量强度参考图"), this);
248 | refDock->setAllowedAreas(Qt::NoDockWidgetArea);
249 | QLabel* imgRef = new QLabel(this);
250 | refDock->setFloating(true);
251 | refDock->hide();
252 | connect(drawCanvas, &MultCanvas::addimgDiff, [=](cv::Mat imgDiff) {
253 | if (isCVAAct->isChecked())
254 | {
255 | refNewHeight = refNewWidth * imgDiff.rows / imgDiff.cols;
256 | cv::cvtColor(imgDiff, imgDiff, cv::COLOR_RGB2BGR);
257 | cv::resize(imgDiff, imgDiff, cv::Size(refNewWidth, refNewHeight));
258 | QImage qimg = QImage(
259 | (const uchar*)(imgDiff.data), imgDiff.cols, imgDiff.rows,
260 | imgDiff.cols * imgDiff.channels(), QImage::Format_RGB888
261 | );
262 | imgRef->setPixmap(QPixmap::fromImage(qimg));
263 | refDock->setFixedSize(refNewWidth, refNewHeight);
264 | refDock->show();
265 | }
266 | });
267 | refDock->setWidget(imgRef);
268 | addDockWidget(Qt::NoDockWidgetArea, refDock);
269 |
270 | /* 界面设置 */
271 | resize(1200, 600);
272 | setWindowTitle(tr("LabCD - 遥感变化检测标注工具"));
273 | setWindowIcon(QIcon(":/main/resources/Icon.png"));
274 | }
275 |
276 | LabCD::~LabCD()
277 | {
278 |
279 | }
280 |
281 | void LabCD::openDir()
282 | {
283 | QStringList t1List;
284 | QStringList t2List;
285 | if (FileWorker::openImageDir(&t1List, &t2List, nullptr, this))
286 | {
287 | // 新建保存目录
288 | QFileInfo fileInfo(t1List.at(0));
289 | savePath = fileInfo.path();
290 | savePath = savePath.replace("\\", "/");
291 | savePath = savePath.section("/", 0, -2);
292 | QString saveImgPath = savePath + "/GT";
293 | FileWorker::createFolder(saveImgPath);
294 | // 加载已有标签
295 | QString jsonPath = savePath + "/label.json";
296 | QFileInfo jsonFileInfo(jsonPath);
297 | if (jsonFileInfo.isFile())
298 | labTableWidget->importLabelFromFile(jsonPath);
299 | // 加载图像
300 | fListWidget->addFileNames(t1List, t2List);
301 | fListWidget->gotoItem(0);
302 | // 加载总进度
303 | fListWidget->resetProgress();
304 | }
305 | }
306 |
307 | void LabCD::openBigImageFile()
308 | {
309 | // 获取文件路径
310 | QString fileName = QFileDialog::getOpenFileName(
311 | this,
312 | tr("打开大图像"),
313 | QString(),
314 | tr("栅格图像文件 (*.tif *.tiff)")
315 | );
316 | if (fileName == "")
317 | return;
318 | QString saveDir = QFileInfo(fileName).absolutePath() + \
319 | QDir::separator() + "split_output";
320 | saveDir = saveDir.replace("\\", "/");
321 | FileWorker::createFolder(saveDir);
322 | // 获取切分大小
323 | bool blockOk = false;
324 | int blockSize = QInputDialog::getInt(
325 | this,
326 | tr("设置"),
327 | tr("设置切块大小"),
328 | 512, 1, 2048, 1,
329 | &blockOk
330 | );
331 | if (!blockOk)
332 | return;
333 | QtConcurrent::run([=]() {
334 | if (ImagePress::splitTiff(fileName, saveDir, blockSize, blockSize))
335 | messageState->setText(tr("切分完成,保存至:") + saveDir);
336 | else
337 | messageState->setText(tr("切分失败,可能是不支持的类型或超出范围的切块大小"));
338 | });
339 | }
340 |
341 | void LabCD::mergeBigImage()
342 | {
343 | QString dirPath = QFileDialog::getExistingDirectory(
344 | this,
345 | QObject::tr("打开图像文件夹"),
346 | QString(),
347 | QFileDialog::ShowDirsOnly
348 | );
349 | if (dirPath.isEmpty())
350 | return;
351 | QtConcurrent::run([=]() {
352 | if (ImagePress::mergeTiff(dirPath))
353 | messageState->setText(
354 | tr("合并完成,保存至:") + dirPath + "/merge.tif");
355 | else
356 | messageState->setText(tr("合并失败"));
357 | });
358 | }
359 |
360 | void LabCD::clearEmptyMask()
361 | {
362 | QStringList t1List;
363 | QStringList t2List;
364 | QStringList GTList;
365 | if (FileWorker::openImageDir(&t1List, &t2List, >List, this))
366 | {
367 | QtConcurrent::run([=]() {
368 | LabCD::_clearEmptyMask(t1List, t2List, GTList); });
369 | messageState->setText(tr("清理完成"));
370 | }
371 | }
372 |
373 | void LabCD::_clearEmptyMask(
374 | QStringList t1List, QStringList t2List, QStringList GTList)
375 | {
376 | std::sort(t1List.begin(), t1List.end());
377 | std::sort(t2List.begin(), t2List.end());
378 | std::sort(GTList.begin(), GTList.end());
379 | QFileInfo fInfo;
380 | QString pathName;
381 | for (int i = 0; i < GTList.size(); ++i)
382 | {
383 | if (ImagePress::maskIsEmpty(GTList.at(i)))
384 | {
385 | // 清理图像
386 | QFile::remove(t1List.at(i));
387 | QFile::remove(t2List.at(i));
388 | // 清理标签
389 | fInfo = QFileInfo(GTList.at(i));
390 | pathName = fInfo.path() + "/" + fInfo.baseName();
391 | QFile::remove(GTList.at(i));
392 | QFile::remove(pathName + ".json");
393 | QFile::remove(pathName + "_pseudo.png");
394 | }
395 | }
396 | }
397 |
398 | void LabCD::convertMask2Json()
399 | {
400 | QString dirPath = QFileDialog::getExistingDirectory(
401 | this,
402 | QObject::tr("打开标签文件夹"),
403 | QString(),
404 | QFileDialog::ShowDirsOnly
405 | );
406 | if (dirPath.isEmpty())
407 | return;
408 | QtConcurrent::run([=]() { LabCD::_convertMask2Json(dirPath); });
409 | messageState->setText(tr("转换完成"));
410 | }
411 |
412 | void LabCD::_convertMask2Json(QString dirPath)
413 | {
414 | QDir maskDir(dirPath);
415 | QStringList nameFilters;
416 | nameFilters << "*.jpg" << "*.jpeg" << "*.png" << "*.tif" << "*.tiff";
417 | QStringList maskList = (maskDir).entryList(
418 | nameFilters, QDir::Readable | QDir::Files, QDir::Name);
419 | QString maskPath;
420 | for (int i = 0; i < maskList.size(); ++i)
421 | {
422 | maskPath = dirPath + "/" + maskList.at(i);
423 | ImagePress::savePolygonFromMask(maskPath);
424 | }
425 | }
426 |
427 | void LabCD::save()
428 | {
429 | if (drawCanvas->imageWidth != 0 && drawCanvas->imageHeight != 0)
430 | {
431 | QString saveImgPath = savePath + "/GT/" + fileName;
432 | int labNum = labTableWidget->getLen();
433 | drawCanvas->finished();
434 | ImagePress::saveResultFromPolygon(
435 | saveImgPath,
436 | labNum,
437 | drawCanvas->imageHeight,
438 | drawCanvas->imageWidth,
439 | drawCanvas->t1Canva->aScene->polygonItems,
440 | drawCanvas->projs,
441 | drawCanvas->trans
442 | );
443 | fListWidget->finishedCurrentItem();
444 | messageState->setText(tr("保存图像:") + saveImgPath);
445 | fListWidget->progressUpAdd();
446 | }
447 | }
448 |
449 | void LabCD::setCrossPenColor()
450 | {
451 | QColor nowCrossColor = drawCanvas->getCrossPenColor();
452 | QColor color = QColorDialog::getColor(
453 | nowCrossColor, this, tr("设置十字丝颜色"), QColorDialog::ShowAlphaChannel
454 | );
455 | setting->setValue("cross_color", color);
456 | drawCanvas->setCrossPenColor(color);
457 | }
458 |
459 | void LabCD::updatePolysColor()
460 | {
461 | for (int i = 0; i < drawCanvas->t1Canva->aScene->polygonItems.count(); ++i)
462 | {
463 | int idx = drawCanvas->t1Canva->aScene->polygonItems[i]->getLabelIndex();
464 | QColor polyColor = drawCanvas->t1Canva->aScene->polygonItems[i]->getColor();
465 | QColor labColor = labTableWidget->getColorByIndex(idx);
466 | if (polyColor.rgb() != labColor.rgb())
467 | {
468 | drawCanvas->t1Canva->aScene->polygonItems[i]->setColor(
469 | labColor, labColor);
470 | drawCanvas->t2Canva->aScene->polygonItems[i]->setColor(
471 | labColor, labColor);
472 | }
473 | }
474 | }
475 |
476 | void LabCD::closeEvent(QCloseEvent* ev)
477 | {
478 | // 保存标签
479 | if (savePath != "")
480 | {
481 | QString jsonPath = savePath + "/label.json";
482 | labTableWidget->exportLabelToFile(jsonPath);
483 | }
484 | // 保存界面
485 | setting->setValue("layout_status", QByteArray(saveState()));
486 | QMainWindow::closeEvent(ev);
487 | }
488 |
--------------------------------------------------------------------------------
/labcd/labcd.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "widgets/labeltable.h"
7 | #include "widgets/filelist.h"
8 | #include "widgets/multcanvas.h"
9 |
10 | class LabCD : public QMainWindow
11 | {
12 | Q_OBJECT
13 |
14 | private:
15 | QSettings* setting;
16 | QString savePath = "";
17 | QString fileName;
18 | bool isCN;
19 | const int refNewWidth = 200;
20 | int refNewHeight = 200;
21 |
22 | void openDir();
23 | void openBigImageFile();
24 | void mergeBigImage();
25 | void clearEmptyMask();
26 | inline void _clearEmptyMask(
27 | QStringList t1List, QStringList t2List, QStringList GTList);
28 | void convertMask2Json();
29 | inline void _convertMask2Json(QString dirPath);
30 | void save();
31 | void setCrossPenColor();
32 | void updatePolysColor();
33 |
34 | public:
35 | FileList* fListWidget = nullptr; // 数据列表
36 | LabelTable* labTableWidget = nullptr; // 标签列表
37 | MultCanvas* drawCanvas = nullptr; // 绘图界面
38 | QLabel* messageState = nullptr; // 消息框
39 |
40 | LabCD(QWidget* parent = nullptr);
41 | ~LabCD();
42 | void closeEvent(QCloseEvent* ev);
43 | };
44 |
--------------------------------------------------------------------------------
/labcd/labcd.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | resources/Icon.png
4 |
5 |
6 | resources/Folder.png
7 | resources/Github.png
8 | resources/Help.png
9 | resources/Chinese.png
10 | resources/English.png
11 | resources/Split.png
12 | resources/Merge.png
13 | resources/ClearMask.png
14 | resources/Convert.png
15 |
16 |
17 | resources/Enlarge.png
18 | resources/Full.png
19 | resources/Narrow.png
20 | resources/Last.png
21 | resources/Next.png
22 | resources/Save.png
23 | resources/DeleteAllPolygons.png
24 | resources/DeletePolygon.png
25 | resources/Color.png
26 | resources/Reference.png
27 |
28 |
29 | resources/AddLabel.png
30 | resources/Delete.png
31 | resources/CantDelete.png
32 |
33 |
34 | configs/colormap.txt
35 |
36 |
37 | i18n/English.qm
38 |
39 |
40 |
--------------------------------------------------------------------------------
/labcd/main.cpp:
--------------------------------------------------------------------------------
1 | #include "labcd.h"
2 | #include
3 | #include
4 | #include
5 |
6 | #include // debug
7 |
8 | int main(int argc, char* argv[])
9 | {
10 | QApplication a(argc, argv);
11 | QSettings setting("./configs/setting.ini", QSettings::IniFormat);
12 | // 居中显示
13 | QScreen* scr = a.primaryScreen();
14 | int scr_w = scr->size().width();
15 | int scr_h = scr->size().height();
16 | // 加载语言
17 | QString langu = setting.value("language").toString();
18 | QTranslator translator;
19 | if (langu == "EN")
20 | {
21 | translator.load(":/translate/i18n/English.qm");
22 | a.installTranslator(&translator);
23 | }
24 | else
25 | a.removeTranslator(&translator);
26 | // 重加载界面和设置
27 | LabCD w;
28 | w.drawCanvas->setCrossPenColor(setting.value("cross_color").value());
29 | QByteArray layoutStatus = setting.value("layout_status").toByteArray();
30 | w.restoreState(layoutStatus);
31 | w.move((scr_w - w.width()) / 2, (scr_h - w.height()) / 2);
32 | w.show();
33 | return a.exec();
34 | }
35 |
--------------------------------------------------------------------------------
/labcd/resources/AddLabel.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/AddLabel.png
--------------------------------------------------------------------------------
/labcd/resources/CantDelete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/CantDelete.png
--------------------------------------------------------------------------------
/labcd/resources/Chinese.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Chinese.png
--------------------------------------------------------------------------------
/labcd/resources/ClearMask.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/ClearMask.png
--------------------------------------------------------------------------------
/labcd/resources/Color.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Color.png
--------------------------------------------------------------------------------
/labcd/resources/Convert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Convert.png
--------------------------------------------------------------------------------
/labcd/resources/Delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Delete.png
--------------------------------------------------------------------------------
/labcd/resources/DeleteAllPolygons.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/DeleteAllPolygons.png
--------------------------------------------------------------------------------
/labcd/resources/DeletePolygon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/DeletePolygon.png
--------------------------------------------------------------------------------
/labcd/resources/English.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/English.png
--------------------------------------------------------------------------------
/labcd/resources/Enlarge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Enlarge.png
--------------------------------------------------------------------------------
/labcd/resources/Folder.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Folder.png
--------------------------------------------------------------------------------
/labcd/resources/Full.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Full.png
--------------------------------------------------------------------------------
/labcd/resources/Github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Github.png
--------------------------------------------------------------------------------
/labcd/resources/Help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Help.png
--------------------------------------------------------------------------------
/labcd/resources/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Icon.png
--------------------------------------------------------------------------------
/labcd/resources/Last.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Last.png
--------------------------------------------------------------------------------
/labcd/resources/Merge.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Merge.png
--------------------------------------------------------------------------------
/labcd/resources/Narrow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Narrow.png
--------------------------------------------------------------------------------
/labcd/resources/Next.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Next.png
--------------------------------------------------------------------------------
/labcd/resources/Reference.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Reference.png
--------------------------------------------------------------------------------
/labcd/resources/Save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Save.png
--------------------------------------------------------------------------------
/labcd/resources/Split.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/geoyee/LabCD/88e0b4080a96109d41db53ff647b1768d621fa20/labcd/resources/Split.png
--------------------------------------------------------------------------------
/labcd/utils/colormap.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "colormap.h"
3 |
4 | int* ColorMap::strToColor(std::string& str)
5 | {
6 | int* color = new int[3];
7 | std::size_t startIdx = 0;
8 | std::size_t endIdx = 0;
9 | std::string strs = str + ",";
10 | for (int i = 0; i < 3; i++)
11 | {
12 | endIdx = strs.find(",", startIdx);
13 | std::string cItem = strs.substr(startIdx, endIdx - startIdx);
14 | startIdx = endIdx + 1;
15 | color[i] = atoi(cItem.c_str());
16 | }
17 | return color;
18 | }
19 |
20 | ColorMap::ColorMap()
21 | {
22 | index = 0;
23 | QFile file(":/configs/configs/colormap.txt");
24 | file.open(QIODevice::ReadOnly);
25 | while (!file.atEnd())
26 | {
27 | QByteArray data = file.readLine();
28 | std::string buff = QString(data).toStdString();
29 | int* c = ColorMap::strToColor(buff);
30 | colorList.push_back(c);
31 | }
32 | file.close();
33 | }
34 |
35 | ColorMap::~ColorMap()
36 | {
37 |
38 | }
39 |
40 | void ColorMap::setIndex(int _index)
41 | {
42 | index = _index;
43 | }
44 |
45 | QColor ColorMap::getColor()
46 | {
47 | if (index >= colorList.size())
48 | index = 0;
49 | int* c = colorList[index];
50 | index++;
51 | return QColor(c[0], c[1], c[2]);
52 | }
53 |
54 | QColor ColorMap::getColor(int index)
55 | {
56 | if (index >= colorList.size())
57 | index = 0;
58 | int* c = colorList[index];
59 | return QColor(c[0], c[1], c[2]);
60 | }
61 |
--------------------------------------------------------------------------------
/labcd/utils/colormap.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | class ColorMap
9 | {
10 | private:
11 | int index;
12 | std::vector colorList;
13 |
14 | int* strToColor(std::string& str);
15 |
16 | public:
17 | ColorMap();
18 | ~ColorMap();
19 | void setIndex(int _index);
20 | QColor getColor();
21 | QColor getColor(int index);
22 | };
23 |
--------------------------------------------------------------------------------
/labcd/utils/fileworker.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "fileworker.h"
5 |
6 | bool FileWorker::openImageDir(
7 | QStringList* t1List,
8 | QStringList* t2List,
9 | QStringList* GTList,
10 | QWidget* parent
11 | )
12 | {
13 | QString dirPath = QFileDialog::getExistingDirectory(
14 | parent,
15 | QObject::tr("打开图像文件夹"),
16 | QString(),
17 | QFileDialog::ShowDirsOnly
18 | );
19 | if (dirPath.isEmpty())
20 | return false;
21 | // 开始读取
22 | QDir dir(dirPath);
23 | QStringList subDirList;
24 | subDirList = dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs, QDir::Name);
25 | if ((subDirList.size() != 2 && subDirList.size() != 3) ||
26 | (subDirList.size() == 2 && subDirList.at(0) != "A" && subDirList.at(1) != "B") ||
27 | (subDirList.size() == 3 && subDirList.at(0) != "A" && subDirList.at(1) != "B" && subDirList.at(2) != "GT"))
28 | {
29 | QMessageBox::critical(
30 | parent,
31 | QObject::tr("错误"),
32 | QObject::tr("文件组织格式错误,请确保数据文件夹下仅存在两个(或三个)子文件夹,分别为A、B(以及GT)。")
33 | );
34 | return false;
35 | }
36 | else // 获取所有图像
37 | {
38 | QStringList nameFilters;
39 | nameFilters << "*.jpg" << "*.jpeg" << "*.png" << "*.tif" << "*.tiff";
40 | QDir dirT1(dirPath + "/" + subDirList.at(0));
41 | QStringList t1ListTmp = (dirT1).entryList(
42 | nameFilters, QDir::Readable | QDir::Files, QDir::Name);
43 | QDir dirT2(dirPath + "/" + subDirList.at(1));
44 | QStringList t2ListTmp = (dirT2).entryList(
45 | nameFilters, QDir::Readable | QDir::Files, QDir::Name);
46 | if (t1ListTmp.size() != t2ListTmp.size())
47 | {
48 | QMessageBox::critical(
49 | parent,
50 | QObject::tr("错误"),
51 | QObject::tr("时段一数据和时段二数据数量不相等。")
52 | );
53 | return false;
54 | }
55 | if (GTList != nullptr && subDirList.size() == 3)
56 | {
57 | QDir dirGT(dirPath + "/" + subDirList.at(2));
58 | QStringList GTListTmp = (dirGT).entryList(
59 | nameFilters, QDir::Readable | QDir::Files, QDir::Name);
60 | QString name;
61 | QStringList nameFlag;
62 | for (int i = 0; i < GTListTmp.size(); ++i)
63 | {
64 | name = GTListTmp.at(i);
65 | nameFlag = name.split("_");
66 | if (nameFlag[nameFlag.size() - 1] == "pseudo.png")
67 | continue;
68 | GTList->append(
69 | dirPath + "/" + subDirList.at(2) + "/" + name);
70 | }
71 | }
72 | for (int i = 0; i < t1ListTmp.size(); ++i)
73 | {
74 | t1List->append(
75 | dirPath + "/" + subDirList.at(0) + "/" + t1ListTmp.at(i));
76 | t2List->append(
77 | dirPath + "/" + subDirList.at(1) + "/" + t2ListTmp.at(i));
78 | }
79 | return true;
80 | }
81 | }
82 |
83 | bool FileWorker::createFolder(QString path)
84 | {
85 | QDir* folder = new QDir(path);
86 | if (!folder->exists())
87 | {
88 | if (folder->mkpath(path))
89 | return true;
90 | }
91 | return true;
92 | }
93 |
--------------------------------------------------------------------------------
/labcd/utils/fileworker.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | class FileWorker
6 | {
7 | public:
8 | static bool openImageDir(
9 | QStringList* t1List,
10 | QStringList* t2List,
11 | QStringList* GTList = nullptr,
12 | QWidget* parent = nullptr
13 | );
14 |
15 | static bool createFolder(QString path);
16 | };
17 |
--------------------------------------------------------------------------------
/labcd/utils/imgpress.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "colormap.h"
9 | #include "imgpress.h"
10 |
11 | bool ImagePress::createArr(
12 | void** data, GDALDataType types, int xSize, int ySize, int band)
13 | {
14 | switch (types)
15 | {
16 | case GDT_Byte:
17 | *data = new unsigned char[xSize * ySize * band];
18 | break;
19 | case GDT_UInt16:
20 | *data = new unsigned short[xSize * ySize * band];
21 | break;
22 | case GDT_Int16:
23 | *data = new short[xSize * ySize * band];
24 | break;
25 | case GDT_UInt32:
26 | *data = new unsigned long[xSize * ySize * band];
27 | break;
28 | case GDT_Int32:
29 | *data = new long[xSize * ySize * band];
30 | break;
31 | case GDT_Float32:
32 | *data = new float[xSize * ySize * band];
33 | break;
34 | case GDT_Float64:
35 | *data = new double[xSize * ySize * band];
36 | break;
37 | default:
38 | return false;
39 | }
40 | return true;
41 | }
42 |
43 | unsigned char* ImagePress::imgSketch(
44 | float* buffer,
45 | GDALRasterBand* currentBand,
46 | int bandSize,
47 | double noValue
48 | )
49 | {
50 | unsigned char* resBuffer = new unsigned char[bandSize];
51 | double max, min;
52 | double minmax[2];
53 | currentBand->ComputeRasterMinMax(1, minmax);
54 | min = minmax[0];
55 | max = minmax[1];
56 | if (min <= noValue && noValue <= max)
57 | min = 0;
58 | for (int i = 0; i < bandSize; ++i)
59 | {
60 | if (buffer[i] > max)
61 | resBuffer[i] = 255;
62 | else if (buffer[i] <= max && buffer[i] >= min)
63 | resBuffer[i] = static_cast(
64 | 255 - 255 * (max - buffer[i]) / (max - min));
65 | else
66 | resBuffer[i] = 0;
67 | }
68 | return resBuffer;
69 | }
70 |
71 | QPixmap ImagePress::GDALRastertoPixmap(QList* imgBand)
72 | {
73 | rsize_t imgWidth = imgBand->at(0)->GetXSize();
74 | rsize_t imgHeight = imgBand->at(0)->GetYSize();
75 | GDALDataType dataType = imgBand->at(0)->GetRasterDataType();
76 | // 首先分别读取RGB三个波段
77 | float* rBand = new float[imgWidth * imgHeight];
78 | float* gBand = new float[imgWidth * imgHeight];
79 | float* bBand = new float[imgWidth * imgHeight];
80 | unsigned char* rBandUC, * gBandUC, * bBandUC;
81 | imgBand->at(0)->RasterIO(
82 | GF_Read, 0, 0, imgWidth, imgHeight, rBand,
83 | imgWidth, imgHeight, GDT_Float32, 0, 0
84 | );
85 | imgBand->at(1)->RasterIO(
86 | GF_Read, 0, 0, imgWidth, imgHeight, gBand,
87 | imgWidth, imgHeight, GDT_Float32, 0, 0
88 | );
89 | imgBand->at(2)->RasterIO(
90 | GF_Read, 0, 0, imgWidth, imgHeight, bBand,
91 | imgWidth, imgHeight, GDT_Float32, 0, 0
92 | );
93 | // 分别拉伸每个波段并将Float转换为unsigned char
94 | rBandUC = imgSketch(
95 | rBand, imgBand->at(0), imgWidth * imgHeight,
96 | imgBand->at(0)->GetNoDataValue()
97 | );
98 | gBandUC = imgSketch(
99 | gBand, imgBand->at(1), imgWidth * imgHeight,
100 | imgBand->at(1)->GetNoDataValue()
101 | );
102 | bBandUC = imgSketch(
103 | bBand, imgBand->at(2), imgWidth * imgHeight,
104 | imgBand->at(2)->GetNoDataValue()
105 | );
106 | // 将三个波段组合起来
107 | int bytePerLine = (imgWidth * 24 + 31) / 8;
108 | unsigned char* allBandUC = new unsigned char[bytePerLine * imgHeight * 3];
109 | for (int h = 0; h < imgHeight; ++h)
110 | {
111 | for (int w = 0; w < imgWidth; ++w)
112 | {
113 | allBandUC[h * bytePerLine + w * 3 + 0] = rBandUC[h * imgWidth + w];
114 | allBandUC[h * bytePerLine + w * 3 + 1] = gBandUC[h * imgWidth + w];
115 | allBandUC[h * bytePerLine + w * 3 + 2] = bBandUC[h * imgWidth + w];
116 | }
117 | }
118 | return QPixmap::fromImage(
119 | QImage(allBandUC, imgWidth, imgHeight, bytePerLine, QImage::Format_RGB888));
120 | }
121 |
122 | bool ImagePress::saveTiffFromGDAL(
123 | std::string savePath,
124 | void* img,
125 | int nImgSizeX,
126 | int nImgSizeY,
127 | int nChannel,
128 | GDALDataType types,
129 | const char* projs,
130 | double* trans,
131 | int* pBandMaps
132 | )
133 | {
134 | GDALDriver* pDriverMEM = GetGDALDriverManager()->GetDriverByName("GTiff");
135 | if (!pDriverMEM)
136 | {
137 | GDALDestroyDriverManager();
138 | return false;
139 | }
140 | GDALDataset* poDataset = pDriverMEM->Create(
141 | savePath.c_str(), nImgSizeX, nImgSizeY, nChannel, types, NULL);
142 | poDataset->SetProjection(projs);
143 | poDataset->SetGeoTransform(trans);
144 | if (!poDataset)
145 | {
146 | GDALClose(poDataset);
147 | GDALDestroyDriverManager();
148 | return false;
149 | }
150 | poDataset->RasterIO(
151 | GF_Write, 0, 0, nImgSizeX, nImgSizeY, img, nImgSizeX, nImgSizeY,
152 | types, nChannel, pBandMaps, 0, 0, 0, NULL
153 | );
154 | GDALClose(poDataset);
155 | return true;
156 | }
157 |
158 | bool ImagePress::saveTiffFromCVMask(
159 | std::string savePath,
160 | cv::Mat mask,
161 | const char* projs,
162 | double* trans
163 | )
164 | {
165 | int nImgSizeX = mask.cols;
166 | int nImgSizeY = mask.rows;
167 | GDALDriver* pDriverMEM = GetGDALDriverManager()->GetDriverByName("GTiff");
168 | if (!pDriverMEM)
169 | {
170 | GDALDestroyDriverManager();
171 | return false;
172 | }
173 | GDALDataset* poDataset = pDriverMEM->Create(
174 | savePath.c_str(), nImgSizeX, nImgSizeY, 1, GDT_Byte, NULL);
175 | poDataset->SetProjection(projs);
176 | poDataset->SetGeoTransform(trans);
177 | if (!poDataset)
178 | {
179 | GDALClose(poDataset);
180 | GDALDestroyDriverManager();
181 | return false;
182 | }
183 | poDataset->GetRasterBand(1)->RasterIO(
184 | GF_Write, 0, 0, nImgSizeX, nImgSizeY, mask.data,
185 | nImgSizeX, nImgSizeY, GDT_Byte, 0, 0, NULL
186 | );
187 | GDALClose(poDataset);
188 | return true;
189 | }
190 |
191 | void ImagePress::calcWindowTrans(double trans[6], int locX, int locY)
192 | {
193 | trans[0] += locX * trans[1];
194 | trans[3] += locY * trans[5];
195 | }
196 |
197 | cv::Mat ImagePress::CVA(cv::Mat t1, cv::Mat t2)
198 | {
199 | float eps = 1e-12;
200 | std::vector t1Channels;
201 | cv::split(t1, t1Channels);
202 | std::vector t2Channels;
203 | cv::split(t2, t2Channels);
204 | int m = t1.rows;
205 | int n = t1.cols;
206 | cv::Mat intensity = cv::Mat::zeros(m, n, CV_64FC1);
207 | for (int i = 0; i < t1Channels.size(); ++i)
208 | {
209 | cv::Mat diff = t1Channels[i] - t2Channels[i];
210 | cv::pow(diff, 2, diff);
211 | intensity += diff;
212 | }
213 | cv::pow(intensity, 0.5, intensity);
214 | // 变化强度
215 | double amin = 0, amax = 0;
216 | cv::Point minPt, maxPt;
217 | minMaxLoc(intensity, &amin, &amax, &minPt, &maxPt);
218 | intensity = 255.0f * (intensity - amin) / (amax - amin);
219 | // 伪彩色渲染
220 | intensity.convertTo(intensity, CV_8UC1);
221 | cv::applyColorMap(intensity, intensity, cv::COLORMAP_HOT);
222 | return intensity;
223 | }
224 |
225 | void ImagePress::saveResultFromPolygon(
226 | QString savePath,
227 | int labNum,
228 | int imgHeight,
229 | int imgWidth,
230 | QList polygons,
231 | std::string projs,
232 | double* trans
233 | )
234 | {
235 | // 处理保存路径
236 | QStringList pathAndName = savePath.split(".");
237 | std::string iPath = pathAndName[0].toStdString();
238 | std::string iExt = pathAndName[1].toStdString();
239 | cv::String pseudoSavaPath = iPath + "_pseudo.png"; // 保存伪彩色
240 | std::string jsonSavePath = iPath + ".json"; // 保存json
241 | // 新建保存的图像
242 | cv::Mat pseudoResult = cv::Mat::zeros(cv::Size(imgWidth, imgHeight), CV_8UC3);
243 | cv::Mat result = cv::Mat::zeros(cv::Size(imgWidth, imgHeight), CV_8UC1);
244 | // 新建保存的json
245 | Json::StyledWriter writer;
246 | Json::Value polyList;
247 | std::ofstream os;
248 | os.open(jsonSavePath);
249 | // 标号小的覆盖标号大的
250 | for (int labIndex = labNum - 1; labIndex >= 0; labIndex--)
251 | {
252 | for (LabPolygon* poly : polygons)
253 | {
254 | if (poly->labelIndex == labIndex)
255 | {
256 | Json::Value polygons;
257 | polygons["labelIndex"] = poly->labelIndex;
258 | QColor c = poly->getColor();
259 | cv::Scalar color = cv::Scalar(c.blue(), c.green(), c.red());
260 | polygons["color"]["R"] = c.red();
261 | polygons["color"]["G"] = c.green();
262 | polygons["color"]["B"] = c.blue();
263 | const int numPoint = poly->mPoints.count();
264 | polygons["polygon"]["pointNumber"] = numPoint;
265 | cv::Point** cvPoints = new cv::Point * [1];
266 | cvPoints[0] = new cv::Point[numPoint];
267 | Json::Value points;
268 | for (int i = 0; i < numPoint; ++i)
269 | {
270 | cvPoints[0][i] = cv::Point(
271 | poly->mPoints.at(i)->x(), poly->mPoints.at(i)->y());
272 | points.append(poly->mPoints.at(i)->x());
273 | points.append(poly->mPoints.at(i)->y());
274 | }
275 | const cv::Point* ppt[1] = { cvPoints[0] };
276 | int npt[] = { numPoint };
277 | cv::fillPoly(pseudoResult, ppt, npt, 1, color);
278 | cv::fillPoly(result, ppt, npt, 1, cv::Scalar(labIndex));
279 | polygons["polygon"]["points"] = points;
280 | polyList.append(polygons);
281 | // 清除数组
282 | delete[] cvPoints[0];
283 | delete[] cvPoints;
284 | }
285 | }
286 | }
287 | // 保存
288 | cv::imwrite(pseudoSavaPath, pseudoResult);
289 | if (iExt == "jpg" || iExt == "jpeg" || iExt == "png")
290 | {
291 | cv::String baseSavaPath = iPath + ".bmp";
292 | cv::imwrite(baseSavaPath, result);
293 | }
294 | else
295 | {
296 | std::string baseSavaPath = iPath + ".tif";
297 | GDALAllRegister();
298 | CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
299 | saveTiffFromCVMask(baseSavaPath, result, projs.c_str(), trans);
300 | GDALDestroyDriverManager();
301 | }
302 | os << writer.write(polyList);
303 | os.close(); // 关闭json
304 | }
305 |
306 | std::vector ImagePress::calcUnique(cv::Mat mask)
307 | {
308 | auto begin = mask.begin(), end = mask.end();
309 | auto last = std::unique(begin, end);
310 | std::sort(begin, last);
311 | last = std::unique(begin, last);
312 | return std::vector(begin, last);
313 | }
314 |
315 | void ImagePress::savePolygonFromMask(QString maskPath)
316 | {
317 | // 新建保存的json
318 | QStringList pathAndName = maskPath.split(".");
319 | std::string iPath = pathAndName[0].toStdString();
320 | std::string jsonSavePath = iPath + ".json";
321 | Json::StyledWriter writer;
322 | Json::Value polyList;
323 | std::ofstream os;
324 | os.open(jsonSavePath);
325 | // 读取图像并保存json
326 | cv::Mat mat = cv::imread(maskPath.toStdString());
327 | cv::cvtColor(mat, mat, cv::COLOR_BGR2GRAY);
328 | cv::Mat uMat;
329 | mat.copyTo(uMat);
330 | std::vector unique = ImagePress::calcUnique(uMat);
331 | std::sort(unique.begin(), unique.end());
332 | // 处理两类带255
333 | if (unique.size() == 2 && unique.at(1) == 255)
334 | {
335 | unique.at(1) = 1;
336 | cv::convertScaleAbs(mat, mat, 1.0 / 255, 0);
337 | }
338 | // 多类处理
339 | ColorMap cMap;
340 | QColor rgb;
341 | for (int i = 1; i < unique.size(); ++i)
342 | {
343 | int clas = static_cast(unique.at(i));
344 | rgb = cMap.getColor(clas - 1);
345 | // 创建一个全为0的输出矩阵,与输入矩阵具有相同的大小和类型
346 | cv::Mat clasMat = cv::Mat::zeros(mat.size(), CV_8UC1);
347 | // 比较输入矩阵与所需值,将比较结果存储在输出矩阵中
348 | cv::compare(mat, clas, clasMat, cv::CMP_EQ);
349 | std::vector< std::vector > contours;
350 | std::vector hierarchy;
351 | cv::findContours(
352 | clasMat,
353 | contours,
354 | hierarchy,
355 | cv::RETR_TREE,
356 | cv::CHAIN_APPROX_TC89_KCOS
357 | );
358 | std::vector contourPolys;
359 | double epsilon;
360 | for (size_t i = 0; i < hierarchy.size(); i++)
361 | {
362 | epsilon = 0.001 * cv::arcLength(cv::Mat(contours[i]), true);
363 | cv::approxPolyDP(cv::Mat(contours[i]), contourPolys, epsilon, true);
364 | Json::Value polygons;
365 | if (hierarchy[i][3] < 0) // 没有父轮廓
366 | {
367 | polygons["labelIndex"] = clas;
368 | polygons["color"]["R"] = rgb.red();
369 | polygons["color"]["G"] = rgb.green();
370 | polygons["color"]["B"] = rgb.blue();
371 | }
372 | else // 孔洞
373 | {
374 | polygons["labelIndex"] = 0;
375 | polygons["color"]["R"] = 0;
376 | polygons["color"]["G"] = 0;
377 | polygons["color"]["B"] = 0;
378 | }
379 | polygons["polygon"]["pointNumber"] = contourPolys.size();
380 | Json::Value points;
381 | for (cv::Point p : contourPolys)
382 | {
383 | points.append(p.x);
384 | points.append(p.y);
385 | }
386 | polygons["polygon"]["points"] = points;
387 | polyList.append(polygons);
388 | }
389 | }
390 | os << writer.write(polyList);
391 | os.close(); // 关闭json
392 | }
393 |
394 | bool ImagePress::openImage(
395 | QString imgPath,
396 | QPixmap& img,
397 | std::string& projs,
398 | double trans[6]
399 | )
400 | {
401 | QFileInfo fileInfo(imgPath);
402 | QString ext = fileInfo.completeSuffix();
403 | QList qtSupporExts = QImageReader::supportedImageFormats();
404 | if (qtSupporExts.contains(ext.toLocal8Bit()))
405 | {
406 | img.load(imgPath);
407 | return true;
408 | }
409 | else // 使用gdal打开
410 | {
411 | GDALAllRegister(); // 注册所有的驱动
412 | CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO"); // 设置支持中文路径和文件名
413 | GDALDataset* poDataset = (GDALDataset*)GDALOpen(
414 | imgPath.toStdString().c_str(), GA_ReadOnly);
415 | if (poDataset == NULL)
416 | {
417 | GDALClose(poDataset);
418 | GDALDestroyDriverManager();
419 | return false;
420 | }
421 | int bandCount = poDataset->GetRasterCount();
422 | QList bandList;
423 | static std::vector HSI_RGB_LOAD;
424 | static int NOW_BAND_COUNT;
425 | if (bandCount < 3) // 小于3个波段只加载第一个波段
426 | {
427 | bandList.append(poDataset->GetRasterBand(1));
428 | bandList.append(poDataset->GetRasterBand(1));
429 | bandList.append(poDataset->GetRasterBand(1));
430 | }
431 | else if (bandCount == 3) // RGB直接加载
432 | {
433 | bandList.append(poDataset->GetRasterBand(1));
434 | bandList.append(poDataset->GetRasterBand(2));
435 | bandList.append(poDataset->GetRasterBand(3));
436 | }
437 | else // 多光谱计算oif再加载
438 | {
439 | // 只计算一次
440 | if (HSI_RGB_LOAD.empty() || bandCount != NOW_BAND_COUNT)
441 | HSI_RGB_LOAD = ImagePress::calcOIF(imgPath);
442 | bandList.append(poDataset->GetRasterBand(HSI_RGB_LOAD[0]));
443 | bandList.append(poDataset->GetRasterBand(HSI_RGB_LOAD[1]));
444 | bandList.append(poDataset->GetRasterBand(HSI_RGB_LOAD[2]));
445 | }
446 | NOW_BAND_COUNT = bandCount;
447 | projs = poDataset->GetProjectionRef();
448 | poDataset->GetGeoTransform(trans);
449 | img = QPixmap(GDALRastertoPixmap(&bandList));
450 | // 注销驱动
451 | GDALClose(poDataset);
452 | GDALDestroyDriverManager();
453 | return true;
454 | }
455 | }
456 |
457 | bool ImagePress::splitTiff(
458 | QString imgPath,
459 | QString saveDir,
460 | int blockHeight,
461 | int blockWidth
462 | )
463 | {
464 | // 获取文件名
465 | QString name = QFileInfo(imgPath).fileName().split(".")[0];
466 | // 读取数据解析属性
467 | GDALAllRegister();
468 | CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
469 | GDALDataset* poDataset = (GDALDataset*)GDALOpen(
470 | imgPath.toStdString().c_str(), GA_ReadOnly);
471 | if (poDataset == NULL)
472 | {
473 | GDALClose(poDataset);
474 | GDALDestroyDriverManager();
475 | return false;
476 | }
477 | int bandCount = poDataset->GetRasterCount();
478 | rsize_t nYSize = poDataset->GetRasterYSize();
479 | rsize_t nXSize = poDataset->GetRasterXSize();
480 | GDALDataType types = poDataset->GetRasterBand(1)->GetRasterDataType();
481 | double trans[6] = { 0 };
482 | double windowTrans[6] = { 0 };
483 | poDataset->GetGeoTransform(trans);
484 | const char* projs = poDataset->GetProjectionRef();
485 | // 定义读取输入图像波段顺序
486 | int* pBandMaps = new int[bandCount];
487 | for (int b = 0; b < bandCount; ++b)
488 | pBandMaps[b] = b + 1;
489 | // 循环分块并进行处理
490 | if (blockHeight > nXSize || blockWidth > nYSize)
491 | return false;
492 | void* pSrcData = nullptr;
493 | ImagePress::createArr(&pSrcData, types, blockWidth, blockHeight, bandCount);
494 | int row = 0;
495 | int tmpI, tmpJ;
496 | for (rsize_t i = 0; i < nYSize; i += blockWidth)
497 | {
498 | int col = 0;
499 | for (rsize_t j = 0; j < nXSize; j += blockHeight)
500 | {
501 | tmpI = i;
502 | tmpJ = j;
503 | if (i + blockHeight > nYSize)
504 | tmpI = nYSize - blockHeight;
505 | if (j + blockWidth > nXSize)
506 | tmpJ = nXSize - blockWidth;
507 | // 读取原始图像块
508 | poDataset->RasterIO(
509 | GF_Read, tmpJ, tmpI, blockWidth, blockHeight, pSrcData,
510 | blockWidth, blockHeight, types, bandCount, pBandMaps, 0, 0, 0, NULL
511 | );
512 | // 保存
513 | std::string windowSavePath = (saveDir + QDir::separator() + name + \
514 | "_" + QString::number(tmpJ) + \
515 | "_" + QString::number(tmpI) + ".tif").toStdString();
516 | std::copy(std::begin(trans), std::end(trans), std::begin(windowTrans));
517 | calcWindowTrans(windowTrans, col * blockWidth, row * blockHeight);
518 | if (!saveTiffFromGDAL(windowSavePath, pSrcData, blockWidth, blockHeight,
519 | bandCount, types, projs, windowTrans, pBandMaps))
520 | {
521 | delete[] pSrcData;
522 | GDALClose(poDataset);
523 | GDALDestroyDriverManager();
524 | return false;
525 | }
526 | col++;
527 | }
528 | row++;
529 | }
530 | delete[] pSrcData;
531 | GDALClose(poDataset);
532 | GDALDestroyDriverManager();
533 | return true;
534 | }
535 |
536 | bool ImagePress::mergeTiff(QString imgDir)
537 | {
538 | // 读取
539 | if (imgDir.isEmpty()) return false;
540 | QDir dir(imgDir);
541 | dir.setFilter(QDir::Files);
542 | dir.setSorting(QDir::Name);
543 | dir.setNameFilters(QString("*.tiff;*.tif").split(";"));
544 | QStringList subDirList = dir.entryList();
545 | std::sort(subDirList.begin(), subDirList.end(),
546 | [](const QString& s1, const QString& s2)
547 | {
548 | QStringList fi1 = QFileInfo(s1).baseName().split("_");
549 | QStringList fi2 = QFileInfo(s2).baseName().split("_");
550 | int j1 = fi1.at(fi1.size() - 2).toInt();
551 | int i1 = fi1.at(fi1.size() - 1).toInt();
552 | int j2 = fi2.at(fi1.size() - 2).toInt();
553 | int i2 = fi2.at(fi1.size() - 1).toInt();
554 | if (j1 < j2) return true;
555 | else if (j1 > j2) return false;
556 | else
557 | {
558 | if (i1 < i2) return true;
559 | else return false;
560 | }
561 | }
562 | );
563 | GDALAllRegister();
564 | CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
565 | // 读取第一个块,获取信息
566 | GDALDataset* tmpDataset = (GDALDataset*)GDALOpen(
567 | (imgDir + QDir::separator() + subDirList.at(0)).toStdString().c_str(),
568 | GA_ReadOnly
569 | );
570 | if (tmpDataset == NULL)
571 | {
572 | GDALClose(tmpDataset);
573 | GDALDestroyDriverManager();
574 | return false;
575 | }
576 | int bandCount = tmpDataset->GetRasterCount();
577 | int* pBandMaps = new int[bandCount];
578 | for (int b = 0; b < bandCount; ++b) pBandMaps[b] = b + 1;
579 | int blockX = tmpDataset->GetRasterXSize();
580 | int blockY = tmpDataset->GetRasterYSize();
581 | double trans[6] = { 0 };
582 | tmpDataset->GetGeoTransform(trans);
583 | GDALDataType types = tmpDataset->GetRasterBand(1)->GetRasterDataType();
584 | const char* projs = tmpDataset->GetProjectionRef();
585 | GDALClose(tmpDataset);
586 | // 读取最后一个块的坐标信息
587 | QString tmp = subDirList.at(subDirList.size() - 1);
588 | QFileInfo tmpInfo = QFileInfo(tmp);
589 | QStringList ids = tmpInfo.baseName().split("_");
590 | int jInd = ids.at(ids.size() - 2).toInt();
591 | int iInd = ids.at(ids.size() - 1).toInt();
592 | int nImgSizeX = jInd + blockX;
593 | int nImgSizeY = iInd + blockY;
594 | // 创建
595 | QString imgPath = imgDir + QDir::separator() + "merge.tif";
596 | GDALDriver* pDriverMEM = GetGDALDriverManager()->GetDriverByName("GTiff");
597 | if (!pDriverMEM)
598 | {
599 | GDALDestroyDriverManager();
600 | return false;
601 | }
602 | GDALDataset* poDataset = pDriverMEM->Create(
603 | imgPath.toStdString().c_str(), nImgSizeX, nImgSizeY, bandCount, types, NULL);
604 | // 循环填入
605 | QFileInfo blockInfo;
606 | QStringList blockIds;
607 | void* tmpSrcData = nullptr;
608 | for (QString blockPath : subDirList)
609 | {
610 | blockInfo = QFileInfo(blockPath);
611 | blockIds = blockInfo.baseName().split("_");
612 | GDALDataset* tmpDataset = (GDALDataset*)GDALOpen(
613 | (imgDir + QDir::separator() + blockPath).toStdString().c_str(),
614 | GA_ReadOnly
615 | );
616 | jInd = blockIds.at(ids.size() - 2).toInt();
617 | iInd = blockIds.at(ids.size() - 1).toInt();
618 | if (tmpDataset == NULL)
619 | {
620 | GDALClose(tmpDataset);
621 | GDALDestroyDriverManager();
622 | return false;
623 | }
624 | ImagePress::createArr(&tmpSrcData, types, blockX, blockY, bandCount);
625 | tmpDataset->RasterIO(
626 | GF_Read, 0, 0, blockX, blockY, tmpSrcData, blockX, blockY,
627 | types, bandCount, pBandMaps, 0, 0, 0, NULL
628 | );
629 | poDataset->RasterIO(
630 | GF_Write, jInd, iInd, blockX, blockY, tmpSrcData, blockX, blockY,
631 | types, bandCount, pBandMaps, 0, 0, 0, NULL
632 | );
633 | delete[] tmpSrcData;
634 | GDALClose(tmpDataset);
635 | poDataset->FlushCache();
636 | }
637 | poDataset->SetProjection(projs);
638 | poDataset->SetGeoTransform(trans);
639 | GDALClose(poDataset);
640 | GDALDestroyDriverManager();
641 | return true;
642 | }
643 |
644 | cv::Mat ImagePress::qpixmapToCVMat(QPixmap pimg)
645 | {
646 | QImage image = pimg.toImage();
647 | cv::Mat mat;
648 | switch (image.format())
649 | {
650 | case QImage::Format_ARGB32:
651 | case QImage::Format_RGB32:
652 | case QImage::Format_ARGB32_Premultiplied:
653 | mat = cv::Mat(
654 | image.height(), image.width(), CV_8UC4,
655 | (void*)image.constBits(), image.bytesPerLine()
656 | );
657 | break;
658 | case QImage::Format_RGB888:
659 | mat = cv::Mat(
660 | image.height(), image.width(), CV_8UC3,
661 | (void*)image.constBits(), image.bytesPerLine()
662 | );
663 | cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
664 | break;
665 | case QImage::Format_Grayscale8:
666 | mat = cv::Mat(
667 | image.height(), image.width(), CV_8UC1,
668 | (void*)image.constBits(), image.bytesPerLine()
669 | );
670 | break;
671 | }
672 | return mat;
673 | }
674 |
675 | std::vector ImagePress::calcOIF(QString hsiPath)
676 | {
677 | GDALDataset* ds = (GDALDataset*)GDALOpen(
678 | hsiPath.toStdString().c_str(), GA_ReadOnly);
679 | int bandCount = ds->GetRasterCount();
680 | int width = ds->GetRasterXSize();
681 | int height = ds->GetRasterYSize();
682 | // 计算标准差和相关系数矩阵
683 | Eigen::MatrixXd coMatrix(bandCount, bandCount);
684 | std::vector stdDev;
685 | for (int i = 1; i <= bandCount; ++i) // 波段1
686 | {
687 | GDALRasterBand* band1 = ds->GetRasterBand(i);
688 | double* data1 = new double[width * height];
689 | band1->RasterIO(
690 | GF_Read, 0, 0, width, height, data1, width, height, GDT_Float64, 0, 0);
691 | Eigen::Map mat1(data1, height, width);
692 | // 计算标准差
693 | double std = mat1.array().sqrt().matrix().mean();
694 | stdDev.push_back(std);
695 | for (int j = i + 1; j <= bandCount; ++j) // 波段2
696 | {
697 | GDALRasterBand* band2 = ds->GetRasterBand(j);
698 | double* data2 = new double[width * height];
699 | band2->RasterIO(
700 | GF_Read, 0, 0, width, height, data2,
701 | width, height, GDT_Float64, 0, 0
702 | );
703 | Eigen::Map mat2(data2, height, width);
704 | // 计算相关系数矩阵
705 | double corr = (mat1.array() - \
706 | mat1.mean()).matrix().normalized().cwiseProduct((mat2.array() - \
707 | mat2.mean()).matrix().normalized()).sum() / ((double)width * height);
708 | coMatrix(i - 1, j - 1) = corr;
709 | coMatrix(j - 1, i - 1) = corr;
710 | // 释放内存
711 | delete[] data2;
712 | }
713 | delete[] data1;
714 | }
715 | // 计算OIF值
716 | double maxOIF(0), oif(0);
717 | std::vector best_bands = { 0, 0, 0 };
718 | for (int i = 1; i <= bandCount; ++i)
719 | {
720 | for (int j = i + 1; j <= bandCount; ++j)
721 | {
722 | for (int k = j + 1; k <= bandCount; ++k)
723 | {
724 | oif = (stdDev[i - 1] + stdDev[j - 1] + stdDev[k - 1]) / (\
725 | coMatrix(i - 1, j - 1) + \
726 | coMatrix(i - 1, k - 1) + \
727 | coMatrix(j - 1, k - 1));
728 | if (oif > maxOIF) {
729 | maxOIF = oif;
730 | best_bands[0] = i;
731 | best_bands[1] = j;
732 | best_bands[2] = k;
733 | }
734 | }
735 | }
736 | }
737 | GDALClose(ds);
738 | return best_bands;
739 | }
740 |
741 | bool ImagePress::maskIsEmpty(QString maskPath)
742 | {
743 | bool isEmpty = false;
744 | GDALAllRegister();
745 | CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO");
746 | CPLSetConfigOption("GDAL_PAM_ENABLED", "NO"); // 不创建aux.xml文件
747 | GDALDataset* maskData = (GDALDataset*)GDALOpen(
748 | maskPath.toStdString().c_str(), GA_ReadOnly);
749 | double mMin, mMax, mMean, mStddev;
750 | maskData->GetRasterBand(1)->ComputeStatistics(
751 | true, &mMin, &mMax, &mMean, &mStddev, NULL, NULL);
752 | if (mMin == mMax && mMin == 0) isEmpty = true;
753 | GDALClose(maskData);
754 | GDALDestroyDriverManager();
755 | return isEmpty;
756 | }
757 |
--------------------------------------------------------------------------------
/labcd/utils/imgpress.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "../widgets/labpolygon.h"
9 |
10 | class ImagePress
11 | {
12 | private:
13 | static bool createArr(
14 | void** data, GDALDataType types, int xSize, int ySize, int band);
15 | static unsigned char* imgSketch(
16 | float* buffer,
17 | GDALRasterBand* currentBand,
18 | int bandSize,
19 | double noValue
20 | );
21 | static QPixmap GDALRastertoPixmap(QList* imgBand);
22 | static bool saveTiffFromGDAL(
23 | std::string savePath,
24 | void* img,
25 | int nImgSizeX,
26 | int nImgSizeY,
27 | int nChannel,
28 | GDALDataType types,
29 | const char* projs,
30 | double* trans,
31 | int* pBandMaps
32 | );
33 | static bool saveTiffFromCVMask(
34 | std::string savePath,
35 | cv::Mat mask,
36 | const char* projs,
37 | double* trans
38 | );
39 | static void calcWindowTrans(double trans[6], int locX, int locY);
40 | static std::vector calcUnique(cv::Mat mask);
41 |
42 | public:
43 | static cv::Mat CVA(cv::Mat t1, cv::Mat t2);
44 | static void saveResultFromPolygon(
45 | QString savePath,
46 | int labNum,
47 | int imgHeight,
48 | int imgWidth,
49 | QList polygons,
50 | std::string projs = "",
51 | double* trans = NULL
52 | );
53 | static void savePolygonFromMask(QString maskPath);
54 | static bool openImage(
55 | QString imgPath,
56 | QPixmap& img,
57 | std::string& projs,
58 | double trans[6]
59 | );
60 | static bool splitTiff(
61 | QString imgPath,
62 | QString saveDir,
63 | int blockHeight = 512,
64 | int blockWidth = 512
65 | );
66 | static bool mergeTiff(QString imgDir);
67 | static cv::Mat qpixmapToCVMat(QPixmap pimg);
68 | static std::vector calcOIF(QString hsiPath);
69 | static bool maskIsEmpty(QString maskPath);
70 | };
71 |
--------------------------------------------------------------------------------
/labcd/utils/label.cpp:
--------------------------------------------------------------------------------
1 | #include "label.h"
2 |
3 | Label::Label()
4 | {
5 | Label::init();
6 | }
7 |
8 | Label::Label(int index, QString name, QColor color)
9 | {
10 | Label::reSet(index, name, color);
11 | }
12 |
13 | Label::~Label()
14 | {
15 |
16 | }
17 |
18 | void Label::init()
19 | {
20 | this->index = -1;
21 | this->name = "";
22 | this->color = QColor();
23 | }
24 |
25 | void Label::reSet(int index, QString name, QColor color)
26 | {
27 | this->index = index;
28 | this->name = name;
29 | this->color = color;
30 | }
31 |
32 | void Label::setIndex(int index)
33 | {
34 | this->index = index;
35 | }
36 |
37 | int Label::getIndex()
38 | {
39 | return this->index;
40 | }
41 |
42 | void Label::setName(QString name)
43 | {
44 | this->name = name;
45 | }
46 |
47 | QString Label::getName()
48 | {
49 | return this->name;
50 | }
51 |
52 | void Label::setColor(QColor color)
53 | {
54 | this->color = color;
55 | }
56 |
57 | QColor Label::getColor()
58 | {
59 | return this->color;
60 | }
61 |
--------------------------------------------------------------------------------
/labcd/utils/label.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | class Label
8 | {
9 | private:
10 | int index;
11 | QString name;
12 | QColor color;
13 |
14 | public:
15 | Label();
16 | Label(int index, QString name, QColor color);
17 | ~Label();
18 | void init();
19 | void reSet(int index, QString name, QColor color);
20 | void setIndex(int index);
21 | int getIndex();
22 | void setName(QString name);
23 | QString getName();
24 | void setColor(QColor color);
25 | QColor getColor();
26 | };
27 |
--------------------------------------------------------------------------------
/labcd/widgets/annotationscence.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "annotationscence.h"
4 |
5 | AnnotationScence::AnnotationScence()
6 | : QGraphicsScene()
7 | {
8 | scaleRate = 1.0;
9 | // 十字丝
10 | crossPen = new QPen();
11 | crossPen->setWidth(2);
12 | crossPen->setColor(QColor(0, 0, 0, 127));
13 | coords = nullptr;
14 | }
15 |
16 | AnnotationScence::~AnnotationScence()
17 | {
18 |
19 | }
20 |
21 | bool AnnotationScence::getItemHovering()
22 | {
23 | for (LabPolygon* poly : polygonItems)
24 | {
25 | if (poly->itemHovering)
26 | return true;
27 | }
28 | return false;
29 | }
30 |
31 | bool AnnotationScence::getPolyHovering()
32 | {
33 | for (LabPolygon* poly : polygonItems)
34 | {
35 | if (poly->polyHovering)
36 | return true;
37 | }
38 | return false;
39 | }
40 |
41 | bool AnnotationScence::getLineHovering()
42 | {
43 | for (LabPolygon* poly : polygonItems)
44 | {
45 | if (poly->lineHovering)
46 | return true;
47 | }
48 | return false;
49 | }
50 |
51 | void AnnotationScence::updateCrossPenSize()
52 | {
53 | crossPen->setWidth(std::max(1, int(2 / scaleRate + 1e-12)));
54 | }
55 |
56 | void AnnotationScence::resetScence()
57 | {
58 | nowItem = nullptr;
59 | drawing = false;
60 | clearFocusAndSelected();
61 | }
62 |
63 | void AnnotationScence::finished()
64 | {
65 | if (nowItem != nullptr)
66 | {
67 | if (drawing == true && nowItem->getLen() < 3)
68 | nowItem->remove();
69 | }
70 | resetScence(); // 清理和置零
71 | }
72 |
73 | void AnnotationScence::setScaleRate(double zoomAll)
74 | {
75 | scaleRate = zoomAll;
76 | }
77 |
78 | void AnnotationScence::updatePolygonSize()
79 | {
80 | for (LabPolygon* poly : polygonItems)
81 | {
82 | for (LabGrid* grip : poly->mItems)
83 | grip->updateSize();
84 | for (LabLine* line : poly->mLines)
85 | line->updateWidth();
86 | }
87 | }
88 |
89 | void AnnotationScence::setColor(QColor _insideColor, QColor _borderColor)
90 | {
91 | insideColor = _insideColor;
92 | borderColor = _borderColor;
93 | }
94 |
95 | QColor AnnotationScence::getCrossPenColor()
96 | {
97 | return crossPen->color();
98 | }
99 |
100 | void AnnotationScence::setCrossPenColor(QColor color)
101 | {
102 | crossPen->setColor(color);
103 | }
104 |
105 | bool AnnotationScence::hovering()
106 | {
107 | if (getItemHovering() || getPolyHovering() || getLineHovering())
108 | {
109 | return true;
110 | }
111 | return false;
112 | }
113 |
114 | int AnnotationScence::findFocusPolygon()
115 | {
116 | int focusIndex = -1;
117 | for (int i = 0; i < polygonItems.count(); ++i)
118 | {
119 | if (polygonItems.at(i)->hasFocus())
120 | {
121 | focusIndex = i;
122 | break;
123 | }
124 | }
125 | return focusIndex;
126 | }
127 |
128 | void AnnotationScence::delPoly(int index)
129 | {
130 | if (index != -1)
131 | polygonItems[index]->remove();
132 | }
133 |
134 | void AnnotationScence::removeAllPolygons()
135 | {
136 | int numPoly = polygonItems.count();
137 | for (int i = numPoly - 1; i >= 0; --i)
138 | delPoly(i);
139 | }
140 |
141 | void AnnotationScence::clearAllFocus()
142 | {
143 | for (QGraphicsItem* obj : items())
144 | {
145 | if (obj->hasFocus())
146 | obj->clearFocus();
147 | }
148 | }
149 |
150 | void AnnotationScence::clearFocusAndSelected()
151 | {
152 | clearAllFocus();
153 | clearSelection();
154 | }
155 |
156 | void AnnotationScence::PressedAddPoint(QPointF point)
157 | {
158 | if (imgWidth != 0)
159 | {
160 | if (nowItem == nullptr)
161 | {
162 | nowItem = new LabPolygon(
163 | this, polygonItems.count(), labelIndex,
164 | imgWidth, imgHeight, insideColor, borderColor, opacity
165 | );
166 | addItem(nowItem);
167 | polygonItems.push_back(nowItem);
168 | }
169 | nowItem->addPointLast(point);
170 | // 选择
171 | clearFocusAndSelected();
172 | nowItem->mItems[nowItem->mItems.count() - 1]->setSelected(true);
173 | }
174 | }
175 |
176 | void AnnotationScence::mousePressEvent(QGraphicsSceneMouseEvent* ev)
177 | {
178 | // 右键或点击多边形激活则完成
179 | if (ev->button() == Qt::RightButton || (hovering() && drawing))
180 | {
181 | finished();
182 | emit mouseOptRequest(-1, -1, OptTypes::SceneMousePress, ev);
183 | }
184 | // 其他操作
185 | else
186 | {
187 | QPointF p = ev->scenePos();
188 | if (p.x() < 0 || p.x() > imgWidth || p.y() < 0 || p.y() > imgHeight)
189 | return; // 超出范围
190 | if (!hovering()) // 非激活状态
191 | {
192 | // 当选择有标签的时候,左键添加
193 | if (ev->button() == Qt::LeftButton && labelIndex != -1)
194 | {
195 | PressedAddPoint(p);
196 | drawing = true;
197 | emit mouseOptRequest(-1, -1, OptTypes::SceneMousePress, ev);
198 | }
199 | }
200 | else
201 | {
202 | for (LabPolygon* poly : polygonItems)
203 | {
204 | if (poly->polyHovering)
205 | emit focusRequest(poly->labelIndex);
206 | }
207 | }
208 | QGraphicsScene::mousePressEvent(ev);
209 | }
210 | }
211 |
212 | void AnnotationScence::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev)
213 | {
214 | QGraphicsItem* nowObj = itemAt(ev->scenePos(), QTransform());
215 | if (nowObj == nullptr)
216 | return;
217 | switch ((int)nowObj->zValue())
218 | {
219 | case 10: // 使双击对多边形无效
220 | break;
221 | default:
222 | QGraphicsScene::mouseDoubleClickEvent(ev);
223 | break;
224 | }
225 | }
226 |
227 | void AnnotationScence::getLabel(Label* label)
228 | {
229 | // 完成之前的
230 | finished();
231 | // 新设定
232 | labelIndex = label->getIndex();
233 | insideColor = label->getColor();
234 | borderColor = label->getColor();
235 | }
236 |
237 | void AnnotationScence::getImageSize(int Width, int Height)
238 | {
239 | imgWidth = Width;
240 | imgHeight = Height;
241 | }
242 |
243 | void AnnotationScence::copyMouseOpt(
244 | int polyIndex, int subIndex, OptTypes optType, QEvent* ev)
245 | {
246 | blockSignals(true);
247 | switch (optType)
248 | {
249 | case OptTypes::SceneMousePress:
250 | mousePressEvent((QGraphicsSceneMouseEvent*)ev);
251 | break;
252 | case OptTypes::SceneMouseDoubleClick:
253 | mouseDoubleClickEvent((QGraphicsSceneMouseEvent*)ev);
254 | break;
255 | case OptTypes::PolyHoverEnter:
256 | if (nowItem != nullptr)
257 | nowItem->hoverEnterEvent((QGraphicsSceneHoverEvent*)ev);
258 | else
259 | {
260 | if (polygonItems.count() != 0)
261 | polygonItems[polyIndex % polygonItems.count()]->hoverEnterEvent(
262 | (QGraphicsSceneHoverEvent*)ev);
263 | }
264 | break;
265 | case OptTypes::PolyHoverLeave:
266 | if (nowItem != nullptr)
267 | nowItem->hoverLeaveEvent((QGraphicsSceneHoverEvent*)ev);
268 | else
269 | {
270 | if (polygonItems.count() != 0)
271 | polygonItems[polyIndex % polygonItems.count()]->hoverLeaveEvent(
272 | (QGraphicsSceneHoverEvent*)ev);
273 | }
274 | break;
275 | case OptTypes::PolyMousePress:
276 | if (nowItem != nullptr)
277 | nowItem->mousePressEvent((QGraphicsSceneMouseEvent*)ev);
278 | else
279 | {
280 | if (polygonItems.count() != 0)
281 | polygonItems[polyIndex % polygonItems.count()]->mousePressEvent(
282 | (QGraphicsSceneMouseEvent*)ev);
283 | }
284 | break;
285 | case OptTypes::PolyMouseRelease:
286 | if (nowItem != nullptr)
287 | nowItem->mouseReleaseEvent((QGraphicsSceneMouseEvent*)ev);
288 | else
289 | {
290 | if (polygonItems.count() != 0)
291 | polygonItems[polyIndex % polygonItems.count()]->mouseReleaseEvent(
292 | (QGraphicsSceneMouseEvent*)ev);
293 | }
294 | break;
295 | case OptTypes::PolyFocusIn:
296 | if (nowItem != nullptr)
297 | nowItem->focusInEvent((QFocusEvent*)ev);
298 | else
299 | {
300 | if (polygonItems.count() != 0)
301 | polygonItems[polyIndex % polygonItems.count()]->focusInEvent(
302 | (QFocusEvent*)ev);
303 | }
304 | break;
305 | case OptTypes::PolyFocusOut:
306 | if (nowItem != nullptr)
307 | nowItem->focusOutEvent((QFocusEvent*)ev);
308 | else
309 | {
310 | if (polygonItems.count() != 0)
311 | polygonItems[polyIndex % polygonItems.count()]->focusOutEvent(
312 | (QFocusEvent*)ev);
313 | }
314 | break;
315 | case OptTypes::LineHoverEnter:
316 | if (nowItem != nullptr)
317 | {
318 | int lens = nowItem->mLines.count();
319 | nowItem->mLines[subIndex % lens]->hoverEnterEvent(
320 | (QGraphicsSceneHoverEvent*)ev);
321 | }
322 | else
323 | {
324 | if (polygonItems.count() != 0)
325 | {
326 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
327 | mLines.count();
328 | polygonItems[polyIndex % polygonItems.count()]-> \
329 | mLines[subIndex % lens]-> \
330 | hoverEnterEvent((QGraphicsSceneHoverEvent*)ev);
331 | }
332 | }
333 | break;
334 | case OptTypes::LineHoverLeave:
335 | if (nowItem != nullptr)
336 | {
337 | int lens = nowItem->mLines.count();
338 | nowItem->mLines[subIndex % lens]->hoverLeaveEvent(
339 | (QGraphicsSceneHoverEvent*)ev);
340 | }
341 | else
342 | {
343 | if (polygonItems.count() != 0)
344 | {
345 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
346 | mLines.count();
347 | polygonItems[polyIndex % polygonItems.count()]-> \
348 | mLines[subIndex % lens]-> \
349 | hoverLeaveEvent((QGraphicsSceneHoverEvent*)ev);
350 | }
351 | }
352 | break;
353 | case OptTypes::LineMousePress:
354 | if (nowItem != nullptr)
355 | {
356 | int lens = nowItem->mLines.count();
357 | nowItem->mLines[subIndex % lens]->mousePressEvent(
358 | (QGraphicsSceneMouseEvent*)ev);
359 | }
360 | else
361 | {
362 | if (polygonItems.count() != 0)
363 | {
364 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
365 | mLines.count();
366 | polygonItems[polyIndex % polygonItems.count()]-> \
367 | mLines[subIndex % lens]-> \
368 | mousePressEvent((QGraphicsSceneMouseEvent*)ev);
369 | }
370 | }
371 | break;
372 | case OptTypes::LineMouseRelease:
373 | if (nowItem != nullptr)
374 | {
375 | int lens = nowItem->mLines.count();
376 | nowItem->mLines[subIndex % lens]->mouseReleaseEvent(
377 | (QGraphicsSceneMouseEvent*)ev);
378 | }
379 | else
380 | {
381 | if (polygonItems.count() != 0)
382 | {
383 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
384 | mLines.count();
385 | polygonItems[polyIndex % polygonItems.count()]-> \
386 | mLines[subIndex % lens]-> \
387 | mouseReleaseEvent((QGraphicsSceneMouseEvent*)ev);
388 | }
389 | }
390 | break;
391 | case OptTypes::LineMouseDoubleClick:
392 | if (nowItem != nullptr)
393 | {
394 | int lens = nowItem->mLines.count();
395 | nowItem->mLines[subIndex % lens]->mouseDoubleClickEvent(
396 | (QGraphicsSceneMouseEvent*)ev);
397 | }
398 | else
399 | {
400 | if (polygonItems.count() != 0)
401 | {
402 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
403 | mLines.count();
404 | polygonItems[polyIndex % polygonItems.count()]-> \
405 | mLines[subIndex % lens]-> \
406 | mouseDoubleClickEvent((QGraphicsSceneMouseEvent*)ev);
407 | }
408 | }
409 | break;
410 | case OptTypes::GridHoverEnter:
411 | if (nowItem != nullptr)
412 | {
413 | int lens = nowItem->mItems.count();
414 | nowItem->mItems[subIndex % lens]->hoverEnterEvent(
415 | (QGraphicsSceneHoverEvent*)ev);
416 | }
417 | else
418 | {
419 | if (polygonItems.count() != 0)
420 | {
421 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
422 | mItems.count();
423 | polygonItems[polyIndex % polygonItems.count()]-> \
424 | mItems[subIndex % lens]-> \
425 | hoverEnterEvent((QGraphicsSceneHoverEvent*)ev);
426 | }
427 | }
428 | break;
429 | case OptTypes::GridHoverLeave:
430 | if (nowItem != nullptr)
431 | {
432 | int lens = nowItem->mItems.count();
433 | nowItem->mItems[subIndex % lens]->hoverLeaveEvent(
434 | (QGraphicsSceneHoverEvent*)ev);
435 | }
436 | else
437 | {
438 | if (polygonItems.count() != 0)
439 | {
440 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
441 | mItems.count();
442 | polygonItems[polyIndex % polygonItems.count()]-> \
443 | mItems[subIndex % lens]-> \
444 | hoverLeaveEvent((QGraphicsSceneHoverEvent*)ev);
445 | }
446 | }
447 | break;
448 | case OptTypes::GridMousePress:
449 | if (nowItem != nullptr)
450 | {
451 | int lens = nowItem->mItems.count();
452 | nowItem->mItems[subIndex % lens]->mousePressEvent(
453 | (QGraphicsSceneMouseEvent*)ev);
454 | }
455 | else
456 | {
457 | if (polygonItems.count() != 0)
458 | {
459 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
460 | mItems.count();
461 | polygonItems[polyIndex % polygonItems.count()]-> \
462 | mItems[subIndex % lens]-> \
463 | mousePressEvent((QGraphicsSceneMouseEvent*)ev);
464 | }
465 | }
466 | break;
467 | case OptTypes::GridMouseRelease:
468 | if (nowItem != nullptr)
469 | {
470 | int lens = nowItem->mItems.count();
471 | nowItem->mItems[subIndex % lens]->mouseReleaseEvent(
472 | (QGraphicsSceneMouseEvent*)ev);
473 | }
474 | else
475 | {
476 | if (polygonItems.count() != 0)
477 | {
478 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
479 | mItems.count();
480 | polygonItems[polyIndex % polygonItems.count()]-> \
481 | mItems[subIndex % lens]-> \
482 | mouseReleaseEvent((QGraphicsSceneMouseEvent*)ev);
483 | }
484 | }
485 | break;
486 | case OptTypes::GridMouseMove:
487 | if (nowItem != nullptr)
488 | {
489 | int lens = nowItem->mItems.count();
490 | nowItem->mItems[subIndex % lens]->mouseMoveEvent(
491 | (QGraphicsSceneMouseEvent*)ev);
492 | }
493 | else
494 | {
495 | if (polygonItems.count() != 0)
496 | {
497 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
498 | mItems.count();
499 | polygonItems[polyIndex % polygonItems.count()]-> \
500 | mItems[subIndex % lens]-> \
501 | mouseMoveEvent((QGraphicsSceneMouseEvent*)ev);
502 | }
503 | }
504 | break;
505 | case OptTypes::GridMouseDoubleClick:
506 | if (nowItem != nullptr)
507 | {
508 | int lens = nowItem->mItems.count();
509 | nowItem->mItems[subIndex % lens]->mouseDoubleClickEvent(
510 | (QGraphicsSceneMouseEvent*)ev);
511 | }
512 | else
513 | {
514 | if (polygonItems.count() != 0)
515 | {
516 | int lens = polygonItems[polyIndex % polygonItems.count()]-> \
517 | mItems.count();
518 | polygonItems[polyIndex % polygonItems.count()]-> \
519 | mItems[subIndex % lens]-> \
520 | mouseDoubleClickEvent((QGraphicsSceneMouseEvent*)ev);
521 | }
522 | }
523 | break;
524 | default:
525 | break;
526 | }
527 | blockSignals(false);
528 | }
529 |
530 | void AnnotationScence::drawForeground(QPainter* painter, const QRectF& rect)
531 | {
532 | if (coords != nullptr && *coords != QPointF(-1, -1))
533 | {
534 | painter->setClipRect(rect);
535 | painter->setPen(*crossPen);
536 | painter->drawLine(
537 | int(coords->x()),
538 | int(rect.top()), int(coords->x()), int(rect.bottom() + 1)
539 | );
540 | painter->drawLine(
541 | int(rect.left()),
542 | int(coords->y()),
543 | int(rect.right() + 1), int(coords->y())
544 | );
545 | }
546 | }
547 |
548 | void AnnotationScence::onMouseChanged(double x, double y)
549 | {
550 | coords = new QPointF(x, y);
551 | invalidate();
552 | }
553 |
--------------------------------------------------------------------------------
/labcd/widgets/annotationscence.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "labpolygon.h"
8 | #include "opttypes.h"
9 | #include "../utils/label.h"
10 |
11 | class AnnotationScence : public QGraphicsScene
12 | {
13 | Q_OBJECT
14 |
15 | private:
16 | double scaleRate;
17 | QColor insideColor;
18 | QColor borderColor;
19 | QPen* crossPen;
20 | QPointF* coords;
21 |
22 | bool getItemHovering();
23 | bool getPolyHovering();
24 | bool getLineHovering();
25 | void updateCrossPenSize();
26 |
27 | public:
28 | int labelIndex = -1;
29 | int imgWidth = 0;
30 | int imgHeight = 0;
31 | double opacity = 0.5;
32 | bool drawing = false;
33 | QList polygonItems;
34 | LabPolygon* nowItem = nullptr;
35 |
36 | AnnotationScence();
37 | ~AnnotationScence();
38 | void resetScence();
39 | void finished();
40 | void setScaleRate(double zoomAll);
41 | void updatePolygonSize();
42 | void setColor(QColor _insideColor, QColor _borderColor);
43 | QColor getCrossPenColor();
44 | void setCrossPenColor(QColor color);
45 | bool hovering();
46 | int findFocusPolygon();
47 | void delPoly(int preFocusIndex);
48 | void removeAllPolygons();
49 | void clearAllFocus();
50 | void clearFocusAndSelected();
51 | // 同步方法
52 | void PressedAddPoint(QPointF point);
53 | // 鼠标事件
54 | void mousePressEvent(QGraphicsSceneMouseEvent* ev);
55 | void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev);
56 | void drawForeground(QPainter* painter, const QRectF& rect);
57 | void onMouseChanged(double x, double y);
58 |
59 | signals:
60 | void focusRequest(int labelIndex);
61 | void mouseOptRequest(int polyIndex, int subIndex, OptTypes optType, QEvent* ev);
62 |
63 | public slots:
64 | void getLabel(Label* label);
65 | void getImageSize(int Width, int Height);
66 | void copyMouseOpt(int polyIndex, int subIndex, OptTypes optType, QEvent* ev);
67 | };
68 |
--------------------------------------------------------------------------------
/labcd/widgets/annotationview.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "annotationview.h"
4 |
5 | AnnotationView::AnnotationView(
6 | AnnotationScence* scence, QWidget* parent)
7 | : QGraphicsView(scence, parent)
8 | {
9 | setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);
10 | setMouseTracking(true);
11 | setTransformationAnchor(QGraphicsView::NoAnchor);
12 | setResizeAnchor(QGraphicsView::NoAnchor);
13 | }
14 |
15 | AnnotationView::~AnnotationView()
16 | {
17 |
18 | }
19 |
20 | double AnnotationView::limitZoom(double min, double now, double max)
21 | {
22 | if (now > max)
23 | return max;
24 | else if (now < min)
25 | return min;
26 | else
27 | return now;
28 | }
29 |
30 | void AnnotationView::sendSyncSignal()
31 | {
32 | emit syncRequest(
33 | horizontalScrollBar()->value(),
34 | verticalScrollBar()->value(),
35 | transform(),
36 | zoomAll
37 | );
38 | }
39 |
40 | bool AnnotationView::checkZoomAll()
41 | {
42 | if (zoomAll < minRange || zoomAll > maxRange)
43 | return false;
44 | else
45 | return true;
46 | }
47 |
48 | void AnnotationView::setZoomAll(double value)
49 | {
50 | zoomAll = value;
51 | }
52 |
53 | double AnnotationView::getZoomAll()
54 | {
55 | return zoomAll;
56 | }
57 |
58 | void AnnotationView::syncTranslate(int hPos, int vPos, QTransform tf, double zoom)
59 | {
60 | zoomAll = zoom;
61 | setTransform(tf);
62 | horizontalScrollBar()->setValue(hPos);
63 | verticalScrollBar()->setValue(vPos);
64 | }
65 |
66 | void AnnotationView::scaleZoom(double _zoom)
67 | {
68 | zoomAll *= _zoom;
69 | // 限制缩放
70 | if (AnnotationView::checkZoomAll())
71 | scale(_zoom, _zoom);
72 | else
73 | zoomAll = AnnotationView::limitZoom(minRange, zoomAll, maxRange);
74 | emit zoomRequest(zoomAll);
75 | }
76 |
77 | void AnnotationView::wheelEvent(QWheelEvent* ev)
78 | {
79 | if (ev->modifiers() && Qt::ControlModifier)
80 | {
81 | double zoom = 1.0 + ev->angleDelta().y() / 2880.0; // 倍率
82 | zoomAll *= zoom;
83 | // 限制缩放
84 | if (AnnotationView::checkZoomAll())
85 | {
86 | QPointF oldPos = mapToScene(ev->position().toPoint());
87 | scale(zoom, zoom);
88 | QPointF newPos = mapToScene(ev->position().toPoint());
89 | QPointF delta = newPos - oldPos;
90 | translate(delta.x(), delta.y());
91 | }
92 | else
93 | zoomAll = AnnotationView::limitZoom(minRange, zoomAll, maxRange);
94 | emit zoomRequest(zoomAll);
95 | ev->ignore(); // 忽略滚动条
96 | }
97 | else
98 | QGraphicsView::wheelEvent(ev);
99 | }
100 |
101 | void AnnotationView::mouseMoveEvent(QMouseEvent* ev)
102 | {
103 | QPointF mousePos = QPointF(mapToScene(ev->pos()));
104 | emit mousePosChanged(mousePos.x(), mousePos.y());
105 | // 出现滚动条才能滚动
106 | if (middleClicking && \
107 | (horizontalScrollBar()->isVisible() || verticalScrollBar()->isVisible()))
108 | {
109 | endPos = new QPoint(ev->pos() / zoomAll - *startPos / zoomAll);
110 | point = new QPoint(*point + *endPos);
111 | startPos = new QPoint(ev->pos());
112 | translate(endPos->x(), endPos->y());
113 | }
114 | QGraphicsView::mouseMoveEvent(ev);
115 | }
116 |
117 | void AnnotationView::mousePressEvent(QMouseEvent* ev)
118 | {
119 | if (ev->button() == Qt::MiddleButton)
120 | {
121 | middleClicking = true;
122 | startPos = new QPoint(ev->pos());
123 | }
124 | QGraphicsView::mousePressEvent(ev);
125 | }
126 |
127 | void AnnotationView::mouseReleaseEvent(QMouseEvent* ev)
128 | {
129 | middleClicking = false;
130 | QGraphicsView::mouseReleaseEvent(ev);
131 | }
132 |
133 | void AnnotationView::scale(qreal sx, qreal sy)
134 | {
135 | QGraphicsView::scale(sx, sy);
136 | sendSyncSignal();
137 | }
138 |
139 | void AnnotationView::translate(qreal dx, qreal dy)
140 | {
141 | QGraphicsView::translate(dx, dy);
142 | sendSyncSignal();
143 | }
144 |
--------------------------------------------------------------------------------
/labcd/widgets/annotationview.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "annotationscence.h"
8 |
9 | class AnnotationView : public QGraphicsView
10 | {
11 | Q_OBJECT
12 |
13 | private:
14 | QPoint* point = new QPoint(0, 0);
15 | QPoint* startPos = new QPoint(0, 0);
16 | QPoint* endPos = new QPoint(0, 0);
17 | bool middleClicking = false; // 中间用于移动
18 | const double minRange = 0.02;
19 | const double maxRange = 50;
20 | double zoomAll = 1;
21 |
22 | double limitZoom(double min, double now, double max);
23 | void sendSyncSignal();
24 |
25 | public:
26 | AnnotationView(
27 | AnnotationScence* scence,
28 | QWidget* parent = nullptr
29 | );
30 | ~AnnotationView();
31 | bool checkZoomAll();
32 | void setZoomAll(double value);
33 | double getZoomAll();
34 | void syncTranslate(int hPos, int vPos, QTransform tf, double zoom);
35 | void scaleZoom(double _zoom);
36 | // 重写鼠标事件
37 | void wheelEvent(QWheelEvent* ev);
38 | void mouseMoveEvent(QMouseEvent* ev);
39 | void mousePressEvent(QMouseEvent* ev);
40 | void mouseReleaseEvent(QMouseEvent* ev);
41 | // 重写变换方法,发送信号
42 | void scale(qreal sx, qreal sy);
43 | void translate(qreal dx, qreal dy);
44 |
45 | signals:
46 | void zoomRequest(double zoomAll);
47 | void mousePosChanged(double x, double y);
48 | // 同步信号给另一个Canvas
49 | void syncRequest(int hPos, int vPos, QTransform tf, double zoom);
50 | };
51 |
--------------------------------------------------------------------------------
/labcd/widgets/canvas.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "canvas.h"
8 | #include "labpolygon.h"
9 |
10 | Canvas::Canvas(QWidget* parent)
11 | : QScrollArea(parent)
12 | {
13 | setWidgetResizable(true);
14 | // 绘图区
15 | aScene = new AnnotationScence();
16 | aView = new AnnotationView(aScene, parent);
17 | QSizePolicy sizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
18 | aView->setSizePolicy(sizePolicy);
19 | aView->setAlignment(Qt::AlignCenter);
20 | aView->setAutoFillBackground(false);
21 | aView->setStyleSheet("background-color: White");
22 | connect(aView, &AnnotationView::mousePosChanged,
23 | aScene, &AnnotationScence::onMouseChanged);
24 | // 保持滑动滑块的时候也能同步
25 | connect(aView->horizontalScrollBar(), &QScrollBar::valueChanged, [=](int value) {
26 | emit syncScroll(value, aView->verticalScrollBar()->value());});
27 | connect(aView->verticalScrollBar(), &QScrollBar::valueChanged, [=](int value) {
28 | emit syncScroll(aView->horizontalScrollBar()->value(), value);});
29 | // 加载
30 | setWidget(aView);
31 | }
32 |
33 | Canvas::~Canvas()
34 | {
35 |
36 | }
37 |
38 | void Canvas::resetZoom(int width, int height)
39 | {
40 | aScene->setSceneRect(0, 0, width, height);
41 | // 缩放清除
42 | aView->scale(1 / aView->getZoomAll(), 1 / aView->getZoomAll()); // 重置缩放
43 | aView->setZoomAll(1);
44 | // 最佳缩放
45 | double scrContWidth = (this->width() * optSize) / width;
46 | double scrContHeight = (this->height() * optSize) / height;
47 | if (scrContWidth * height > this->height())
48 | aView->setZoomAll(scrContHeight);
49 | else
50 | aView->setZoomAll(scrContWidth);
51 | aView->scale(aView->getZoomAll(), aView->getZoomAll());
52 | aScene->setScaleRate(aView->getZoomAll());
53 | }
54 |
55 | void Canvas::loadImageFromPixmap(QPixmap pixmap)
56 | {
57 | aScene->removeAllPolygons();
58 | aScene->clear();
59 | Canvas::resetZoom(pixmap.width(), pixmap.height());
60 | QGraphicsPixmapItem* pixmapItem = new QGraphicsPixmapItem();
61 | pixmapItem->setPixmap(pixmap);
62 | aScene->addItem(pixmapItem);
63 | }
64 |
65 | void Canvas::loadJSONFromFile(QString jsonPath)
66 | {
67 | std::ifstream ifs(jsonPath.toStdString(), std::ios::binary);
68 | if (!ifs.is_open())
69 | return;
70 | Json::Reader reader;
71 | Json::Value root;
72 | // 解析json内容
73 | if (reader.parse(ifs, root))
74 | {
75 | for (int i = 0; i < root.size(); ++i)
76 | {
77 | QColor jColor = QColor(
78 | root[i]["color"]["R"].asInt(),
79 | root[i]["color"]["G"].asInt(),
80 | root[i]["color"]["B"].asInt()
81 | );
82 | int jIndex = root[i]["labelIndex"].asInt();
83 | emit addJsonPoly(jIndex, jColor);
84 | int jPointNumber = root[i]["polygon"]["pointNumber"].asInt();
85 | // 新建多边形
86 | LabPolygon* nowItem = new LabPolygon(
87 | aScene, i, jIndex, aScene->imgWidth, aScene->imgHeight,
88 | jColor, jColor, aScene->opacity
89 | );
90 | aScene->addItem(nowItem);
91 | aScene->polygonItems.push_back(nowItem);
92 | // 添加点
93 | for (int j = 0; j < jPointNumber; ++j)
94 | {
95 | QPointF* point = new QPointF(
96 | root[i]["polygon"]["points"][2 * j].asFloat(),
97 | root[i]["polygon"]["points"][2 * j + 1].asFloat()
98 | );
99 | nowItem->addPointLast(*point);
100 | }
101 | }
102 | }
103 | ifs.close();
104 | }
105 |
106 | void Canvas::scroolTranslate(int hPos, int vPos)
107 | {
108 | aView->horizontalScrollBar()->setValue(hPos);
109 | aView->verticalScrollBar()->setValue(vPos);
110 | }
111 |
--------------------------------------------------------------------------------
/labcd/widgets/canvas.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "annotationview.h"
7 | #include "annotationscence.h"
8 | #include "../utils/label.h"
9 |
10 | class Canvas : public QScrollArea
11 | {
12 | Q_OBJECT
13 |
14 | private:
15 | double optSize = 0.98; // 最佳缩放
16 |
17 |
18 | public:
19 | AnnotationView* aView = nullptr;
20 | AnnotationScence* aScene = nullptr;
21 |
22 | Canvas(QWidget* parent = nullptr);
23 | ~Canvas();
24 | void resetZoom(int width, int height);
25 | void loadImageFromPixmap(QPixmap pixmap);
26 | void loadJSONFromFile(QString jsonPath);
27 | void scroolTranslate(int hPos, int vPos);
28 |
29 | signals:
30 | void syncScroll(int hPos, int vPos); // 滑块控制
31 | void addJsonPoly(int index, QColor color);
32 | };
33 |
--------------------------------------------------------------------------------
/labcd/widgets/filelist.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "filelist.h"
5 |
6 | FileList::FileList(QWidget* parent)
7 | : QWidget(parent)
8 | {
9 | // 布局
10 | QVBoxLayout* vLayout = new QVBoxLayout(this);
11 | fList = new QListWidget(this);
12 | connect(fList, &QListWidget::itemDoubleClicked, [=](QListWidgetItem* item) {
13 | emit saveLastFileRequest(); // 发送信号,保存上一张图像
14 | gotoItem(item); // 发送信号,当前点击的文件名打开图像
15 | });
16 | vLayout->addWidget(fList);
17 | progressBar = new QProgressBar(this);
18 | progressBar->setValue(0);
19 | progressBar->setFormat("0 / 0");
20 | progressBar->setAlignment(Qt::AlignLeft | Qt::AlignVCenter); // 对齐方式
21 | vLayout->addWidget(progressBar);
22 | // 加载
23 | setLayout(vLayout);
24 | }
25 |
26 | FileList::~FileList()
27 | {
28 |
29 | }
30 |
31 | QString FileList::getGTJsonPath(QString t1Path)
32 | {
33 | QFileInfo fileInfo(t1Path);
34 | QString jsonPath = t1Path.replace("A", "GT");
35 | jsonPath = jsonPath.replace(fileInfo.completeSuffix(), "json");
36 | QFileInfo jsonInfo(jsonPath);
37 | if (jsonInfo.isFile())
38 | return jsonPath;
39 | else
40 | return "";
41 | }
42 |
43 | void FileList::addFileNames(QStringList t1List, QStringList t2List)
44 | {
45 | QStringList t1Files = t1List;
46 | t2Files = t2List;
47 | // 显示
48 | fList->addItems(t1Files);
49 | fList->setCurrentRow(t1Files.size() - 1); // 移动位置
50 | }
51 |
52 | bool FileList::gotoItem(int index)
53 | {
54 | if (index < 0 || index > fList->count() - 1)
55 | return false;
56 | fList->setCurrentRow(index);
57 | nowIndex = index;
58 | QString t1Path = fList->currentItem()->text();
59 | QString t2Path = t2Files.at(fList->currentRow());
60 | emit FileList::FileClickRequest(t1Path, t2Path, getGTJsonPath(t1Path));
61 | return true;
62 | }
63 |
64 | bool FileList::gotoItem(QListWidgetItem* item)
65 | {
66 | try
67 | {
68 | fList->setCurrentItem(item);
69 | nowIndex = fList->currentRow();
70 | QString t1Path = item->text();
71 | QString t2Path = t2Files.at(fList->currentRow());
72 | emit FileList::FileClickRequest(t1Path, t2Path, getGTJsonPath(t1Path));
73 | return true;
74 | }
75 | catch (...)
76 | {
77 | return false;
78 | }
79 | }
80 |
81 | bool FileList::gotoLastItem()
82 | {
83 | return gotoItem(fList->currentIndex().row() - 1);
84 | }
85 |
86 | bool FileList::gotoNextItem()
87 | {
88 | return gotoItem(fList->currentIndex().row() + 1);
89 | }
90 |
91 | void FileList::finishedCurrentItem()
92 | {
93 | QListWidgetItem* item = fList->item(nowIndex);
94 | if (item->background().color() != QColor("#7fffd4"))
95 | {
96 | item->setBackground(QBrush(QColor("#7fffd4")));
97 | finishedNumber++;
98 | }
99 | }
100 |
101 | void FileList::resetProgress()
102 | {
103 | progressBar->setRange(0, fList->count());
104 | progressBar->reset();
105 | finishedNumber = 0;
106 | progressBar->setValue(0);
107 | progressBar->setFormat("0 / %m");
108 | }
109 |
110 | void FileList::progressUpAdd()
111 | {
112 | progressBar->setValue(finishedNumber);
113 | progressBar->setFormat(QString::number(finishedNumber) + " / %m");
114 | }
115 |
--------------------------------------------------------------------------------
/labcd/widgets/filelist.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | class FileList : public QWidget
8 | {
9 | Q_OBJECT
10 |
11 | private:
12 | QListWidget* fList = nullptr; // t1Files
13 | QProgressBar* progressBar = nullptr; // 进度条
14 | QStringList t2Files;
15 | int nowIndex = 0;
16 | int finishedNumber = 0;
17 |
18 | QString getGTJsonPath(QString t1Path);
19 |
20 | public:
21 | FileList(QWidget* parent = nullptr);
22 | ~FileList();
23 | void addFileNames(QStringList t1List, QStringList t2List);
24 | bool gotoItem(int index);
25 | bool gotoItem(QListWidgetItem* item);
26 | bool gotoLastItem();
27 | bool gotoNextItem();
28 | void finishedCurrentItem();
29 | void resetProgress();
30 | void progressUpAdd();
31 |
32 | signals:
33 | void FileClickRequest(QString t1Path, QString t2Path, QString jsonPath);
34 | void saveLastFileRequest();
35 | };
36 |
--------------------------------------------------------------------------------
/labcd/widgets/labeltable.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "labeltable.h"
10 |
11 | LabelTable::LabelTable(QWidget* parent)
12 | : QWidget(parent)
13 | {
14 | nowLabel = new Label();
15 | // 颜色表
16 | cMap = new ColorMap();
17 | // 布局
18 | QVBoxLayout* vLayout = new QVBoxLayout(this);
19 | // 标签列表
20 | labelTable = new QTableWidget(this);
21 | labelTable->setRowCount(0);
22 | labelTable->setColumnCount(4);
23 | labelTable->setColumnWidth(2, 50);
24 | auto labTabHHeader = labelTable->horizontalHeader();
25 | labTabHHeader->hide();
26 | labTabHHeader->setSectionResizeMode(QHeaderView::Stretch); //铺满
27 | // 仅文字框宽度自适应
28 | labTabHHeader->setDefaultSectionSize(30);
29 | labTabHHeader->setSectionResizeMode(0, QHeaderView::Fixed);
30 | labTabHHeader->setSectionResizeMode(2, QHeaderView::Fixed);
31 | labTabHHeader->setSectionResizeMode(3, QHeaderView::Fixed);
32 | auto labTabVHeader = labelTable->verticalHeader();
33 | labTabVHeader->hide();
34 | // 选择、切换颜色和删除
35 | connect(labelTable, &QTableWidget::cellClicked, this, &LabelTable::clickItem);
36 | // 标签改变文本
37 | connect(labelTable, &QTableWidget::cellChanged, [=](int row, int column) {
38 | if (column == 1)
39 | nowLabel->setName(labelTable->item(row, 1)->text());
40 | });
41 | // 初始标签
42 | addLabelItem(true);
43 | vLayout->addWidget(labelTable);
44 | // 添加标签按钮
45 | QPushButton* btnAddLabel = new QPushButton(tr("添加标签"), this);
46 | btnAddLabel->setIcon(QIcon(":/docks/resources/AddLabel.png"));
47 | connect(btnAddLabel, &QPushButton::clicked, this, &LabelTable::addLabelItem);
48 | vLayout->addWidget(btnAddLabel);
49 | // 加载
50 | setLayout(vLayout);
51 | }
52 |
53 | LabelTable::~LabelTable()
54 | {
55 |
56 | }
57 |
58 | void LabelTable::clickItem(int row, int column)
59 | {
60 | int nowIndex = labelTable->item(row, 0)->text().toInt();
61 | QString nowName = labelTable->item(row, 1)->text();
62 | QColor nowColor = labelTable->item(row, 2)->background().color();
63 | nowLabel->reSet(nowIndex, nowName, nowColor);
64 | if (column == 2 && row != 0) // 换颜色
65 | {
66 | QColor newColor = QColorDialog::getColor(
67 | nowColor, this, tr("标签颜色选择"));
68 | if (newColor.isValid())
69 | {
70 | labelTable->item(row, 2)->setBackground(newColor);
71 | nowLabel->setColor(newColor);
72 | emit colorChanged(row, newColor);
73 | }
74 | }
75 | else if (column == 3 && row != 0) // 删除
76 | {
77 | if (nowLabel->getIndex() == row)
78 | nowLabel->init();
79 | labelTable->removeRow(row);
80 | for (int r = 0; r < labelTable->rowCount(); ++r)
81 | {
82 | labelTable->item(r, 0)->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
83 | labelTable->item(r, 0)->setText(QString::number(r));
84 | labelTable->item(r, 0)->setFlags(
85 | labelTable->item(r, 0)->flags() & ~Qt::ItemIsEditable
86 | );
87 | }
88 | }
89 | else // 点击特效
90 | {
91 | for (int col = 0; col < 2; col++)
92 | {
93 | for (int r = 0; r < labelTable->rowCount(); ++r)
94 | labelTable->item(r, col)->setBackground(
95 | QColor(255, 255, 255));
96 | labelTable->item(row, col)->setBackground(
97 | QColor(48, 140, 198));
98 | labelTable->item(row, 0)->setSelected(true);
99 | }
100 | emit labelSelected(nowLabel);
101 | }
102 | }
103 |
104 | void LabelTable::createLabelItem(int _index, QString _name, QColor _color)
105 | {
106 | labelTable->insertRow(labelTable->rowCount()); // 插入行
107 | QTableWidgetItem* indexItem = new QTableWidgetItem();
108 | indexItem->setText(QString::number(_index));
109 | indexItem->setTextAlignment(Qt::AlignCenter);
110 | indexItem->setFlags(indexItem->flags() & ~Qt::ItemIsEditable);
111 | labelTable->setItem(_index, 0, indexItem); // 序号
112 | QTableWidgetItem* nameItem = new QTableWidgetItem();
113 | nameItem->setText(_name);
114 | nameItem->setFlags(Qt::ItemIsEnabled | Qt::ItemIsEditable);
115 | labelTable->setItem(_index, 1, nameItem); // 名称
116 | QTableWidgetItem* colorItem = new QTableWidgetItem();
117 | colorItem->setBackground(_color);
118 | colorItem->setFlags(Qt::ItemIsEnabled);
119 | labelTable->setItem(_index, 2, colorItem); // 颜色
120 | QTableWidgetItem* delItem = new QTableWidgetItem();
121 | delItem->setIcon(QIcon(":/docks/resources/Delete.png"));
122 | delItem->setFlags(Qt::ItemIsEnabled);
123 | labelTable->setItem(_index, 3, delItem); // 删除按钮
124 | }
125 |
126 | int LabelTable::getLen()
127 | {
128 | return labelTable->rowCount();
129 | }
130 |
131 | QColor LabelTable::getColorByIndex(int index)
132 | {
133 | if (index > labelTable->rowCount() - 1 || index < 0)
134 | exit(-1);
135 | return labelTable->item(index, 2)->background().color();
136 | }
137 |
138 | void LabelTable::addLabelItem(bool init)
139 | {
140 | if (init)
141 | {
142 | createLabelItem(0, tr("背景"), QColor(0, 0, 0));
143 | labelTable->item(0, 1)->setFlags(
144 | labelTable->item(0, 1)->flags() & ~Qt::ItemIsEditable);
145 | labelTable->item(0, 3)->setIcon(QIcon(":/docks/resources/CantDelete.png"));
146 | }
147 | else
148 | {
149 | int idx = labelTable->rowCount();
150 | createLabelItem(idx, "", cMap->getColor());
151 | }
152 | }
153 |
154 | void LabelTable::exportLabelToFile(QString path)
155 | {
156 | Json::StyledWriter writer;
157 | Json::Value root;
158 | std::ofstream os;
159 | os.open(path.toStdString());
160 | // 写入json内容
161 | for (int i = 1; i < labelTable->rowCount(); ++i)
162 | {
163 | Json::Value leaf;
164 | leaf["index"] = labelTable->item(i, 0)->text().toInt();
165 | leaf["classname"] = labelTable->item(i, 1)->text().toStdString();
166 | QColor c = labelTable->item(i, 2)->background().color();
167 | leaf["color"]["R"] = c.red();
168 | leaf["color"]["G"] = c.green();
169 | leaf["color"]["B"] = c.blue();
170 | root.append(leaf);
171 | }
172 | os << writer.write(root);
173 | os.close();
174 | }
175 |
176 | bool LabelTable::importLabelFromFile(QString path)
177 | {
178 | std::ifstream ifs(path.toStdString(), std::ios::binary);
179 | if (!ifs.is_open())
180 | return false;
181 | Json::Reader reader;
182 | Json::Value root;
183 | // 解析json内容
184 | if (reader.parse(ifs, root))
185 | {
186 | for (int i = 0; i < root.size(); i++)
187 | {
188 | int jIndex = root[i]["index"].asInt();
189 | QString jClassName = QString(root[i]["classname"].asCString());
190 | QColor jColor = QColor(
191 | root[i]["color"]["R"].asInt(),
192 | root[i]["color"]["G"].asInt(),
193 | root[i]["color"]["B"].asInt()
194 | );
195 | createLabelItem(jIndex, jClassName, jColor); // 恢复标签
196 | }
197 | }
198 | ifs.close();
199 | cMap->setIndex(root.size()); // 移动色表
200 | return true;
201 | }
202 |
203 | void LabelTable::changeLabelDuotoAddPolyJson(int index, QColor color)
204 | {
205 | if (index > labelTable->rowCount() - 1)
206 | {
207 | for (int i = 0; i < index - labelTable->rowCount() + 1; ++i)
208 | LabelTable::createLabelItem(
209 | labelTable->rowCount() + i, "", color);
210 | }
211 | }
212 |
--------------------------------------------------------------------------------
/labcd/widgets/labeltable.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include "../utils/label.h"
8 | #include "../utils/colormap.h"
9 |
10 | class LabelTable : public QWidget
11 | {
12 | Q_OBJECT
13 |
14 | private:
15 | QTableWidget* labelTable = nullptr;
16 | Label* nowLabel = nullptr;
17 | ColorMap* cMap = nullptr;
18 |
19 | void clickItem(int row, int column);
20 | void createLabelItem(int _index, QString _name, QColor _color);
21 |
22 | public:
23 | LabelTable(QWidget* parent = nullptr);
24 | ~LabelTable();
25 | int getLen();
26 | QColor getColorByIndex(int index);
27 | void addLabelItem(bool init = false);
28 | void exportLabelToFile(QString path);
29 | bool importLabelFromFile(QString path);
30 | void changeLabelDuotoAddPolyJson(int index, QColor color);
31 |
32 | signals:
33 | void labelSelected(Label* nowLabel);
34 | void colorChanged(int labelIndex, QColor newColor);
35 | };
36 |
--------------------------------------------------------------------------------
/labcd/widgets/labgrid.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include "labpolygon.h"
4 | #include "labgrid.h"
5 | #include "annotationscence.h"
6 | #include "opttypes.h"
7 |
8 | LabGrid::LabGrid(
9 | LabPolygon* _annItem,
10 | int _index,
11 | QColor _color,
12 | int _imgHeight,
13 | int _imgWidth
14 | )
15 | {
16 | // 初始化
17 | annItem = _annItem;
18 | index = _index;
19 | color = _color;
20 | color.setAlphaF(1.0);
21 | imgHeight = _imgHeight;
22 | imgWidth = _imgWidth;
23 | // 设置
24 | updateSize();
25 | setPath(circlePath);
26 | setBrush(color);
27 | setPen(QPen(color, 1));
28 | setFlag(QGraphicsItem::ItemIsSelectable, true);
29 | setFlag(QGraphicsItem::ItemIsMovable, true);
30 | setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
31 | setFlag(QGraphicsItem::ItemIsFocusable, true);
32 | setAcceptHoverEvents(true);
33 | setZValue(12);
34 | setCursor(QCursor(Qt::PointingHandCursor));
35 | }
36 |
37 | LabGrid::~LabGrid()
38 | {
39 |
40 | }
41 |
42 | void LabGrid::setColor(QColor c)
43 | {
44 | setBrush(c);
45 | setPen(QPen(c, 1));
46 | color = c;
47 | }
48 |
49 | void LabGrid::updateSize(double s)
50 | {
51 | double size = minSize;
52 | circlePath.addEllipse(QRectF(-size, -size, size * s, size * s));
53 | squarePath.addRect(QRectF(-size, -size, size * s, size * s));
54 | if (hovering)
55 | setPath(squarePath);
56 | else
57 | setPath(circlePath);
58 | }
59 |
60 | void LabGrid::hoverEnterEvent(QGraphicsSceneHoverEvent* ev)
61 | {
62 | setPath(squarePath);
63 | setBrush(QColor(0, 0, 0, 0));
64 | annItem->itemHovering = true;
65 | hovering = true;
66 | emit annItem->scene()->mouseOptRequest(
67 | annItem->index, index, OptTypes::GridHoverEnter, ev);
68 | QGraphicsPathItem::hoverEnterEvent(ev);
69 | }
70 |
71 | void LabGrid::hoverLeaveEvent(QGraphicsSceneHoverEvent* ev)
72 | {
73 | setPath(circlePath);
74 | setBrush(color);
75 | annItem->itemHovering = false;
76 | hovering = false;
77 | emit annItem->scene()->mouseOptRequest(
78 | annItem->index, index, OptTypes::GridHoverLeave, ev);
79 | QGraphicsPathItem::hoverLeaveEvent(ev);
80 | }
81 |
82 | void LabGrid::mousePressEvent(QGraphicsSceneMouseEvent* ev)
83 | {
84 | if (ev->button() == Qt::LeftButton)
85 | {
86 | annItem->scene()->clearSelection();
87 | setSelected(true);
88 | emit annItem->scene()->mouseOptRequest(
89 | annItem->index, index, OptTypes::GridMousePress, ev);
90 | QGraphicsPathItem::mousePressEvent(ev);
91 | }
92 | }
93 |
94 | void LabGrid::mouseReleaseEvent(QGraphicsSceneMouseEvent* ev)
95 | {
96 | setSelected(false);
97 | emit annItem->scene()->mouseOptRequest(
98 | annItem->index, index, OptTypes::GridMouseRelease, ev);
99 | QGraphicsPathItem::mouseReleaseEvent(ev);
100 | }
101 |
102 | void LabGrid::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev)
103 | {
104 | // 创建中无法删除
105 | if (!annItem->scene()->drawing)
106 | {
107 | emit annItem->scene()->mouseOptRequest(
108 | annItem->index, index, OptTypes::GridMouseDoubleClick, ev);
109 | annItem->removeFocusPoint(index);
110 | }
111 | }
112 |
113 | void LabGrid::mouseMoveEvent(QGraphicsSceneMouseEvent* ev)
114 | {
115 | emit annItem->scene()->mouseOptRequest(
116 | annItem->index, index, OptTypes::GridMouseMove, ev);
117 | QGraphicsPathItem::mouseMoveEvent(ev);
118 | }
119 |
120 | QVariant LabGrid::itemChange(
121 | GraphicsItemChange change, const QVariant& value
122 | )
123 | {
124 | double x, y;
125 | QVariant tmpVal = QVariant(value);
126 | if (change == QGraphicsItem::ItemPositionChange && isEnabled())
127 | {
128 | if (tmpVal.toPointF().x() > imgWidth)
129 | x = imgWidth;
130 | else if (tmpVal.toPointF().x() < 0)
131 | x = 0;
132 | else
133 | x = tmpVal.toPointF().x();
134 | if (tmpVal.toPointF().y() > imgHeight)
135 | y = imgHeight;
136 | else if (tmpVal.toPointF().y() < 0)
137 | y = 0;
138 | else
139 | y = tmpVal.toPointF().y();
140 | tmpVal = QPointF(x, y);
141 | annItem->movePoint(index, tmpVal.toPointF());
142 | }
143 | return QGraphicsPathItem::itemChange(change, tmpVal);
144 | }
145 |
146 | QPainterPath LabGrid::shape()
147 | {
148 | QPainterPath newPath;
149 | QPointF p = mapFromScene(pos());
150 | newPath.addEllipse(p, 2 * minSize, 2 * minSize);
151 | return newPath;
152 | }
153 |
--------------------------------------------------------------------------------
/labcd/widgets/labgrid.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | class LabPolygon;
9 |
10 | class LabGrid : public QGraphicsPathItem
11 | {
12 | private:
13 | const double minSize = 0.8;
14 | const double maxSize = 1.5;
15 | QPainterPath circlePath;
16 | QPainterPath squarePath;
17 |
18 | public:
19 | LabPolygon* annItem;
20 | bool hovering = false;
21 | int index;
22 | QColor color;
23 | int imgHeight;
24 | int imgWidth;
25 |
26 | LabGrid(
27 | LabPolygon* _annItem,
28 | int _index,
29 | QColor _color,
30 | int _imgHeight,
31 | int _imgWidth
32 | );
33 | ~LabGrid();
34 | void setColor(QColor c);
35 | void updateSize(double s = 2.0);
36 | // 事件
37 | void hoverEnterEvent(QGraphicsSceneHoverEvent* ev);
38 | void hoverLeaveEvent(QGraphicsSceneHoverEvent* ev);
39 | void mousePressEvent(QGraphicsSceneMouseEvent* ev);
40 | void mouseReleaseEvent(QGraphicsSceneMouseEvent* ev);
41 | void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev);
42 | void mouseMoveEvent(QGraphicsSceneMouseEvent* ev);
43 | // 重写方法
44 | QVariant itemChange(
45 | GraphicsItemChange change, const QVariant& value);
46 | QPainterPath shape();
47 | };
48 |
--------------------------------------------------------------------------------
/labcd/widgets/labline.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "labpolygon.h"
3 | #include "labline.h"
4 | #include "annotationscence.h"
5 | #include "opttypes.h"
6 |
7 | LabLine::LabLine(
8 | LabPolygon* _annItem,
9 | int _index,
10 | QColor _color
11 | )
12 | {
13 | // 初始化
14 | annItem = _annItem;
15 | index = _index;
16 | color = _color;
17 | // 设置
18 | setPen(QPen(color, minWidth));
19 | setZValue(11);
20 | setFlag(QGraphicsItem::ItemIsSelectable, true);
21 | setFlag(QGraphicsItem::ItemIsFocusable, true);
22 | setAcceptHoverEvents(true);
23 | setBoundingRegionGranularity(0.5);
24 | updateWidth();
25 | }
26 |
27 | LabLine::~LabLine()
28 | {
29 |
30 | }
31 |
32 | void LabLine::setColor(QColor c)
33 | {
34 | setPen(QPen(color, minWidth));
35 | color = c;
36 | updateWidth();
37 | }
38 |
39 | void LabLine::updateWidth()
40 | {
41 | setPen(QPen(color, minWidth));
42 | }
43 |
44 | QPolygonF LabLine::boundingPolygon()
45 | {
46 | double w = minWidth * 1.5;
47 | w = w < 2 ? w : 2;
48 | QPointF start = line().p1();
49 | QPointF end = line().p2();
50 | QPointF dir = start - end;
51 | double dx, dy;
52 | dx = -dir.y();
53 | dy = dir.x();
54 | double norm = sqrt(dx * dx + dy * dy);
55 | dx /= (norm + 1e-16);
56 | dy /= (norm + 1e-16);
57 | QPointF point(dx * w, dy * w);
58 | QList pointList = QList() << \
59 | start - point << start + point << \
60 | end + point << end - point;
61 | QPolygonF poly = QPolygonF(pointList);
62 | return poly;
63 | }
64 |
65 | void LabLine::hoverEnterEvent(QGraphicsSceneHoverEvent* ev)
66 | {
67 | boundingPolygon();
68 | annItem->lineHovering = true;
69 | setPen(QPen(color, minWidth * 1.4));
70 | emit annItem->scene()->mouseOptRequest(
71 | annItem->index, index, OptTypes::LineHoverEnter, ev);
72 | QGraphicsLineItem::hoverEnterEvent(ev);
73 | }
74 |
75 | void LabLine::hoverLeaveEvent(QGraphicsSceneHoverEvent* ev)
76 | {
77 | annItem->lineHovering = false;
78 | setPen(QPen(color, minWidth));
79 | emit annItem->scene()->mouseOptRequest(
80 | annItem->index, index, OptTypes::LineHoverLeave, ev);
81 | QGraphicsLineItem::hoverLeaveEvent(ev);
82 | }
83 |
84 | void LabLine::mousePressEvent(QGraphicsSceneMouseEvent* ev)
85 | {
86 | if (ev->button() == Qt::LeftButton)
87 | {
88 | annItem->scene()->clearSelection();
89 | setSelected(true);
90 | emit annItem->scene()->mouseOptRequest(
91 | annItem->index, index, OptTypes::LineMousePress, ev);
92 | QGraphicsLineItem::mousePressEvent(ev);
93 | }
94 | }
95 |
96 | void LabLine::mouseReleaseEvent(QGraphicsSceneMouseEvent* ev)
97 | {
98 | setSelected(false);
99 | emit annItem->scene()->mouseOptRequest(
100 | annItem->index, index, OptTypes::LineMouseRelease, ev);
101 | QGraphicsLineItem::mouseReleaseEvent(ev);
102 | }
103 |
104 | void LabLine::mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev)
105 | {
106 | setPen(QPen(color, minWidth));
107 | annItem->addPointMiddle(index, ev->pos());
108 | emit annItem->scene()->mouseOptRequest(
109 | annItem->index, index, OptTypes::LineMouseDoubleClick, ev);
110 | }
111 |
112 | QPainterPath LabLine::shape()
113 | {
114 | QPainterPath path;
115 | path.addPolygon(boundingPolygon());
116 | return path;
117 | }
118 |
--------------------------------------------------------------------------------
/labcd/widgets/labline.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | class LabPolygon;
9 |
10 | class LabLine : public QGraphicsLineItem
11 | {
12 | private:
13 | const double minWidth = 0.5;
14 | const double maxWidth = 1;
15 |
16 | public:
17 | LabPolygon* annItem;
18 | int index;
19 | QColor color;
20 |
21 | LabLine(
22 | LabPolygon* _annItem,
23 | int _index,
24 | QColor _color
25 | );
26 | ~LabLine();
27 | void setColor(QColor c);
28 | void updateWidth();
29 | QPolygonF boundingPolygon();
30 | // 事件
31 | void hoverEnterEvent(QGraphicsSceneHoverEvent* ev);
32 | void hoverLeaveEvent(QGraphicsSceneHoverEvent* ev);
33 | void mousePressEvent(QGraphicsSceneMouseEvent* ev);
34 | void mouseReleaseEvent(QGraphicsSceneMouseEvent* ev);
35 | void mouseDoubleClickEvent(QGraphicsSceneMouseEvent* ev);
36 | // 重写方法
37 | QPainterPath shape();
38 | };
39 |
--------------------------------------------------------------------------------
/labcd/widgets/labpolygon.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "annotationscence.h"
5 | #include "labpolygon.h"
6 | #include "opttypes.h"
7 |
8 | LabPolygon::LabPolygon(
9 | AnnotationScence* _nSence,
10 | int _index,
11 | int _labelIndex,
12 | int _imgWidth,
13 | int _imgHeight,
14 | QColor _insideColor,
15 | QColor _borderColor,
16 | double _opacity
17 | )
18 | {
19 | // 初始化
20 | nSence = _nSence;
21 | index = _index;
22 | labelIndex = _labelIndex;
23 | imgWidth = _imgWidth;
24 | imgHeight = _imgHeight;
25 | insideColor = _insideColor;
26 | halfInsideColor = _insideColor;
27 | borderColor = _borderColor;
28 | opacity = _opacity;
29 | // 设置颜色
30 | setZValue(10);
31 | insideColor.setAlphaF(opacity);
32 | halfInsideColor.setAlphaF(opacity / 2);
33 | setBrush(halfInsideColor);
34 | borderColor.setAlphaF(0.8);
35 | setPen(QPen(borderColor));
36 | setAcceptHoverEvents(true);
37 | // 其他设置
38 | setFlag(QGraphicsItem::ItemIsSelectable, true);
39 | setFlag(QGraphicsItem::ItemIsMovable, false);
40 | setFlag(QGraphicsItem::ItemSendsGeometryChanges, true);
41 | setFlag(QGraphicsItem::ItemIsFocusable, true);
42 | setCursor(QCursor(Qt::PointingHandCursor));
43 | }
44 |
45 | LabPolygon::~LabPolygon()
46 | {
47 |
48 | }
49 |
50 | QList LabPolygon::getPointsNotPtr()
51 | {
52 | QList tmpPoints;
53 | for (QPointF* p : mPoints)
54 | tmpPoints.push_back(*p);
55 | return tmpPoints;
56 | }
57 |
58 | int LabPolygon::getLen()
59 | {
60 | return mPoints.count();
61 | }
62 |
63 | int LabPolygon::getLabelIndex()
64 | {
65 | return labelIndex;
66 | }
67 |
68 | void LabPolygon::setColor(QColor _insideColor, QColor _borderColor)
69 | {
70 | insideColor = _insideColor;
71 | insideColor.setAlphaF(opacity);
72 | halfInsideColor = QColor(insideColor);
73 | halfInsideColor.setAlphaF(opacity / 2);
74 | setBrush(halfInsideColor);
75 | borderColor = _borderColor;
76 | borderColor.setAlphaF(0.8);
77 | setPen(QPen(borderColor));
78 | for (LabGrid* item : mItems)
79 | item->setColor(borderColor);
80 | for (LabLine* line : mLines)
81 | line->setColor(borderColor);
82 | }
83 |
84 | QColor LabPolygon::getColor()
85 | {
86 | return borderColor;
87 | }
88 |
89 | QList LabPolygon::getScenePos()
90 | {
91 | QList points;
92 | for (QPointF* p : mPoints)
93 | {
94 | p = new QPointF(mapToScene(*p));
95 | points.push_back(p);
96 | }
97 | return points;
98 | }
99 |
100 | void LabPolygon::addPointMiddle(int lineIndex, QPointF point)
101 | {
102 | // 添加点
103 | LabGrid* gripItem = new LabGrid(
104 | this, lineIndex + 1, borderColor, imgHeight, imgWidth);
105 | gripItem->setEnabled(false);
106 | gripItem->setPos(point);
107 | scene()->addItem(gripItem);
108 | gripItem->updateSize();
109 | gripItem->setEnabled(true);
110 | for (int i = lineIndex + 1; i < mItems.count(); i++)
111 | mItems.at(i)->index += 1;
112 | mItems.insert(static_cast(lineIndex) + 1, gripItem);
113 | QPointF* gripPoint = new QPointF(mapFromScene(point));
114 | mPoints.insert(static_cast(lineIndex) + 1, gripPoint);
115 | setPolygon(QPolygonF(getPointsNotPtr()));
116 | // 连线
117 | for (int i = lineIndex + 1; i < mLines.count(); i++)
118 | mLines.at(i)->index += 1;
119 | QLineF line1 = QLineF(mapToScene(*(mPoints.at(lineIndex))), point);
120 | mLines.at(lineIndex)->setLine(line1);
121 | LabLine* lineItem = new LabLine(this, lineIndex + 1, borderColor);
122 | QLineF line2 = QLineF(
123 | point,
124 | mapToScene(*(mPoints.at((lineIndex + 2) % getLen())))
125 | );
126 | lineItem->setLine(line2);
127 | mLines.insert(static_cast(lineIndex) + 1, lineItem);
128 | scene()->addItem(lineItem);
129 | lineItem->updateWidth();
130 | }
131 |
132 | void LabPolygon::addPointLast(QPointF point)
133 | {
134 | LabGrid* grip = new LabGrid(this, getLen(), borderColor, imgHeight, imgWidth);
135 | scene()->addItem(grip);
136 | mItems.push_back(grip);
137 | grip->updateSize();
138 | grip->setPos(point);
139 | if (getLen() == 0)
140 | {
141 | LabLine* line = new LabLine(this, getLen(), borderColor);
142 | scene()->addItem(line);
143 | mLines.push_back(line);
144 | line->setLine(QLineF());
145 | }
146 | else
147 | {
148 | mLines.at(mLines.count() - 1)->setLine(
149 | QLineF(*mPoints.at(mPoints.count() - 1), point));
150 | LabLine* line = new LabLine(this, getLen(), borderColor);
151 | scene()->addItem(line);
152 | mLines.push_back(line);
153 | line->setLine(QLineF(point, *mPoints.at(0)));
154 | }
155 | QPointF* nPoint = new QPointF(point);
156 | mPoints.push_back(nPoint);
157 | setPolygon(QPolygonF(getPointsNotPtr()));
158 | }
159 |
160 | void LabPolygon::remove()
161 | {
162 | for (LabGrid* grip : mItems)
163 | scene()->removeItem(grip);
164 | for (LabLine* line : mLines)
165 | scene()->removeItem(line);
166 | while (mItems.count() != 0)
167 | mItems.pop_back();
168 | while (mLines.count() != 0)
169 | mLines.pop_back();
170 | scene()->polygonItems.removeAll(this);
171 | // 重排序
172 | for (int i = 0; i < scene()->polygonItems.count(); i++)
173 | scene()->polygonItems[i]->index = i;
174 | scene()->removeItem(this);
175 | delete this;
176 | }
177 |
178 | void LabPolygon::removeFocusPoint(int preFocusIndex)
179 | {
180 | int focusIndex = -1;
181 | if (preFocusIndex == -1)
182 | {
183 | for (int i = 0; i < mItems.count(); ++i)
184 | {
185 | if (mItems.at(i)->hasFocus())
186 | {
187 | focusIndex = i;
188 | break;
189 | }
190 | }
191 | }
192 | else
193 | focusIndex = preFocusIndex;
194 | if (focusIndex != -1)
195 | {
196 | if (getLen() <= 3)
197 | remove();
198 | else
199 | {
200 | mPoints.removeAt(focusIndex);
201 | setPolygon(QPolygonF(getPointsNotPtr()));
202 | scene()->removeItem(mItems.at(focusIndex));
203 | mItems.removeAt(focusIndex);
204 | for (int i = focusIndex; i < mItems.count(); ++i)
205 | {
206 | mItems.at(i)->index -= 1;
207 | if (mItems.at(i)->index < 0)
208 | mItems.at(i)->index += mItems.count();
209 | }
210 | scene()->removeItem(mLines.at(focusIndex));
211 | mLines.removeAt(focusIndex);
212 | int lastIndex = focusIndex - 1;
213 | if (lastIndex < 0)
214 | lastIndex += mItems.count();
215 | QLineF line(
216 | mapToScene(*mPoints.at(lastIndex % getLen())),
217 | mapToScene(*mPoints.at(focusIndex % getLen()))
218 | );
219 | mLines.at(lastIndex % getLen())->setLine(line);
220 | for (int i = focusIndex; i < mLines.count(); ++i)
221 | {
222 | mLines.at(i)->index -= 1;
223 | if (mLines.at(i)->index < 0)
224 | mLines.at(i)->index += mLines.count();
225 | }
226 | }
227 | }
228 | }
229 |
230 | void LabPolygon::movePoint(int index, QPointF point)
231 | {
232 | if (0 <= index && index < mPoints.count())
233 | {
234 | mPoints[index] = new QPointF(mapFromScene(point));
235 | setPolygon(QPolygonF(getPointsNotPtr()));
236 | moveLine(index);
237 | }
238 | }
239 |
240 | void LabPolygon::moveLine(int index)
241 | {
242 | if (!noMove)
243 | {
244 | QLineF line1 = QLineF(
245 | mapToScene(*mPoints.at(index)),
246 | mapToScene(*mPoints.at((index + 1) % getLen()))
247 | );
248 | mLines[index]->setLine(line1);
249 | int lastIndex = index - 1;
250 | if (lastIndex < 0)
251 | lastIndex += mPoints.count();
252 | QLineF line2 = QLineF(
253 | mapToScene(*mPoints.at(lastIndex % getLen())),
254 | mapToScene(*mPoints.at(index))
255 | );
256 | mLines[lastIndex % getLen()]->setLine(line2);
257 | }
258 | }
259 |
260 | void LabPolygon::moveItem(int index, QPointF point)
261 | {
262 | if (0 <= index && index < mItems.count())
263 | {
264 | mItems[index]->setEnabled(false);
265 | mItems[index]->setPos(point);
266 | mItems[index]->setEnabled(true);
267 | moveLine(index);
268 | }
269 | }
270 |
271 | // hover指鼠标放在多边形上
272 | void LabPolygon::hoverEnterEvent(QGraphicsSceneHoverEvent* ev)
273 | {
274 | if (scene()->drawing)
275 | return;
276 | polyHovering = true;
277 | scene()->clearAllFocus();
278 | setBrush(insideColor);
279 | emit scene()->mouseOptRequest(index, -1, OptTypes::PolyHoverEnter, ev);
280 | QGraphicsPolygonItem::hoverEnterEvent(ev);
281 | }
282 |
283 | void LabPolygon::hoverLeaveEvent(QGraphicsSceneHoverEvent* ev)
284 | {
285 | if (scene()->drawing)
286 | return;
287 | polyHovering = false;
288 | if (!hasFocus())
289 | setBrush(halfInsideColor);
290 | emit scene()->mouseOptRequest(index, -1, OptTypes::PolyHoverLeave, ev);
291 | QGraphicsPolygonItem::hoverLeaveEvent(ev);
292 |
293 | }
294 |
295 | void LabPolygon::mousePressEvent(QGraphicsSceneMouseEvent* ev)
296 | {
297 | scene()->clearFocusAndSelected();
298 | if (scene()->drawing)
299 | return;
300 | if (ev->button() == Qt::LeftButton)
301 | {
302 | setSelected(true);
303 | setFocus();
304 | }
305 | emit scene()->mouseOptRequest(index, -1, OptTypes::PolyMousePress, ev);
306 | QGraphicsPolygonItem::mousePressEvent(ev);
307 | }
308 |
309 | void LabPolygon::mouseReleaseEvent(QGraphicsSceneMouseEvent* ev)
310 | {
311 | setSelected(false);
312 | emit scene()->mouseOptRequest(index, -1, OptTypes::PolyMouseRelease, ev);
313 | QGraphicsPolygonItem::mouseReleaseEvent(ev);
314 | }
315 |
316 | // focus指鼠标点击多边形上
317 | void LabPolygon::focusInEvent(QFocusEvent* ev)
318 | {
319 | if (scene()->drawing)
320 | return;
321 | setBrush(insideColor);
322 | emit scene()->mouseOptRequest(index, -1, OptTypes::PolyFocusIn, ev);
323 | }
324 |
325 | void LabPolygon::focusOutEvent(QFocusEvent* ev)
326 | {
327 | if (scene()->drawing)
328 | return;
329 | if (!polyHovering)
330 | setBrush(halfInsideColor);
331 | emit scene()->mouseOptRequest(index, -1, OptTypes::PolyFocusOut, ev);
332 | }
333 |
334 | QVariant LabPolygon::itemChange(
335 | GraphicsItemChange change, const QVariant& value)
336 | {
337 | if (change == QGraphicsItem::ItemPositionHasChanged)
338 | {
339 | for (int i = 0; i < mPoints.count(); i++)
340 | moveItem(i, mapToScene(*(mPoints.at(i))));
341 | }
342 | return QGraphicsPolygonItem::itemChange(change, value);
343 | }
344 |
345 | AnnotationScence* LabPolygon::scene()
346 | {
347 | return nSence;
348 | }
349 |
--------------------------------------------------------------------------------
/labcd/widgets/labpolygon.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include "labline.h"
7 | #include "labgrid.h"
8 |
9 | class AnnotationScence;
10 |
11 | class LabPolygon : public QGraphicsPolygonItem
12 | {
13 | private:
14 | AnnotationScence* nSence;
15 | double opacity = 0.5;
16 | QColor insideColor = QColor(255, 0, 0);
17 | QColor halfInsideColor = QColor(255, 0, 0);
18 | QColor borderColor = QColor(0, 255, 0);
19 |
20 | QList getPointsNotPtr(); // 将点的指针的list转为点的list
21 |
22 | public:
23 | QList mPoints;
24 | QList mItems;
25 | QList mLines;
26 | int index;
27 | int labelIndex;
28 | int imgWidth;
29 | int imgHeight;
30 | bool itemHovering = false;
31 | bool lineHovering = false;
32 | bool polyHovering = false;
33 | bool noMove = false;
34 | bool lastFocse = false;
35 |
36 | LabPolygon(
37 | AnnotationScence* _nSence,
38 | int _index,
39 | int _labelIndex,
40 | int _imgWidth,
41 | int _imgHeight,
42 | QColor _insideColor = QColor(255, 0, 0),
43 | QColor _borderColor = QColor(0, 255, 0),
44 | double _opacity = 0.5
45 | );
46 | ~LabPolygon();
47 | int getLen();
48 | int getLabelIndex();
49 | void setColor(QColor _insideColor, QColor _borderColor);
50 | QColor getColor();
51 | QList getScenePos();
52 | void addPointMiddle(int lineIndex, QPointF point);
53 | void addPointLast(QPointF point);
54 | void remove();
55 | void removeFocusPoint(int preFocusIndex = -1);
56 | void movePoint(int index, QPointF point);
57 | void moveLine(int index);
58 | void moveItem(int index, QPointF point);
59 | // 事件
60 | void hoverEnterEvent(QGraphicsSceneHoverEvent* ev);
61 | void hoverLeaveEvent(QGraphicsSceneHoverEvent* ev);
62 | void mousePressEvent(QGraphicsSceneMouseEvent* ev);
63 | void mouseReleaseEvent(QGraphicsSceneMouseEvent* ev);
64 | void focusInEvent(QFocusEvent* ev);
65 | void focusOutEvent(QFocusEvent* ev);
66 | // 重写
67 | QVariant itemChange(
68 | GraphicsItemChange change, const QVariant& value);
69 | AnnotationScence* scene();
70 | };
71 |
--------------------------------------------------------------------------------
/labcd/widgets/multcanvas.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include "multcanvas.h"
5 | #include "../utils/imgpress.h"
6 |
7 | MultCanvas::MultCanvas(QWidget* parent)
8 | : QWidget(parent)
9 | {
10 | QGridLayout* gLayout = new QGridLayout(this);
11 | t1Canva = new Canvas(this);
12 | gLayout->addWidget(t1Canva, 0, 0);
13 | t2Canva = new Canvas(this);
14 | gLayout->addWidget(t2Canva, 0, 1);
15 | /* 操作关联 */
16 | // 同步鼠标移动缩放
17 | connect(
18 | t1Canva->aView, &AnnotationView::syncRequest,
19 | t2Canva->aView, &AnnotationView::syncTranslate
20 | );
21 | connect(
22 | t2Canva->aView, &AnnotationView::syncRequest,
23 | t1Canva->aView, &AnnotationView::syncTranslate
24 | );
25 | // 同步垂直和水平滑动条位置
26 | connect(t1Canva, &Canvas::syncScroll, t2Canva, &Canvas::scroolTranslate);
27 | connect(t2Canva, &Canvas::syncScroll, t1Canva, &Canvas::scroolTranslate);
28 | // 加载标签和图像大小的同步
29 | connect(this, &MultCanvas::labelSelected, [=](Label* label) {
30 | t1Canva->aScene->getLabel(label);
31 | t2Canva->aScene->getLabel(label);
32 | });
33 | connect(this, &MultCanvas::imageLoaded, [=](int imgWidth, int imgHeight) {
34 | t1Canva->aScene->getImageSize(imgWidth, imgHeight);
35 | t2Canva->aScene->getImageSize(imgWidth, imgHeight);
36 | });
37 | // 同步画图
38 | connect(
39 | t1Canva->aScene, &AnnotationScence::mouseOptRequest,
40 | t2Canva->aScene, &AnnotationScence::copyMouseOpt
41 | );
42 | connect(
43 | t2Canva->aScene, &AnnotationScence::mouseOptRequest,
44 | t1Canva->aScene, &AnnotationScence::copyMouseOpt
45 | );
46 | // 同步十字丝
47 | connect(
48 | t1Canva->aView, &AnnotationView::mousePosChanged,
49 | t2Canva->aScene, &AnnotationScence::onMouseChanged
50 | );
51 | connect(
52 | t2Canva->aView, &AnnotationView::mousePosChanged,
53 | t1Canva->aScene, &AnnotationScence::onMouseChanged
54 | );
55 | setLayout(gLayout);
56 | }
57 |
58 | MultCanvas::~MultCanvas()
59 | {
60 |
61 | }
62 |
63 | void MultCanvas::loadImages(QString t1Path, QString t2Path, QString jsonPath)
64 | {
65 | QPixmap t1;
66 | QPixmap t2;
67 | bool t1Succ = ImagePress::openImage(t1Path, t1, projs, trans);
68 | bool t2Succ = ImagePress::openImage(t2Path, t2, projs, trans);
69 | if (!t1Succ || !t2Succ)
70 | QMessageBox::critical(
71 | this,
72 | tr("错误"),
73 | tr("无法打开图像文件。")
74 | );
75 | else
76 | {
77 | if (t1.width() != t2.width() || t1.height() != t2.height())
78 | QMessageBox::critical(
79 | this,
80 | tr("错误"),
81 | tr("两个时段的数据大小不一致。")
82 | );
83 | else
84 | {
85 | t1Canva->loadImageFromPixmap(t1);
86 | t2Canva->loadImageFromPixmap(t2);
87 | imageWidth = t1.width();
88 | imageHeight = t1.height();
89 | emit imageLoaded(imageWidth, imageHeight); // 发送大小
90 | // 加载已标注过的数据
91 | if (jsonPath != "")
92 | {
93 | t1Canva->loadJSONFromFile(jsonPath);
94 | t2Canva->loadJSONFromFile(jsonPath);
95 | }
96 | t1Canva->aScene->resetScence();
97 | t2Canva->aScene->resetScence();
98 | // CVA变化图
99 | cv::Mat imgT1 = ImagePress::qpixmapToCVMat(t1);
100 | cv::Mat imgT2 = ImagePress::qpixmapToCVMat(t2);
101 | cv::Mat imgDiff = ImagePress::CVA(imgT1, imgT2);
102 | emit addimgDiff(imgDiff);
103 | }
104 | }
105 | }
106 |
107 | void MultCanvas::clearFocusAndSelected()
108 | {
109 | t1Canva->aScene->clearFocusAndSelected();
110 | t2Canva->aScene->clearFocusAndSelected();
111 | }
112 |
113 | void MultCanvas::finished()
114 | {
115 | t1Canva->aScene->finished();
116 | t1Canva->aScene->finished();
117 | }
118 |
119 | QColor MultCanvas::getCrossPenColor()
120 | {
121 | return t1Canva->aScene->getCrossPenColor();
122 | }
123 |
124 | void MultCanvas::setCrossPenColor(QColor color)
125 | {
126 | t1Canva->aScene->setCrossPenColor(color);
127 | t2Canva->aScene->setCrossPenColor(color);
128 | }
129 |
--------------------------------------------------------------------------------
/labcd/widgets/multcanvas.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include "canvas.h"
6 | #include "../utils/label.h"
7 |
8 | class MultCanvas : public QWidget
9 | {
10 | Q_OBJECT
11 |
12 | public:
13 | Canvas* t1Canva = nullptr;
14 | Canvas* t2Canva = nullptr;
15 | int imageWidth = 0;
16 | int imageHeight = 0;
17 | std::string projs = "";
18 | double trans[6] = { 0, 1, 0, 0, 0, 1 };
19 |
20 | MultCanvas(QWidget* parent = nullptr);
21 | ~MultCanvas();
22 | void loadImages(QString t1Path, QString t2Path, QString jsonPath);
23 | void clearFocusAndSelected();
24 | void finished();
25 | QColor getCrossPenColor();
26 | void setCrossPenColor(QColor color);
27 |
28 | signals:
29 | void labelSelected(Label* label);
30 | void imageLoaded(int imgWidth, int imgHeight);
31 | void addimgDiff(cv::Mat imgDiff);
32 | };
33 |
--------------------------------------------------------------------------------
/labcd/widgets/opttypes.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | enum class OptTypes
4 | {
5 | SceneMousePress,
6 | SceneMouseDoubleClick,
7 | PolyHoverEnter,
8 | PolyHoverLeave,
9 | PolyMousePress,
10 | PolyMouseRelease,
11 | PolyFocusIn,
12 | PolyFocusOut,
13 | LineHoverEnter,
14 | LineHoverLeave,
15 | LineMousePress,
16 | LineMouseRelease,
17 | LineMouseDoubleClick,
18 | GridHoverEnter,
19 | GridHoverLeave,
20 | GridMousePress,
21 | GridMouseRelease,
22 | GridMouseMove,
23 | GridMouseDoubleClick
24 | };
25 |
--------------------------------------------------------------------------------