Codebase list hdrmerge / 9cf3550
Import Upstream version 0.5+git20200117 Gürkan Myczko authored 4 years ago root committed 4 years ago
94 changed file(s) with 10204 addition(s) and 0 deletion(s). Raw diff Collapse all Expand all
0 *.o
1 *.orig
2 *.swp
3 *~
4 .cproject
5 .project
6 .directory
7 .DS_Store
8 .kde*
9 .settings/
10 Makefile
11 Build*/
12 build*/
13 hdrmerge.kde*
14 moc_*
15 samples/
16 third_party/xmp_sdk
0 language: generic
1
2 compiler:
3 - g++
4
5 sudo: required
6
7 dist: trusty
8
9 addons:
10 apt:
11 packages:
12 - g++
13 - gettext
14 - intltool
15 - libqt4-dev
16 - autoconf
17 - automake
18 - libexiv2-dev
19 - libalglib-dev
20 - libboost-all-dev
21
22
23 before_script:
24 - mkdir -p build
25 - cd build
26 - export PKG_CONFIG_PATH=/app/lib/pkgconfig:${PKG_CONFIG_PATH}
27 - export LD_LIBRARY_PATH=/app/lib:${LD_LIBRARY_PATH}
28 - git clone https://github.com/LibRaw/LibRaw.git
29 - cd LibRaw
30 - autoreconf --install
31 #- automake --add-missing
32 #- autoconf
33 - pwd
34 - ls
35 - ./configure --prefix=/app
36 - make -j2
37 - sudo make install
38 - cd ..
39 - rm -rf LibRaw
40 - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=/app ..
41 - make -j2
42 - sudo make install
43 - cd ..
44
45 script:
46 - bash appimage/appimage.sh
0 # Project initiator:
1 Javier Celaya
2 <jcelaya@gmail.com>
3 https://github.com/jcelaya
0 cmake_minimum_required(VERSION 3.4)
1
2 cmake_policy(SET CMP0025 NEW)
3
4 project(hdrmerge)
5
6 if(WIN32)
7 if(NOT(ALGLIB_ROOT))
8 message(FATAL_ERROR "When building on Windows, you need to download 'alglib' source and define its root directory in ALGLIB_ROOT !")
9 else()
10 message(STATUS "ALGLIB_ROOT = ${ALGLIB_ROOT}")
11 endif()
12 endif()
13
14 if(APPLE)
15 if(NOT(ALGLIB_ROOT))
16 message(FATAL_ERROR "Passage of ALGLIB_ROOT to cmake failed. Download alglib and define its root directory in ALGLIB_ROOT")
17 else()
18 message(STATUS "ALGLIB_ROOT = ${ALGLIB_ROOT}")
19 endif()
20 endif()
21
22 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wno-unknown-pragmas")
23 set(PLATFORM "")
24
25 # Required and optional packages
26 set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${PROJECT_SOURCE_DIR}/cmake")
27
28 # Find includes in corresponding build directories
29 set(CMAKE_INCLUDE_CURRENT_DIR ON)
30
31 # Instruct CMake to run moc automatically when needed.
32 set(CMAKE_AUTOMOC ON)
33
34 # Generate resources automatically
35 set(CMAKE_AUTORCC ON)
36
37 find_package(Qt5 5.6 REQUIRED Core Widgets)
38
39 #include(${QT_USE_FILE})
40 add_definitions(${QT_DEFINITIONS})
41
42 find_package(LibRaw REQUIRED)
43 find_package(Exiv2 REQUIRED)
44 find_package(ZLIB REQUIRED)
45 find_package(OpenMP)
46
47 # Commented-out as it doesn't link
48 #find_package(Boost 1.46 COMPONENTS unit_test_framework)
49
50 if(WIN32)
51 set(ALGLIB_INCLUDES ${ALGLIB_ROOT}/src)
52 else()
53 find_package(Alglib REQUIRED)
54 endif()
55
56 include_directories(
57 "${LibRaw_INCLUDE_DIR}"
58 "${EXIV2_INCLUDE_DIR}"
59 "${ZLIB_INCLUDE_DIRS}"
60 "${ALGLIB_INCLUDES}"
61 )
62
63 if(NOT(CMAKE_BUILD_TYPE))
64 set(CMAKE_BUILD_TYPE Release)
65 endif()
66
67 if(CMAKE_BUILD_TYPE STREQUAL "Release")
68 if(OPENMP_FOUND)
69 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
70 endif()
71 if(CMAKE_COMPILER_IS_GNUCC)
72 set(STRIP -s)
73 endif()
74 endif()
75
76 # The version number.
77 set(HDRMERGE_VERSION_MAJOR 0)
78 set(HDRMERGE_VERSION_MINOR 5)
79 set(HDRMERGE_VERSION_REV 0)
80 set(HDRMERGE_VERSION ${HDRMERGE_VERSION_MAJOR}.${HDRMERGE_VERSION_MINOR}.${HDRMERGE_VERSION_REV})
81
82 # configure a header file to pass some of the CMake settings to the source code
83 configure_file(
84 "${PROJECT_SOURCE_DIR}/src/config.h.in"
85 "${PROJECT_BINARY_DIR}/config.h"
86 )
87
88 # add the binary tree to the search path for include files so that we will find config.h
89 include_directories("${PROJECT_BINARY_DIR}")
90
91 set(PLATFORM_SOURCES "")
92 if(WIN32)
93 # This is required by libraw.h
94 set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWIN32 -DLIBRAW_NODLL")
95 set(PLATFORM WIN32)
96 configure_file(
97 "${PROJECT_SOURCE_DIR}/data/winres.rc"
98 "${PROJECT_BINARY_DIR}/winres.rc"
99 )
100 set(PLATFORM_SOURCES "${PROJECT_BINARY_DIR}/winres.rc")
101 elseif(APPLE)
102 set(PLATFORM MACOSX_BUNDLE)
103 set(MACOSX_BUNDLE_ICON_FILE "icon")
104 set(MACOSX_BUNDLE_GUI_IDENTIFIER "org.hdrmerge")
105 set(MACOSX_BUNDLE_LONG_VERSION_STRING "HDRMerge for macOS v${HDRMERGE_VERSION}")
106 set(MACOSX_BUNDLE_BUNDLE_NAME "HDRMerge")
107 set(MACOSX_BUNDLE_SHORT_VERSION_STRING "${HDRMERGE_VERSION}")
108 set(MACOSX_BUNDLE_BUNDLE_VERSION "${HDRMERGE_VERSION}")
109 set(MACOSX_BUNDLE_INFO_COPYRIGHT "Copyright 2018 Javier Celaya")
110 set(PLATFORM_SOURCES "data/images/icon.icns")
111 endif()
112
113 # Sources and headers
114 set(hdrmerge_sources
115 src/Image.cpp
116 src/ImageStack.cpp
117 src/Bitmap.cpp
118 src/RawParameters.cpp
119 src/EditableMask.cpp
120 src/DngFloatWriter.cpp
121 src/TiffDirectory.cpp
122 src/BoxBlur.cpp
123 src/ExifTransfer.cpp
124 src/ImageIO.cpp
125 )
126
127 set(hdrmerge_gui_sources
128 src/AboutDialog.cpp
129 src/MainWindow.cpp
130 src/PreviewWidget.cpp
131 src/DraggableScrollArea.cpp
132 src/DngPropertiesDialog.cpp
133 src/LoadOptionsDialog.cpp
134 src/FileSystem.cpp
135 )
136
137 set(hdrmerge_qobject_headers
138 src/MainWindow.hpp
139 src/PreviewWidget.hpp
140 src/LoadOptionsDialog.hpp
141 src/DraggableScrollArea.hpp
142 src/DngPropertiesDialog.hpp
143 src/AboutDialog.hpp
144 src/FileSystem.hpp
145 )
146
147 set(hdrmerge_translations
148 data/hdrmerge_es.ts
149 )
150
151 if(WIN32)
152 set(alglib_sources
153 ${ALGLIB_ROOT}/src/alglibinternal.cpp
154 ${ALGLIB_ROOT}/src/alglibmisc.cpp
155 ${ALGLIB_ROOT}/src/ap.cpp
156 ${ALGLIB_ROOT}/src/dataanalysis.cpp
157 ${ALGLIB_ROOT}/src/diffequations.cpp
158 ${ALGLIB_ROOT}/src/fasttransforms.cpp
159 ${ALGLIB_ROOT}/src/integration.cpp
160 ${ALGLIB_ROOT}/src/interpolation.cpp
161 ${ALGLIB_ROOT}/src/linalg.cpp
162 ${ALGLIB_ROOT}/src/optimization.cpp
163 ${ALGLIB_ROOT}/src/solvers.cpp
164 ${ALGLIB_ROOT}/src/specialfunctions.cpp
165 ${ALGLIB_ROOT}/src/statistics.cpp
166 )
167 endif()
168
169 if(APPLE)
170 set(alglib_sources
171 ${ALGLIB_ROOT}/src/alglibinternal.cpp
172 ${ALGLIB_ROOT}/src/alglibmisc.cpp
173 ${ALGLIB_ROOT}/src/ap.cpp
174 ${ALGLIB_ROOT}/src/dataanalysis.cpp
175 ${ALGLIB_ROOT}/src/diffequations.cpp
176 ${ALGLIB_ROOT}/src/fasttransforms.cpp
177 ${ALGLIB_ROOT}/src/integration.cpp
178 ${ALGLIB_ROOT}/src/interpolation.cpp
179 ${ALGLIB_ROOT}/src/linalg.cpp
180 ${ALGLIB_ROOT}/src/optimization.cpp
181 ${ALGLIB_ROOT}/src/solvers.cpp
182 ${ALGLIB_ROOT}/src/specialfunctions.cpp
183 ${ALGLIB_ROOT}/src/statistics.cpp
184 )
185 endif()
186
187 # Libs
188 set(hdrmerge_libs
189 "${LibRaw_r_LIBRARIES}"
190 "${EXIV2_LIBRARY}"
191 "${ZLIB_LIBRARIES}"
192 )
193
194 if(WIN32)
195 get_filename_component(LIB_PATH "${EXIV2_LIBRARY}" PATH)
196 find_library(EXPAT_LIBRARY libexpat.a "${LIB_PATH}")
197 find_library(ICONV_LIBRARY libiconv.a "${LIB_PATH}")
198 find_library(GETTEXT_LIBRARY libintl.a "${LIB_PATH}")
199 set(hdrmerge_libs
200 ${hdrmerge_libs}
201 "${EXPAT_LIBRARY}"
202 "${GETTEXT_LIBRARY}"
203 "${ICONV_LIBRARY}"
204 psapi
205 wsock32
206 imm32
207 winmm
208 )
209 remove_definitions(-DQT_DLL)
210 else()
211 set(hdrmerge_libs
212 "${hdrmerge_libs}"
213 "${ALGLIB_LIBRARIES}"
214 )
215 endif()
216
217 #QT4_ADD_TRANSLATION(hdrmerge_qm ${hdrmerge_translations})
218
219 # Generate the XML version of hdrmerge_qm
220 foreach(file ${hdrmerge_qm})
221 get_filename_component(basename ${file} NAME)
222 set(HDRMERGE_QM_XML "${HDRMERGE_QM_XML}
223 <file>${basename}</file>")
224 endforeach()
225
226 configure_file(
227 "${PROJECT_SOURCE_DIR}/data/translations.qrc.in"
228 "${PROJECT_BINARY_DIR}/translations.qrc"
229 )
230
231 if(WIN32)
232 add_library(alglib-objects OBJECT ${alglib_sources})
233 endif()
234
235 if(APPLE)
236 add_library(alglib-objects OBJECT ${alglib_sources})
237 endif()
238
239 if(WIN32)
240 add_executable(hdrmerge ${PLATFORM}
241 src/main.cpp
242 src/Launcher.cpp
243 data/resources.qrc
244 ${hdrmerge_translations}
245 ${hdrmerge_sources}
246 ${hdrmerge_gui_sources}
247 ${hdrmerge_moc}
248 $<TARGET_OBJECTS:alglib-objects>
249 "${PLATFORM_SOURCES}"
250 )
251 elseif(APPLE)
252 add_executable(hdrmerge ${PLATFORM}
253 src/main.cpp
254 src/Launcher.cpp
255 data/resources.qrc
256 $<TARGET_OBJECTS:alglib-objects>
257 ${hdrmerge_translations}
258 ${hdrmerge_sources}
259 ${hdrmerge_gui_sources}
260 ${hdrmerge_moc}
261 "${PLATFORM_SOURCES}"
262 )
263 else()
264 add_executable(hdrmerge ${PLATFORM}
265 src/main.cpp
266 src/Launcher.cpp
267 data/resources.qrc
268 ${hdrmerge_translations}
269 ${hdrmerge_sources}
270 ${hdrmerge_gui_sources}
271 ${hdrmerge_moc}
272 "${PLATFORM_SOURCES}"
273 )
274 endif()
275
276 target_link_libraries(hdrmerge ${STRIP} ${hdrmerge_libs} Qt5::Widgets)
277
278 if(WIN32)
279 # Compile a target without GUI, for the .com executable
280 add_executable(hdrmerge-nogui
281 src/main.cpp
282 src/Launcher.cpp
283 data/resources.qrc
284 ${hdrmerge_translations}
285 ${hdrmerge_sources}
286 ${hdrmerge_moc}
287 $<TARGET_OBJECTS:alglib-objects>
288 "${PLATFORM_SOURCES}"
289 )
290
291 # WARNING: ImageIO class need QImage which need Qt5::Widgets
292 # Launcher.cpp need QApplication which need Qt5::Widgets
293 target_link_libraries(hdrmerge-nogui ${STRIP} ${hdrmerge_libs} Qt5::Widgets Qt5::Core)
294 set_target_properties(hdrmerge-nogui PROPERTIES COMPILE_DEFINITIONS "NO_GUI")
295 # Create the installer with makensis
296 find_program(MAKENSIS_EXECUTABLE makensis.exe PATH_SUFFIXES "NSIS/Bin")
297 if(MAKENSIS_EXECUTABLE)
298 message(STATUS "NSIS found")
299 set(SETUP_PROG hdrmerge-setup${WIN_ARCH}-${HDRMERGE_VERSION}.exe)
300 string(REPLACE "/" "\\" PROJ_SRC_DIR "${PROJECT_SOURCE_DIR}")
301 message(STATUS "PROJ_SRC_DIR = ${PROJ_SRC_DIR}")
302
303 # ASSUMING YOU'RE USING EXIV2 BUNDLED WITH MSYS2
304 find_program(MINGW_LIB_DIR exiv2.exe)
305 if(MINGW_LIB_DIR)
306 get_filename_component(MINGW_LIB_DIR "${MINGW_LIB_DIR}" PATH)
307 string(REPLACE "/" "\\" MINGW_LIB_DIR "${MINGW_LIB_DIR}")
308 message(STATUS "MINGW_LIB_DIR = ${MINGW_LIB_DIR}")
309
310 set(QT5_PLUGINS_DIR "${MINGW_LIB_DIR}\\..\\share\\qt5\\plugins")
311 message(STATUS "QT5_PLUGINS_DIR = ${QT5_PLUGINS_DIR}")
312
313 configure_file(
314 "${PROJECT_SOURCE_DIR}/data/setup.nsi"
315 "${PROJECT_BINARY_DIR}/setup.nsi" @ONLY
316 )
317 add_custom_command(OUTPUT ${SETUP_PROG}
318 COMMAND "${MAKENSIS_EXECUTABLE}" -V2 setup.nsi
319 MAIN_DEPENDENCY setup.nsi
320 DEPENDS "${PROJECT_BINARY_DIR}/hdrmerge.exe"
321 WORKING_DIRECTORY "${PROJECT_BINARY_DIR}")
322 add_custom_target(hdrmerge-setup ALL DEPENDS "${PROJECT_BINARY_DIR}/${SETUP_PROG}")
323 else(MINGW_LIB_DIR)
324 message(STATUS "Unable to locate the 'bin' directory to get the dependencies")
325 endif(MINGW_LIB_DIR)
326 else()
327 message(STATUS "NSIS not found")
328 endif()
329 elseif(APPLE)
330 set_source_files_properties(data/images/icon.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
331 install(TARGETS hdrmerge BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
332 else()
333 if(NOT DEFINED CMAKE_INSTALL_BINDIR)
334 set(CMAKE_INSTALL_BINDIR "bin")
335 endif()
336 message(STATUS "Install binary to: " ${CMAKE_INSTALL_BINDIR})
337 install(TARGETS hdrmerge DESTINATION ${CMAKE_INSTALL_BINDIR})
338 find_program(XDG-DESKTOP-MENU_EXECUTABLE xdg-desktop-menu)
339 find_program(XDG-ICON-RESOURCE_EXECUTABLE xdg-icon-resource)
340 if(XDG-DESKTOP-MENU_EXECUTABLE AND XDG-ICON-RESOURCE_EXECUTABLE)
341 configure_file(
342 "${PROJECT_SOURCE_DIR}/data/hdrmerge.desktop"
343 "${PROJECT_BINARY_DIR}/hdrmerge.desktop"
344 )
345 install(CODE "
346 execute_process(COMMAND \"${XDG-ICON-RESOURCE_EXECUTABLE}\" install --novendor --size 128 \"${PROJECT_SOURCE_DIR}/data/images/icon.png\" hdrmerge-icon)
347 execute_process(COMMAND \"${XDG-DESKTOP-MENU_EXECUTABLE}\" install --novendor \"${PROJECT_BINARY_DIR}/hdrmerge.desktop\")"
348 )
349 endif()
350 endif()
351
352 if(Boost_FOUND)
353 add_subdirectory(test)
354 endif()
355
0 ## Thank You
1
2 Thank you for showing interest in contributing to HDRMerge. It is people such as yourself who make this program and project possible.
3
4 ## Contributing as a Programmer
5
6 - Announce and discuss your plans in GitHub before starting work.
7 - Work in a new branch. Fork if necessary.
8 - Keep branches small.
9 - Prefer templates and `constexpr` variables over macros. Avoid macros as much as possible (e.g. use `#pragma once` instead of traditional include guards).
10 - Use C++11.
11
12 ### Naming and Comments
13
14 The naming isn't homogeneous throughout the code but here is a rough guideline:
15
16 - *Identifiers* (variables, functions, methods, keys, enums, etc.) should be clear and unambiguous. Make them as long as necessary to ensure that your code is understandable to others and to your future self.
17 - *Types* (classes, structs, enums, typedefs...) should be named with `UpperCamelCase`.
18 - *Functions* and *methods* should be named with `lowerCamelCase`.
19 - *Variables* should be named with `lower_underscores` to avoid conflicts.
20 - *Enum values* should be named with `UPPER_UNDERSCORES`.
21 - Add *comments* when needed. Not too little but not too much. Try to be reasonable and keep a good balance.
22 - Give an overview of the code block/algorithm. If the identifiers are clearly describing the intent, single lines don't need to be commented.
23 - Be consistent, even when not sticking to the rules.
24
25 ### Refactoring Code
26
27 Don't shy away from refactoring code, it helps more than you think.
28
29 - Change the naming and add comments when they don't match with the guidelines above.
30 - C++11 gives many opportunities to simplify code (e.g. default initializing with `= {}` or default returning with `return {};`).
31 - When using `auto` think a bit about the reviewers reading diffs and guessing types. It's a double-edged sword.
32 - Help the compiler and reviewers by using `const` as default for almost anything.
33 - Reduce scopes. This also means moving private functions with little or no dependency to `this` to an anonymous namespace inside the implementation.
34 - Try to separate "code refactoring" commits from actual functionality changes.
0 # Contributors
1
2 People who made significant contributions, in last name alphabetical order:
3
4 - Pat David https://github.com/patdavid
5 - Maciej Dworak https://github.com/Beep6581
6 - Andrea Ferrero https://github.com/aferrero2707
7 - Alex Forencich https://github.com/alexforencich
8 - Jean-Christophe https://github.com/Hombre57
9 - Eduardo Pérez https://github.com/eduperez
10 - Philip Ries https://github.com/phries
11 - Ingo Weyrich https://github.com/heckflosse
0 # Compilation
1
2 This document explains how to compile HDRMerge.
3
4 ## Dependencies
5
6 - [ALGLIB](http://www.alglib.net/)
7 - [Boost](http://www.boost.org/)
8 - [CMake](https://cmake.org/) 3.4
9 - [exiv2](http://www.exiv2.org/)
10 - [gettext](http://www.gnu.org/software/gettext/)
11 - [libexpat](http://expat.sourceforge.net/)
12 - [libiconv](https://www.gnu.org/software/libiconv/)
13 - [LibRaw](http://www.libraw.org/)
14 - [Qt](https://www.qt.io/) 5.6
15 - [zlib](http://www.zlib.net/)
16
17 Install the dependencies and proceed to the next section.
18
19 ### Arch and derivatives
20
21 ```bash
22 sudo pacman -Syy
23 sudo pacman -S --needed cmake libraw pacaur qt5
24 pacaur -S alglib
25 ```
26
27 ### Debian/Ubuntu and derivatives
28
29 ```bash
30 sudo apt update
31 sudo apt install build-essential cmake git libalglib-dev libboost-all-dev libexiv2-dev libexpat-dev libraw-dev qt5-default zlib1g-dev
32 ```
33
34 ### Gentoo and derivatives
35
36 ```bash
37 sudo emerge -uva sci-libs/alglib dev-libs/boost dev-util/cmake media-gfx/exiv2 dev-vcs/git media-libs/libraw sys-devel/gettext dev-libs/expat virtual/libiconv dev-qt/qtcore:5 sys-libs/zlib
38 ```
39
40 ## Compilation in Windows
41
42 - Download ALGLIB from http://www.alglib.net/download.php and set the base path in `ALGLIB_ROOT`.
43 - Install NSIS from http://nsis.sourceforge.net/Download
44 If you install it to a custom folder, make sure it is accessible from your `PATH` environment variable.
45
46 Install all other dependencies.
47
48 After the `cmake` process, open `${PROJECT_BINARY_DIR}\setup.nsi` and replace all backslashes with forward slashes in the following:
49 - All `MUI_` variables.
50 - The lines following `File "hdrmerge.exe"`
51
52 Now you can `make install`.
53
54 You have finished.
55
56 ## Compilation in Linux for Linux
57
58 Once you have installed the dependencies, run the following to clone and compile HDRMerge:
59
60 ```bash
61 mkdir ~/programs
62 git clone https://github.com/jcelaya/hdrmerge.git ~/programs/code-hdrmerge
63 cd ~/programs/code-hdrmerge
64 ./scripts/build-hdrmerge
65 ```
66
67 HDRMerge will be ready for use in `~/programs/hdrmerge/hdrmerge`
68 You have finished.
69
70 ## Compilation in Linux for Windows
71
72 These instructions are meant to be a guide to cross-compile HDRMerge on Debian for the Windows platform, both 32- and 64-bit editions.
73
74 It is assumed that you are installing your cross-compiled libraries in `$HOME/usr/x86_64-w64-mingw32`.
75 For the 32-bit version, substitute `x86_64-w64-mingw32` for `i686-w64-mingw32` everywhere it appears.
76
77 You will need to build/download the following libraries:
78 - Qt
79 _**TODO**: Update to Qt5_
80 Unpack Qt and build it as:
81 ```bash
82 ./configure -static -xplatform win32-g++ -device-option CROSS_COMPILE=x86_64-w64-mingw32- -prefix $HOME/usr/x86_64-w64-mingw32/Qt-4.8.6-static -opensource -qt-sql-sqlite -no-qt3support -no-xmlpatterns -no-multimedia -no-phonon -no-webkit -no-javascript-jit -no-script -no-scripttools -no-declarative -qt-zlib -qt-libtiff -qt-libpng -qt-libmng -qt-libjpeg -no-openssl -no-nis -no-cups -no-dbus -qt-freetype -make libs -nomake tools -nomake examples -nomake tests -qtlibinfix 4
83 make install
84 ```
85 - LibRaw
86 For the libraries that can be built with CMake, you can use the toolchain file that is shipped with HDRMerge. There are two versions, one for 32-bit and another for 64-bit. You probably do not need to change them, but they include some paths that may be different in your system. In particular, you must set the variable `PREFIX` (or pass it as parameter to CMake) to where you want CMake to look for additional software.
87 Once LibRaw is uncompressed, it is built with GNU Autoconf:
88 ```bash
89 ./configure --prefix=$HOME/usr/x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-shared --enable-openmp --disable-jpeg --disable-jasper --disable-lcms --disable-examples
90 make install
91 ```
92 - zlib
93 ZLib is built using a Makefile in the win32 directory:
94 ```bash
95 make -f win32/Makefile.gcc PREFIX=x86_64-w64-mingw32- BINARY_PATH=$HOME/usr/x86_64-w64-mingw32/bin INCLUDE_PATH=$HOME/usr/x86_64-w64-mingw32/include LIBRARY_PATH=$HOME/usr/x86_64-w64-mingw32/lib install
96 ```
97 - libiconv
98 GNU libiconv is built with GNU Autoconf:
99 ```bash
100 ./configure --prefix=$HOME/usr/x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-shared
101 make install
102 ```
103 - libexpat
104 libexpat is built with GNU Autoconf:
105 ```bash
106 ./configure --prefix=$HOME/usr/x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-shared
107 make install
108 ```
109 - gettext
110 ```bash
111 ./configure --prefix=$HOME/usr/x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-shared
112 make install
113 ```
114 - Exiv2
115 ```bash
116 ./configure --prefix=$HOME/usr/x86_64-w64-mingw32 --host=x86_64-w64-mingw32 --disable-shared --with-zlib=$HOME/usr/x86_64-w64-mingw32
117 make install
118 ```
119
120 Once you have the dependencies installed, you are ready to compile HDRMerge.
121 _**TODO**: Is QT_ROOT still valid in Qt5?_
122 Set the CMake variable `QT_ROOT` to where you have the Qt libraries installed.
123
124 ```bash
125 mkdir build-win64
126 cd build-win64
127 cmake -DCMAKE_TOOLCHAIN_FILE=${HDRMERGE_PATH}/cmake/Windows64.cmake -DPREFIX=$HOME/usr/x86_64-w64-mingw32 -DQT_ROOT=$HOME/usr/x86_64-w64-mingw32/Qt-4.8.6-static ${HDRMERGE_PATH}
128 make
129 ```
130
131 The result of the compilation should be the binaries `hdrmerge.exe` and `hdrmerge-nogui.exe`.
132 You have finished.
133
134 ## Compilation in macOS
135
136 The first step is to get all the dependencies as well as the source code:
137
138 **NOTE:** xCode is not required but recommended. The Command Line tools that are implicitly installed with Homebrew are sufficient.
139
140 1. Install [Homebrew](https://brew.sh):
141 https://brew.sh
142
143 2. Install the dependencies:
144 ``` bash
145 brew install cmake boost exiv2 libraw qt libomp
146 ```
147 3. alglib is not available on brew (for now) so [Download ALGLIB 3.15.0 for C++](http://www.alglib.net/download.php) and extract to ~/alglib manually or:
148 ``` bash
149 mkdir ~/alglib && cd ~/alglib
150 curl http://www.alglib.net/translator/re/alglib-3.15.0.cpp.gpl.zip --output ~/alglib/ALGLIB.zip
151 unzip ~/alglib/ALGLIB.zip -d ~/alglib && rm ~/alglib/ALGLIB.zip
152 ```
153
154 4. Clone HDRMerge into ~/hdrmerge and checkout master branch
155 ``` bash
156 git clone https://github.com/jcelaya/hdrmerge.git ~/hdrmerge
157 cd ~/hdrmerge
158 git checkout master
159 ```
160 5. Make and go to the build directory.
161 ``` bash
162 mkdir ~/hdrmerge/build && cd ~/hdrmerge/build
163 ```
164 6. Issue cmake command
165 ``` bash
166 cmake .. -DQt5_DIR=/usr/local/Cellar/Qt/5.12.3/lib/cmake/Qt5 -DCMAKE_BUILD_TYPE=Release -DOpenMP_C_FLAGS=-fopenmp=libomp -DOpenMP_CXX_FLAGS=-fopenmp=libomp -DOpenMP_C_LIB_NAMES="libomp" -DOpenMP_CXX_LIB_NAMES="libomp" -DOpenMP_libomp_LIBRARY="/usr/local/lib/libomp.dylib" -DOpenMP_CXX_FLAGS="-Xpreprocessor -fopenmp /usr/local/lib/libomp.dylib -I/usr/local/include" -DOpenMP_CXX_LIB_NAMES="libomp" -DOpenMP_C_FLAGS="-Xpreprocessor -fopenmp /usr/local/lib/libomp.dylib -I/usr/local/include" -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DALGLIB_ROOT=$HOME/alglib/cpp -DALGLIB_INCLUDES=$HOME/alglib/cpp/src -DALGLIB_LIBRARIES=$HOME/alglib/cpp/src -DCMAKE_INSTALL_BINDIR=$HOME/hdrmerge/build/install
167 ```
168 If the command fails then make sure the version of Qt from the command matches the one you have installed.
169
170 7. Compile
171 ``` bash
172 make -j4 install
173 ```
174
175 8. Copy two of the dependencies into Frameworks.
176 ``` bash
177 mkdir ~/hdrmerge/build/install/hdrmerge.app/Contents/Frameworks
178 cp /usr/local/lib/libomp.dylib ~/hdrmerge/build/install/hdrmerge.app/Contents/Frameworks/.
179 cp /usr/local/lib/libexiv2.dylib ~/hdrmerge/build/install/hdrmerge.app/Contents/Frameworks/.
180 ```
181
182 9. Run Qt5's macdeployqt (adapt to your Qt version)
183 ``` bash
184 /usr/local/Cellar/qt/5.12.3/bin/macdeployqt ~/hdrmerge/build/install/hdrmerge.app -no-strip -verbose=1
185 ```
186
187 10. Install an rpath
188 ``` bash
189 install_name_tool -add_rpath "@executable_path/../Frameworks" ~/hdrmerge/build/install/hdrmerge.app/Contents/MacOS/hdrmerge
190 ```
191
192 11. Make the .dmg for further distribution (optional)
193 ``` bash
194 sudo hdiutil create -ov -srcfolder ~/hdrmerge/build/install/hdrmerge.app ~/hdrmerge/build/install/HDRMerge.dmg
195 ```
196
197 That was it! You can move the hdrmerge.app to you applications folder and start using it!
198 ``` bash
199 ditto ~/hdrmerge/build/install/hdrmerge.app /Applications/HDRmerge.app
200 ```
0 GNU GENERAL PUBLIC LICENSE
1 Version 3, 29 June 2007
2
3 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
4 Everyone is permitted to copy and distribute verbatim copies
5 of this license document, but changing it is not allowed.
6
7 Preamble
8
9 The GNU General Public License is a free, copyleft license for
10 software and other kinds of works.
11
12 The licenses for most software and other practical works are designed
13 to take away your freedom to share and change the works. By contrast,
14 the GNU General Public License is intended to guarantee your freedom to
15 share and change all versions of a program--to make sure it remains free
16 software for all its users. We, the Free Software Foundation, use the
17 GNU General Public License for most of our software; it applies also to
18 any other work released this way by its authors. You can apply it to
19 your programs, too.
20
21 When we speak of free software, we are referring to freedom, not
22 price. Our General Public Licenses are designed to make sure that you
23 have the freedom to distribute copies of free software (and charge for
24 them if you wish), that you receive source code or can get it if you
25 want it, that you can change the software or use pieces of it in new
26 free programs, and that you know you can do these things.
27
28 To protect your rights, we need to prevent others from denying you
29 these rights or asking you to surrender the rights. Therefore, you have
30 certain responsibilities if you distribute copies of the software, or if
31 you modify it: responsibilities to respect the freedom of others.
32
33 For example, if you distribute copies of such a program, whether
34 gratis or for a fee, you must pass on to the recipients the same
35 freedoms that you received. You must make sure that they, too, receive
36 or can get the source code. And you must show them these terms so they
37 know their rights.
38
39 Developers that use the GNU GPL protect your rights with two steps:
40 (1) assert copyright on the software, and (2) offer you this License
41 giving you legal permission to copy, distribute and/or modify it.
42
43 For the developers' and authors' protection, the GPL clearly explains
44 that there is no warranty for this free software. For both users' and
45 authors' sake, the GPL requires that modified versions be marked as
46 changed, so that their problems will not be attributed erroneously to
47 authors of previous versions.
48
49 Some devices are designed to deny users access to install or run
50 modified versions of the software inside them, although the manufacturer
51 can do so. This is fundamentally incompatible with the aim of
52 protecting users' freedom to change the software. The systematic
53 pattern of such abuse occurs in the area of products for individuals to
54 use, which is precisely where it is most unacceptable. Therefore, we
55 have designed this version of the GPL to prohibit the practice for those
56 products. If such problems arise substantially in other domains, we
57 stand ready to extend this provision to those domains in future versions
58 of the GPL, as needed to protect the freedom of users.
59
60 Finally, every program is threatened constantly by software patents.
61 States should not allow patents to restrict development and use of
62 software on general-purpose computers, but in those that do, we wish to
63 avoid the special danger that patents applied to a free program could
64 make it effectively proprietary. To prevent this, the GPL assures that
65 patents cannot be used to render the program non-free.
66
67 The precise terms and conditions for copying, distribution and
68 modification follow.
69
70 TERMS AND CONDITIONS
71
72 0. Definitions.
73
74 "This License" refers to version 3 of the GNU General Public License.
75
76 "Copyright" also means copyright-like laws that apply to other kinds of
77 works, such as semiconductor masks.
78
79 "The Program" refers to any copyrightable work licensed under this
80 License. Each licensee is addressed as "you". "Licensees" and
81 "recipients" may be individuals or organizations.
82
83 To "modify" a work means to copy from or adapt all or part of the work
84 in a fashion requiring copyright permission, other than the making of an
85 exact copy. The resulting work is called a "modified version" of the
86 earlier work or a work "based on" the earlier work.
87
88 A "covered work" means either the unmodified Program or a work based
89 on the Program.
90
91 To "propagate" a work means to do anything with it that, without
92 permission, would make you directly or secondarily liable for
93 infringement under applicable copyright law, except executing it on a
94 computer or modifying a private copy. Propagation includes copying,
95 distribution (with or without modification), making available to the
96 public, and in some countries other activities as well.
97
98 To "convey" a work means any kind of propagation that enables other
99 parties to make or receive copies. Mere interaction with a user through
100 a computer network, with no transfer of a copy, is not conveying.
101
102 An interactive user interface displays "Appropriate Legal Notices"
103 to the extent that it includes a convenient and prominently visible
104 feature that (1) displays an appropriate copyright notice, and (2)
105 tells the user that there is no warranty for the work (except to the
106 extent that warranties are provided), that licensees may convey the
107 work under this License, and how to view a copy of this License. If
108 the interface presents a list of user commands or options, such as a
109 menu, a prominent item in the list meets this criterion.
110
111 1. Source Code.
112
113 The "source code" for a work means the preferred form of the work
114 for making modifications to it. "Object code" means any non-source
115 form of a work.
116
117 A "Standard Interface" means an interface that either is an official
118 standard defined by a recognized standards body, or, in the case of
119 interfaces specified for a particular programming language, one that
120 is widely used among developers working in that language.
121
122 The "System Libraries" of an executable work include anything, other
123 than the work as a whole, that (a) is included in the normal form of
124 packaging a Major Component, but which is not part of that Major
125 Component, and (b) serves only to enable use of the work with that
126 Major Component, or to implement a Standard Interface for which an
127 implementation is available to the public in source code form. A
128 "Major Component", in this context, means a major essential component
129 (kernel, window system, and so on) of the specific operating system
130 (if any) on which the executable work runs, or a compiler used to
131 produce the work, or an object code interpreter used to run it.
132
133 The "Corresponding Source" for a work in object code form means all
134 the source code needed to generate, install, and (for an executable
135 work) run the object code and to modify the work, including scripts to
136 control those activities. However, it does not include the work's
137 System Libraries, or general-purpose tools or generally available free
138 programs which are used unmodified in performing those activities but
139 which are not part of the work. For example, Corresponding Source
140 includes interface definition files associated with source files for
141 the work, and the source code for shared libraries and dynamically
142 linked subprograms that the work is specifically designed to require,
143 such as by intimate data communication or control flow between those
144 subprograms and other parts of the work.
145
146 The Corresponding Source need not include anything that users
147 can regenerate automatically from other parts of the Corresponding
148 Source.
149
150 The Corresponding Source for a work in source code form is that
151 same work.
152
153 2. Basic Permissions.
154
155 All rights granted under this License are granted for the term of
156 copyright on the Program, and are irrevocable provided the stated
157 conditions are met. This License explicitly affirms your unlimited
158 permission to run the unmodified Program. The output from running a
159 covered work is covered by this License only if the output, given its
160 content, constitutes a covered work. This License acknowledges your
161 rights of fair use or other equivalent, as provided by copyright law.
162
163 You may make, run and propagate covered works that you do not
164 convey, without conditions so long as your license otherwise remains
165 in force. You may convey covered works to others for the sole purpose
166 of having them make modifications exclusively for you, or provide you
167 with facilities for running those works, provided that you comply with
168 the terms of this License in conveying all material for which you do
169 not control copyright. Those thus making or running the covered works
170 for you must do so exclusively on your behalf, under your direction
171 and control, on terms that prohibit them from making any copies of
172 your copyrighted material outside their relationship with you.
173
174 Conveying under any other circumstances is permitted solely under
175 the conditions stated below. Sublicensing is not allowed; section 10
176 makes it unnecessary.
177
178 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
179
180 No covered work shall be deemed part of an effective technological
181 measure under any applicable law fulfilling obligations under article
182 11 of the WIPO copyright treaty adopted on 20 December 1996, or
183 similar laws prohibiting or restricting circumvention of such
184 measures.
185
186 When you convey a covered work, you waive any legal power to forbid
187 circumvention of technological measures to the extent such circumvention
188 is effected by exercising rights under this License with respect to
189 the covered work, and you disclaim any intention to limit operation or
190 modification of the work as a means of enforcing, against the work's
191 users, your or third parties' legal rights to forbid circumvention of
192 technological measures.
193
194 4. Conveying Verbatim Copies.
195
196 You may convey verbatim copies of the Program's source code as you
197 receive it, in any medium, provided that you conspicuously and
198 appropriately publish on each copy an appropriate copyright notice;
199 keep intact all notices stating that this License and any
200 non-permissive terms added in accord with section 7 apply to the code;
201 keep intact all notices of the absence of any warranty; and give all
202 recipients a copy of this License along with the Program.
203
204 You may charge any price or no price for each copy that you convey,
205 and you may offer support or warranty protection for a fee.
206
207 5. Conveying Modified Source Versions.
208
209 You may convey a work based on the Program, or the modifications to
210 produce it from the Program, in the form of source code under the
211 terms of section 4, provided that you also meet all of these conditions:
212
213 a) The work must carry prominent notices stating that you modified
214 it, and giving a relevant date.
215
216 b) The work must carry prominent notices stating that it is
217 released under this License and any conditions added under section
218 7. This requirement modifies the requirement in section 4 to
219 "keep intact all notices".
220
221 c) You must license the entire work, as a whole, under this
222 License to anyone who comes into possession of a copy. This
223 License will therefore apply, along with any applicable section 7
224 additional terms, to the whole of the work, and all its parts,
225 regardless of how they are packaged. This License gives no
226 permission to license the work in any other way, but it does not
227 invalidate such permission if you have separately received it.
228
229 d) If the work has interactive user interfaces, each must display
230 Appropriate Legal Notices; however, if the Program has interactive
231 interfaces that do not display Appropriate Legal Notices, your
232 work need not make them do so.
233
234 A compilation of a covered work with other separate and independent
235 works, which are not by their nature extensions of the covered work,
236 and which are not combined with it such as to form a larger program,
237 in or on a volume of a storage or distribution medium, is called an
238 "aggregate" if the compilation and its resulting copyright are not
239 used to limit the access or legal rights of the compilation's users
240 beyond what the individual works permit. Inclusion of a covered work
241 in an aggregate does not cause this License to apply to the other
242 parts of the aggregate.
243
244 6. Conveying Non-Source Forms.
245
246 You may convey a covered work in object code form under the terms
247 of sections 4 and 5, provided that you also convey the
248 machine-readable Corresponding Source under the terms of this License,
249 in one of these ways:
250
251 a) Convey the object code in, or embodied in, a physical product
252 (including a physical distribution medium), accompanied by the
253 Corresponding Source fixed on a durable physical medium
254 customarily used for software interchange.
255
256 b) Convey the object code in, or embodied in, a physical product
257 (including a physical distribution medium), accompanied by a
258 written offer, valid for at least three years and valid for as
259 long as you offer spare parts or customer support for that product
260 model, to give anyone who possesses the object code either (1) a
261 copy of the Corresponding Source for all the software in the
262 product that is covered by this License, on a durable physical
263 medium customarily used for software interchange, for a price no
264 more than your reasonable cost of physically performing this
265 conveying of source, or (2) access to copy the
266 Corresponding Source from a network server at no charge.
267
268 c) Convey individual copies of the object code with a copy of the
269 written offer to provide the Corresponding Source. This
270 alternative is allowed only occasionally and noncommercially, and
271 only if you received the object code with such an offer, in accord
272 with subsection 6b.
273
274 d) Convey the object code by offering access from a designated
275 place (gratis or for a charge), and offer equivalent access to the
276 Corresponding Source in the same way through the same place at no
277 further charge. You need not require recipients to copy the
278 Corresponding Source along with the object code. If the place to
279 copy the object code is a network server, the Corresponding Source
280 may be on a different server (operated by you or a third party)
281 that supports equivalent copying facilities, provided you maintain
282 clear directions next to the object code saying where to find the
283 Corresponding Source. Regardless of what server hosts the
284 Corresponding Source, you remain obligated to ensure that it is
285 available for as long as needed to satisfy these requirements.
286
287 e) Convey the object code using peer-to-peer transmission, provided
288 you inform other peers where the object code and Corresponding
289 Source of the work are being offered to the general public at no
290 charge under subsection 6d.
291
292 A separable portion of the object code, whose source code is excluded
293 from the Corresponding Source as a System Library, need not be
294 included in conveying the object code work.
295
296 A "User Product" is either (1) a "consumer product", which means any
297 tangible personal property which is normally used for personal, family,
298 or household purposes, or (2) anything designed or sold for incorporation
299 into a dwelling. In determining whether a product is a consumer product,
300 doubtful cases shall be resolved in favor of coverage. For a particular
301 product received by a particular user, "normally used" refers to a
302 typical or common use of that class of product, regardless of the status
303 of the particular user or of the way in which the particular user
304 actually uses, or expects or is expected to use, the product. A product
305 is a consumer product regardless of whether the product has substantial
306 commercial, industrial or non-consumer uses, unless such uses represent
307 the only significant mode of use of the product.
308
309 "Installation Information" for a User Product means any methods,
310 procedures, authorization keys, or other information required to install
311 and execute modified versions of a covered work in that User Product from
312 a modified version of its Corresponding Source. The information must
313 suffice to ensure that the continued functioning of the modified object
314 code is in no case prevented or interfered with solely because
315 modification has been made.
316
317 If you convey an object code work under this section in, or with, or
318 specifically for use in, a User Product, and the conveying occurs as
319 part of a transaction in which the right of possession and use of the
320 User Product is transferred to the recipient in perpetuity or for a
321 fixed term (regardless of how the transaction is characterized), the
322 Corresponding Source conveyed under this section must be accompanied
323 by the Installation Information. But this requirement does not apply
324 if neither you nor any third party retains the ability to install
325 modified object code on the User Product (for example, the work has
326 been installed in ROM).
327
328 The requirement to provide Installation Information does not include a
329 requirement to continue to provide support service, warranty, or updates
330 for a work that has been modified or installed by the recipient, or for
331 the User Product in which it has been modified or installed. Access to a
332 network may be denied when the modification itself materially and
333 adversely affects the operation of the network or violates the rules and
334 protocols for communication across the network.
335
336 Corresponding Source conveyed, and Installation Information provided,
337 in accord with this section must be in a format that is publicly
338 documented (and with an implementation available to the public in
339 source code form), and must require no special password or key for
340 unpacking, reading or copying.
341
342 7. Additional Terms.
343
344 "Additional permissions" are terms that supplement the terms of this
345 License by making exceptions from one or more of its conditions.
346 Additional permissions that are applicable to the entire Program shall
347 be treated as though they were included in this License, to the extent
348 that they are valid under applicable law. If additional permissions
349 apply only to part of the Program, that part may be used separately
350 under those permissions, but the entire Program remains governed by
351 this License without regard to the additional permissions.
352
353 When you convey a copy of a covered work, you may at your option
354 remove any additional permissions from that copy, or from any part of
355 it. (Additional permissions may be written to require their own
356 removal in certain cases when you modify the work.) You may place
357 additional permissions on material, added by you to a covered work,
358 for which you have or can give appropriate copyright permission.
359
360 Notwithstanding any other provision of this License, for material you
361 add to a covered work, you may (if authorized by the copyright holders of
362 that material) supplement the terms of this License with terms:
363
364 a) Disclaiming warranty or limiting liability differently from the
365 terms of sections 15 and 16 of this License; or
366
367 b) Requiring preservation of specified reasonable legal notices or
368 author attributions in that material or in the Appropriate Legal
369 Notices displayed by works containing it; or
370
371 c) Prohibiting misrepresentation of the origin of that material, or
372 requiring that modified versions of such material be marked in
373 reasonable ways as different from the original version; or
374
375 d) Limiting the use for publicity purposes of names of licensors or
376 authors of the material; or
377
378 e) Declining to grant rights under trademark law for use of some
379 trade names, trademarks, or service marks; or
380
381 f) Requiring indemnification of licensors and authors of that
382 material by anyone who conveys the material (or modified versions of
383 it) with contractual assumptions of liability to the recipient, for
384 any liability that these contractual assumptions directly impose on
385 those licensors and authors.
386
387 All other non-permissive additional terms are considered "further
388 restrictions" within the meaning of section 10. If the Program as you
389 received it, or any part of it, contains a notice stating that it is
390 governed by this License along with a term that is a further
391 restriction, you may remove that term. If a license document contains
392 a further restriction but permits relicensing or conveying under this
393 License, you may add to a covered work material governed by the terms
394 of that license document, provided that the further restriction does
395 not survive such relicensing or conveying.
396
397 If you add terms to a covered work in accord with this section, you
398 must place, in the relevant source files, a statement of the
399 additional terms that apply to those files, or a notice indicating
400 where to find the applicable terms.
401
402 Additional terms, permissive or non-permissive, may be stated in the
403 form of a separately written license, or stated as exceptions;
404 the above requirements apply either way.
405
406 8. Termination.
407
408 You may not propagate or modify a covered work except as expressly
409 provided under this License. Any attempt otherwise to propagate or
410 modify it is void, and will automatically terminate your rights under
411 this License (including any patent licenses granted under the third
412 paragraph of section 11).
413
414 However, if you cease all violation of this License, then your
415 license from a particular copyright holder is reinstated (a)
416 provisionally, unless and until the copyright holder explicitly and
417 finally terminates your license, and (b) permanently, if the copyright
418 holder fails to notify you of the violation by some reasonable means
419 prior to 60 days after the cessation.
420
421 Moreover, your license from a particular copyright holder is
422 reinstated permanently if the copyright holder notifies you of the
423 violation by some reasonable means, this is the first time you have
424 received notice of violation of this License (for any work) from that
425 copyright holder, and you cure the violation prior to 30 days after
426 your receipt of the notice.
427
428 Termination of your rights under this section does not terminate the
429 licenses of parties who have received copies or rights from you under
430 this License. If your rights have been terminated and not permanently
431 reinstated, you do not qualify to receive new licenses for the same
432 material under section 10.
433
434 9. Acceptance Not Required for Having Copies.
435
436 You are not required to accept this License in order to receive or
437 run a copy of the Program. Ancillary propagation of a covered work
438 occurring solely as a consequence of using peer-to-peer transmission
439 to receive a copy likewise does not require acceptance. However,
440 nothing other than this License grants you permission to propagate or
441 modify any covered work. These actions infringe copyright if you do
442 not accept this License. Therefore, by modifying or propagating a
443 covered work, you indicate your acceptance of this License to do so.
444
445 10. Automatic Licensing of Downstream Recipients.
446
447 Each time you convey a covered work, the recipient automatically
448 receives a license from the original licensors, to run, modify and
449 propagate that work, subject to this License. You are not responsible
450 for enforcing compliance by third parties with this License.
451
452 An "entity transaction" is a transaction transferring control of an
453 organization, or substantially all assets of one, or subdividing an
454 organization, or merging organizations. If propagation of a covered
455 work results from an entity transaction, each party to that
456 transaction who receives a copy of the work also receives whatever
457 licenses to the work the party's predecessor in interest had or could
458 give under the previous paragraph, plus a right to possession of the
459 Corresponding Source of the work from the predecessor in interest, if
460 the predecessor has it or can get it with reasonable efforts.
461
462 You may not impose any further restrictions on the exercise of the
463 rights granted or affirmed under this License. For example, you may
464 not impose a license fee, royalty, or other charge for exercise of
465 rights granted under this License, and you may not initiate litigation
466 (including a cross-claim or counterclaim in a lawsuit) alleging that
467 any patent claim is infringed by making, using, selling, offering for
468 sale, or importing the Program or any portion of it.
469
470 11. Patents.
471
472 A "contributor" is a copyright holder who authorizes use under this
473 License of the Program or a work on which the Program is based. The
474 work thus licensed is called the contributor's "contributor version".
475
476 A contributor's "essential patent claims" are all patent claims
477 owned or controlled by the contributor, whether already acquired or
478 hereafter acquired, that would be infringed by some manner, permitted
479 by this License, of making, using, or selling its contributor version,
480 but do not include claims that would be infringed only as a
481 consequence of further modification of the contributor version. For
482 purposes of this definition, "control" includes the right to grant
483 patent sublicenses in a manner consistent with the requirements of
484 this License.
485
486 Each contributor grants you a non-exclusive, worldwide, royalty-free
487 patent license under the contributor's essential patent claims, to
488 make, use, sell, offer for sale, import and otherwise run, modify and
489 propagate the contents of its contributor version.
490
491 In the following three paragraphs, a "patent license" is any express
492 agreement or commitment, however denominated, not to enforce a patent
493 (such as an express permission to practice a patent or covenant not to
494 sue for patent infringement). To "grant" such a patent license to a
495 party means to make such an agreement or commitment not to enforce a
496 patent against the party.
497
498 If you convey a covered work, knowingly relying on a patent license,
499 and the Corresponding Source of the work is not available for anyone
500 to copy, free of charge and under the terms of this License, through a
501 publicly available network server or other readily accessible means,
502 then you must either (1) cause the Corresponding Source to be so
503 available, or (2) arrange to deprive yourself of the benefit of the
504 patent license for this particular work, or (3) arrange, in a manner
505 consistent with the requirements of this License, to extend the patent
506 license to downstream recipients. "Knowingly relying" means you have
507 actual knowledge that, but for the patent license, your conveying the
508 covered work in a country, or your recipient's use of the covered work
509 in a country, would infringe one or more identifiable patents in that
510 country that you have reason to believe are valid.
511
512 If, pursuant to or in connection with a single transaction or
513 arrangement, you convey, or propagate by procuring conveyance of, a
514 covered work, and grant a patent license to some of the parties
515 receiving the covered work authorizing them to use, propagate, modify
516 or convey a specific copy of the covered work, then the patent license
517 you grant is automatically extended to all recipients of the covered
518 work and works based on it.
519
520 A patent license is "discriminatory" if it does not include within
521 the scope of its coverage, prohibits the exercise of, or is
522 conditioned on the non-exercise of one or more of the rights that are
523 specifically granted under this License. You may not convey a covered
524 work if you are a party to an arrangement with a third party that is
525 in the business of distributing software, under which you make payment
526 to the third party based on the extent of your activity of conveying
527 the work, and under which the third party grants, to any of the
528 parties who would receive the covered work from you, a discriminatory
529 patent license (a) in connection with copies of the covered work
530 conveyed by you (or copies made from those copies), or (b) primarily
531 for and in connection with specific products or compilations that
532 contain the covered work, unless you entered into that arrangement,
533 or that patent license was granted, prior to 28 March 2007.
534
535 Nothing in this License shall be construed as excluding or limiting
536 any implied license or other defenses to infringement that may
537 otherwise be available to you under applicable patent law.
538
539 12. No Surrender of Others' Freedom.
540
541 If conditions are imposed on you (whether by court order, agreement or
542 otherwise) that contradict the conditions of this License, they do not
543 excuse you from the conditions of this License. If you cannot convey a
544 covered work so as to satisfy simultaneously your obligations under this
545 License and any other pertinent obligations, then as a consequence you may
546 not convey it at all. For example, if you agree to terms that obligate you
547 to collect a royalty for further conveying from those to whom you convey
548 the Program, the only way you could satisfy both those terms and this
549 License would be to refrain entirely from conveying the Program.
550
551 13. Use with the GNU Affero General Public License.
552
553 Notwithstanding any other provision of this License, you have
554 permission to link or combine any covered work with a work licensed
555 under version 3 of the GNU Affero General Public License into a single
556 combined work, and to convey the resulting work. The terms of this
557 License will continue to apply to the part which is the covered work,
558 but the special requirements of the GNU Affero General Public License,
559 section 13, concerning interaction through a network will apply to the
560 combination as such.
561
562 14. Revised Versions of this License.
563
564 The Free Software Foundation may publish revised and/or new versions of
565 the GNU General Public License from time to time. Such new versions will
566 be similar in spirit to the present version, but may differ in detail to
567 address new problems or concerns.
568
569 Each version is given a distinguishing version number. If the
570 Program specifies that a certain numbered version of the GNU General
571 Public License "or any later version" applies to it, you have the
572 option of following the terms and conditions either of that numbered
573 version or of any later version published by the Free Software
574 Foundation. If the Program does not specify a version number of the
575 GNU General Public License, you may choose any version ever published
576 by the Free Software Foundation.
577
578 If the Program specifies that a proxy can decide which future
579 versions of the GNU General Public License can be used, that proxy's
580 public statement of acceptance of a version permanently authorizes you
581 to choose that version for the Program.
582
583 Later license versions may give you additional or different
584 permissions. However, no additional obligations are imposed on any
585 author or copyright holder as a result of your choosing to follow a
586 later version.
587
588 15. Disclaimer of Warranty.
589
590 THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
591 APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
592 HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
593 OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
594 THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
595 PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
596 IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
597 ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
598
599 16. Limitation of Liability.
600
601 IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
602 WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
603 THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
604 GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
605 USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
606 DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
607 PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
608 EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
609 SUCH DAMAGES.
610
611 17. Interpretation of Sections 15 and 16.
612
613 If the disclaimer of warranty and limitation of liability provided
614 above cannot be given local legal effect according to their terms,
615 reviewing courts shall apply local law that most closely approximates
616 an absolute waiver of all civil liability in connection with the
617 Program, unless a warranty or assumption of liability accompanies a
618 copy of the Program in return for a fee.
619
620 END OF TERMS AND CONDITIONS
621
622 How to Apply These Terms to Your New Programs
623
624 If you develop a new program, and you want it to be of the greatest
625 possible use to the public, the best way to achieve this is to make it
626 free software which everyone can redistribute and change under these terms.
627
628 To do so, attach the following notices to the program. It is safest
629 to attach them to the start of each source file to most effectively
630 state the exclusion of warranty; and each file should have at least
631 the "copyright" line and a pointer to where the full notice is found.
632
633 <one line to give the program's name and a brief idea of what it does.>
634 Copyright (C) <year> <name of author>
635
636 This program is free software: you can redistribute it and/or modify
637 it under the terms of the GNU General Public License as published by
638 the Free Software Foundation, either version 3 of the License, or
639 (at your option) any later version.
640
641 This program is distributed in the hope that it will be useful,
642 but WITHOUT ANY WARRANTY; without even the implied warranty of
643 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
644 GNU General Public License for more details.
645
646 You should have received a copy of the GNU General Public License
647 along with this program. If not, see <http://www.gnu.org/licenses/>.
648
649 Also add information on how to contact you by electronic and paper mail.
650
651 If the program does terminal interaction, make it output a short
652 notice like this when it starts in an interactive mode:
653
654 <program> Copyright (C) <year> <name of author>
655 This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
656 This is free software, and you are welcome to redistribute it
657 under certain conditions; type `show c' for details.
658
659 The hypothetical commands `show w' and `show c' should show the appropriate
660 parts of the General Public License. Of course, your program's commands
661 might be different; for a GUI interface, you would use an "about box".
662
663 You should also get your employer (if you work as a programmer) or school,
664 if any, to sign a "copyright disclaimer" for the program, if necessary.
665 For more information on this, and how to apply and follow the GNU GPL, see
666 <http://www.gnu.org/licenses/>.
667
668 The GNU General Public License does not permit incorporating your program
669 into proprietary programs. If your program is a subroutine library, you
670 may consider it more useful to permit linking proprietary applications with
671 the library. If this is what you want to do, use the GNU Lesser General
672 Public License instead of this License. But first, please read
673 <http://www.gnu.org/philosophy/why-not-lgpl.html>.
0 HDRMerge uses a subset of the Oxygen Icon Theme
1 Oxygen Icon Theme has been developed by The Oxygen Team.
2
3 Art Directors:
4 Nuno F. Pinheiro <nuno@oxygen-icons.org>
5 David Vignoni <david@oxygen-icons.org>
6
7 Naming Coordinator
8 Jakob Petsovits <jpetso@gmx.at>
9
10 Designers:
11 David J. Miller <miller@oxygen-icons.org>
12 David Vignoni <david@oxygen-icons.org>
13 Johann Ollivier Lapeyre <johann@oxygen-icons.org>
14 Kenneth Wimer <ken@oxygen-icons.org>
15 Nuno F. Pinheiro <nuno@oxygen-icons.org>
16 Riccardo Iaconelli <riccardo@oxygen-icons.org>
17 David J. Miller <miller@oxygen-icons.org>
18
19 Thanks to:
20 Lee Olson: Contributed drawing used in application-x-bittorent icon.
21 Marco Aurélio "Coré": Improved audio-input-microphone icon.
22 Matthias Kretz: Contributed "audio-input-line" device icon.
23 Mauricio Piacentini <piacentini@kde.org> : game icons mashup
24 Erlend Hamberg: "text-x-haskell" mimetype icon.
25
26 Oxygen Icon Theme is licensed under the LGPLv3 license:
27
28 GNU LESSER GENERAL PUBLIC LICENSE
29 Version 3, 29 June 2007
30
31 Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
32 Everyone is permitted to copy and distribute verbatim copies
33 of this license document, but changing it is not allowed.
34
35
36 This version of the GNU Lesser General Public License incorporates
37 the terms and conditions of version 3 of the GNU General Public
38 License, supplemented by the additional permissions listed below.
39
40 0. Additional Definitions.
41
42 As used herein, "this License" refers to version 3 of the GNU Lesser
43 General Public License, and the "GNU GPL" refers to version 3 of the GNU
44 General Public License.
45
46 "The Library" refers to a covered work governed by this License,
47 other than an Application or a Combined Work as defined below.
48
49 An "Application" is any work that makes use of an interface provided
50 by the Library, but which is not otherwise based on the Library.
51 Defining a subclass of a class defined by the Library is deemed a mode
52 of using an interface provided by the Library.
53
54 A "Combined Work" is a work produced by combining or linking an
55 Application with the Library. The particular version of the Library
56 with which the Combined Work was made is also called the "Linked
57 Version".
58
59 The "Minimal Corresponding Source" for a Combined Work means the
60 Corresponding Source for the Combined Work, excluding any source code
61 for portions of the Combined Work that, considered in isolation, are
62 based on the Application, and not on the Linked Version.
63
64 The "Corresponding Application Code" for a Combined Work means the
65 object code and/or source code for the Application, including any data
66 and utility programs needed for reproducing the Combined Work from the
67 Application, but excluding the System Libraries of the Combined Work.
68
69 1. Exception to Section 3 of the GNU GPL.
70
71 You may convey a covered work under sections 3 and 4 of this License
72 without being bound by section 3 of the GNU GPL.
73
74 2. Conveying Modified Versions.
75
76 If you modify a copy of the Library, and, in your modifications, a
77 facility refers to a function or data to be supplied by an Application
78 that uses the facility (other than as an argument passed when the
79 facility is invoked), then you may convey a copy of the modified
80 version:
81
82 a) under this License, provided that you make a good faith effort to
83 ensure that, in the event an Application does not supply the
84 function or data, the facility still operates, and performs
85 whatever part of its purpose remains meaningful, or
86
87 b) under the GNU GPL, with none of the additional permissions of
88 this License applicable to that copy.
89
90 3. Object Code Incorporating Material from Library Header Files.
91
92 The object code form of an Application may incorporate material from
93 a header file that is part of the Library. You may convey such object
94 code under terms of your choice, provided that, if the incorporated
95 material is not limited to numerical parameters, data structure
96 layouts and accessors, or small macros, inline functions and templates
97 (ten or fewer lines in length), you do both of the following:
98
99 a) Give prominent notice with each copy of the object code that the
100 Library is used in it and that the Library and its use are
101 covered by this License.
102
103 b) Accompany the object code with a copy of the GNU GPL and this license
104 document.
105
106 4. Combined Works.
107
108 You may convey a Combined Work under terms of your choice that,
109 taken together, effectively do not restrict modification of the
110 portions of the Library contained in the Combined Work and reverse
111 engineering for debugging such modifications, if you also do each of
112 the following:
113
114 a) Give prominent notice with each copy of the Combined Work that
115 the Library is used in it and that the Library and its use are
116 covered by this License.
117
118 b) Accompany the Combined Work with a copy of the GNU GPL and this license
119 document.
120
121 c) For a Combined Work that displays copyright notices during
122 execution, include the copyright notice for the Library among
123 these notices, as well as a reference directing the user to the
124 copies of the GNU GPL and this license document.
125
126 d) Do one of the following:
127
128 0) Convey the Minimal Corresponding Source under the terms of this
129 License, and the Corresponding Application Code in a form
130 suitable for, and under terms that permit, the user to
131 recombine or relink the Application with a modified version of
132 the Linked Version to produce a modified Combined Work, in the
133 manner specified by section 6 of the GNU GPL for conveying
134 Corresponding Source.
135
136 1) Use a suitable shared library mechanism for linking with the
137 Library. A suitable mechanism is one that (a) uses at run time
138 a copy of the Library already present on the user's computer
139 system, and (b) will operate properly with a modified version
140 of the Library that is interface-compatible with the Linked
141 Version.
142
143 e) Provide Installation Information, but only if you would otherwise
144 be required to provide such information under section 6 of the
145 GNU GPL, and only to the extent that such information is
146 necessary to install and execute a modified version of the
147 Combined Work produced by recombining or relinking the
148 Application with a modified version of the Linked Version. (If
149 you use option 4d0, the Installation Information must accompany
150 the Minimal Corresponding Source and Corresponding Application
151 Code. If you use option 4d1, you must provide the Installation
152 Information in the manner specified by section 6 of the GNU GPL
153 for conveying Corresponding Source.)
154
155 5. Combined Libraries.
156
157 You may place library facilities that are a work based on the
158 Library side by side in a single library together with other library
159 facilities that are not Applications and are not covered by this
160 License, and convey such a combined library under terms of your
161 choice, if you do both of the following:
162
163 a) Accompany the combined library with a copy of the same work based
164 on the Library, uncombined with any other library facilities,
165 conveyed under the terms of this License.
166
167 b) Give prominent notice with the combined library that part of it
168 is a work based on the Library, and explaining where to find the
169 accompanying uncombined form of the same work.
170
171 6. Revised Versions of the GNU Lesser General Public License.
172
173 The Free Software Foundation may publish revised and/or new versions
174 of the GNU Lesser General Public License from time to time. Such new
175 versions will be similar in spirit to the present version, but may
176 differ in detail to address new problems or concerns.
177
178 Each version is given a distinguishing version number. If the
179 Library as you received it specifies that a certain numbered version
180 of the GNU Lesser General Public License "or any later version"
181 applies to it, you have the option of following the terms and
182 conditions either of that published version or of any later version
183 published by the Free Software Foundation. If the Library as you
184 received it does not specify a version number of the GNU Lesser
185 General Public License, you may choose any version of the GNU Lesser
186 General Public License ever published by the Free Software Foundation.
187
188 If the Library as you received it specifies that a proxy can decide
189 whether future versions of the GNU Lesser General Public License shall
190 apply, that proxy's public statement of acceptance of any version is
191 permanent authorization for you to choose that version for the
192 Library.
0 # HDRMerge
1
2 HDRMerge combines two or more raw images into a single raw with an extended dynamic range. It can import any raw image supported by LibRaw, and outputs a DNG 1.4 image with floating point data. The output raw is built from the less noisy pixels of the input, so that shadows maintain as much detail as possible. This tool also offers a GUI to remove ghosts from the resulting image.
3
4 ## Download & Installation
5
6 Find the latest builds on our [releases page](https://github.com/jcelaya/hdrmerge/releases).
7
8 Linux users can get HDRMerge from their package manager. If your package manager does not ship the latest version of HDRMerge, file a bug report using your distribution's bug tracker asking them to ship the latest version.
9
10 ## Compilation
11
12 If you would like to compile HDRMerge yourself, follow the instructions in the `INSTALL.md` file.
13
14 ## Usage
15
16 Source images can be loaded from the Open option in the File menu, or passed as arguments in the command line. They must be made with the same camera. After loading them, HDRMerge will correct small misalignments by translation. So, if your camera allows it, you can take the shots with bracketing in burst mode. I have successfully done this just holding the camera with my hands, but using a tripod is highly recommended to obtain the best results.
17
18 Once the input images are loaded, the interface presents you with a 100% preview of the result. The selected pixels from each input image are painted with a different color. You can then pan the result to inspect it.
19
20 When some objects were moving while you took the shots, there will appear "ghosts". You can use the toolbar to add or remove pixels from each image but the last one, until all the pixels that belong to a moving object only come from one of the input images. Usually, you will want to only remove pixels, starting with the first layer and then going down. Adding pixels to the first layers may result in burned areas appearing in the result image, so be careful. On the other hand, the pixels of the first layers contain less noise in the shadows. These operations can be undone and redone with the actions of the Edit menu.
21
22 Once the preview is satisfactory, the Save HDR option of the File menu generates the output DNG file. You can select the number of bits per sample (16, 24 or 32), the size of the embedded preview (full, half or no preview) and whether to save an image with the mask that was used to merge the input files. The number of bits per sample has an important impact in the output file size. As a rule of thumb, the default value of 16 bits will be enough most of the time. Empirical tests (thanks to DrSlony) show no apparent difference between 16- and 32-bit images, after merging 5 exposures with 2EV steps, despite strong manipulation of shadows/mid-tones/highlights. Nevertheless, if you see some unexpected quantization noise in the output image, you can try a 24-bit output. 32 bits will almost never be necessary, but it can be selected anyway.
23
24 The program can also be run without GUI, in batch mode. This is accomplished either by providing an output file name with the "-o" switch, or by generating an automatic one with the "-a" switch. Other switches control the output parameters, refer to the output of the "--help" switch. macOS users may need to change their current working directory to the one which contains the executable in order to run it in CLI mode.
25
26 HDRMerge merges raw files to produce an HDR image in DNG format. Once you have obtained your image from HDRMerge, you need to further process it in an application that supports HDR images in the DNG format, such as [RawTherapee](https://rawtherapee.com) or [darktable](https://darktable.org).
27
28 ## Licence
29
30 HDRMerge is released under the GNU General Public License v3.0.
31 See the file `LICENSE`.
32
33 ## Contributing
34
35 Fork the project and send pull requests, or send patches by creating a new issue on our GitHub page:
36 https://github.com/jcelaya/hdrmerge/issues
37
38 ## Reporting Bugs
39
40 Report bugs by creating a new issue on our GitHub page:
41 https://github.com/jcelaya/hdrmerge/issues
42
43 ## Changelog:
44
45 - v0.6 (not released yet)
46 - Allow user to specify custom white level in case of artifacts with automatically computed white level from LibRaw.
47 - Added support for raw files from Fufjifilm X-Trans sensors.
48 - Speed optimization.
49 - Assume aperture of f/8 if the aperture is invalid.
50 - Migrated from Qt4 to Qt5.
51 - Enable compilation in Windows.
52 - Documentation updated.
53 - Repository tree restructured.
54 - v0.5.0:
55 - First Mac OS X build! Thanks to Philip Ries for his help.
56 - Several bug fixes:
57 - Fix dealing with images with non-ANSI file names.
58 - Calculate response function with non-linear behavior.
59 - Fix file locking issues by transfering Exif tags in memory.
60 - Correctly calculate the response function of very dark images.
61 - v0.4.5:
62 - Better compatibility with other programs, by producing a DNG file that maintains the original layout: frame and active area sizes, black and white levels, etc. *Note that, if you use RawTherapee, you need v4.1.23 or higher to open these files.
63 - Batch mode in command line! Merge several sets of HDR images at once.
64 - Creation of menu launchers and a Windows installer.
65 - Support for CYGM and Fujifilm X-Trans sensors (experimental).
66 - Several bug-fixes.
67 - Improved accuracy and performance.
68 - v0.4.4:
69 - Better support for more camera models.
70 - Better rendering of the embedded preview image.
71 - Change the edit brush radius with Alt+Mouse wheel.
72 - Several bug fixes.
73 - The original embedded preview is not included in the output anymore.
74 - Fixed some glitches with the edit tools.
75 - v0.4.3:
76 - Fix segmentation fault error painting the preview of some rotated images.
77 - Fix DateTime tag in Windows hosts.
78 - v0.4.2:
79 - Improved GUI:
80 - A slider to control the brush radius.
81 - A slider to control the preview exposure.
82 - Movable toolbars.
83 - Layer selector with color codes.
84 - Improved brush visibility on different backgrounds.
85 - Posibility of saving the output options.
86 - First release with a Windows version, both 32- and 64-bit.
87 - v0.4.1:
88 - Bugfixes release
89 - v0.4:
90 - Great performance improvements with OpenMP.
91 - Not depend anymore on DNG & XMP SDK! Windows and Mac version soon...
92 - More robust MBT alignment.
93 - More control on the logging output.
94 - The user may disable alignment and/or cropping. This is most useful to obtain an image of the same size as the inputs. Some programs have this requirement to apply a flat-field image, for instance.
95 - v0.3: This is the first public version of HDRMerge
96 - Supports most raw format supported by LibRaw (No foveon of Fuji formats for the moment).
97 - Automatic alignment of small translations.
98 - Automatic crop to the optimal size.
99 - Automatic merge mask creation. The mask identifies the best source image for each pixel of the output.
100 - Editable merge mask, to manually select pixels from specific source images.
101 - Writes DNG files with 16, 24 and 32 bits per pixel.
102 - Writes full, half or no preview to the output image.
103 - Copies the EXIF data from the least exposed source image.
104
105 ## Acknowledgments
106
107 I would like to thank all the people that have contributed ideas, critics and samples to improve HDRMerge. In particular, to the team of [RawTherapee](https://github.com/Beep6581/RawTherapee).
108
109 Also, HDRMerge implements or is based on the techniques described in the following works:
110 - Ward, G. (2003). Fast, robust image registration for compositing high dynamic range photographs from hand-held exposures. *Journal of graphics tools*, 8(2), 17-30.
111 - Guillermo Luijk, Zero Noise, <http://www.guillermoluijk.com/tutorial/zeronoise/index.html>
112 - Jens Mueller, dngconvert, <https://github.com/jmue/dngconvert>
113 - Jarosz, W. (2001). Fast image convolutions. In SIGGRAPH Workshop. Code from Ivan Kuckir, <http://blog.ivank.net/fastest-gaussian-blur.html>
114
115 There is also a community forum for discussions and connecting with other users (as well as other Free Software projects) at <https://discuss.pixls.us>, hosted by [PIXLS.US](https://pixls.us).
116
117 ## Links
118
119 Website and documentation: http://jcelaya.github.io/hdrmerge/
120 Forum: https://discuss.pixls.us/c/software/hdrmerge
121 GitHub: https://github.com/jcelaya/hdrmerge
0 #! /bin/bash
1
2 # Move blacklisted files to a special folder
3 move_blacklisted()
4 {
5 mkdir -p ./usr/lib-blacklisted
6 #BLACKLISTED_FILES=$(wget -q https://github.com/probonopd/AppImages/raw/master/excludelist -O - | sed '/^\s*$/d' | sed '/^#.*$/d')
7 BLACKLISTED_FILES=$(cat $APPIMAGEBASE/AppImages/excludelist | sed '/^\s*$/d' | sed '/^#.*$/d')
8 echo $BLACKLISTED_FILES
9 for FILE in $BLACKLISTED_FILES ; do
10 FOUND=$(find . -type f -name "${FILE}" 2>/dev/null)
11 if [ ! -z "$FOUND" ] ; then
12 echo "Deleting blacklisted ${FOUND}"
13 #rm -f "${FOUND}"
14 mv "${FOUND}" ./usr/lib-blacklisted
15 fi
16 done
17 }
18
19
20 wget -q https://github.com/probonopd/AppImages/raw/master/functions.sh -O ./functions.sh
21 . ./functions.sh
22
23 APP=hdrmerge
24 APP_VERSION=0.5.0
25 LOWERAPP=${APP,,}
26
27 UPDATE=1
28 REBUILD=1
29
30 rm -rf out $APP
31
32 WD=$(pwd)
33
34 PREFIX=/app
35 export PATH=$PREFIX/bin:$PATH
36 export LD_LIBRARY_PATH=$PREFIX/lib:$LD_LIBRARY_PATH
37 export XDG_DATA_DIRS=$PREFIX/share:$XDG_DATA_DIRS
38 export PKG_CONFIG_PATH=$PREFIX/lib/pkgconfig:$PKG_CONFIG_PATH
39
40
41 pwd
42
43 mkdir -p $APP/$APP.AppDir
44 cd $APP/$APP.AppDir
45 mkdir -p usr
46 cp -rL $PREFIX/* usr
47 mv usr/bin/$LOWERAPP usr/bin/$LOWERAPP.bin
48
49 #cp -a ../../$LOWERAPP.launcher usr/bin/$LOWERAPP
50 cat > usr/bin/$LOWERAPP <<\EOF
51 #! /bin/bash
52 HERE="$(dirname "$(readlink -f "${0}")")"
53 echo "LD_LIBRARY_PATH: $LD_LIBRARY_PATH"
54 echo "Input parameters: \"$@\""
55 echo ""
56 echo "Input File: $1"
57 ldd $HERE/LOWERAPP.bin
58 echo ""
59 echo "$HERE/LOWERAPP.bin -vv \"$@\""
60 $HERE/LOWERAPP.bin -vv "$@"
61 EOF
62 sed -i -e "s|LOWERAPP|$LOWERAPP|g" usr/bin/$LOWERAPP
63 chmod u+x usr/bin/$LOWERAPP
64
65
66 #cp ./usr/share/applications/$LOWERAPP.desktop .
67 #sed -i -e "s|gimp-2.9|$LOWERAPP|g" $LOWERAPP.desktop
68 rm -rf ./usr/share/icons/48x48/apps || true
69 cp $TRAVIS_BUILD_DIR/data/images/icon.png hdrmerge.png
70
71 get_apprun
72
73 # The original desktop file is a bit strange, hence we provide our own
74 cat > $LOWERAPP.desktop <<\EOF
75 [Desktop Entry]
76 Version=1.0
77 Type=Application
78 Name=HDRMerge AppImage
79 GenericName=HDR raw image merge
80 GenericName[es]=Mezcla de imágenes HDR raw
81 Comment=Merge several raw photos into a single raw DNG with high dynamic range.
82 Comment[es]=Mezcla varias imágenes raw en una única imagen DNG raw de alto rango dinámico.
83 Exec=LOWERAPP %f
84 TryExec=LOWERAPP
85 Icon=LOWERAPP
86 Terminal=false
87 Categories=Graphics;
88 MimeType=image/x-dcraw;image/x-adobe-dng;
89 EOF
90 sed -i -e "s|LOWERAPP|$LOWERAPP|g" $LOWERAPP.desktop
91
92 mkdir -p usr/lib/qt4/plugins
93 cp -a /usr/lib/x86_64-linux-gnu/qt4/plugins/* usr/lib/qt4/plugins
94
95 # Copy in the indirect dependencies
96 copy_deps ; copy_deps ; copy_deps # Three runs to ensure we catch indirect ones
97
98 cp -a app/lib/* usr/lib
99 cp -a app/lib64/* usr/lib64
100 rm -rf app
101
102 move_lib
103
104 delete_blacklisted
105 #move_blacklisted
106
107
108 # patch_usr
109 # Patching only the executable files seems not to be enough for darktable
110 find usr/ -type f -exec sed -i -e "s|$PREFIX|././|g" {} \;
111 find usr/ -type f -exec sed -i -e "s|/usr|././|g" {} \;
112
113
114 # Workaround for:
115 # GLib-GIO-ERROR **: Settings schema 'org.gtk.Settings.FileChooser' is not installed
116 # when trying to use the file open dialog
117 # AppRun exports usr/share/glib-2.0/schemas/ which might be hurting us here
118 ( mkdir -p usr/share/glib-2.0/schemas/ ; cd usr/share/glib-2.0/schemas/ ; ln -s /usr/share/glib-2.0/schemas/gschemas.compiled . )
119
120 # Workaround for:
121 # ImportError: /usr/lib/x86_64-linux-gnu/libgdk-x11-2.0.so.0: undefined symbol: XRRGetMonitors
122 cp $(ldconfig -p | grep libgdk-x11-2.0.so.0 | cut -d ">" -f 2 | xargs) ./usr/lib/
123 cp $(ldconfig -p | grep libgtk-x11-2.0.so.0 | cut -d ">" -f 2 | xargs) ./usr/lib/
124
125 VER1=$APP_VERSION-$(date +%Y%m%d)
126 GLIBC_NEEDED=$(glibc_needed)
127 VERSION=git-${TRAVIS_BRANCH}-${TRAVIS_COMMIT}-$(date +%Y%m%d).glibc$GLIBC_NEEDED
128 #VERSION=$VER1.glibc$GLIBC_NEEDED
129 echo $VERSION
130
131 get_desktopintegration $LOWERAPP
132 #cp -a ../../desktopintegration ./usr/bin/$LOWERAPP.wrapper
133 #chmod a+x ./usr/bin/$LOWERAPP.wrapper
134 #sed -i -e "s|Exec=$LOWERAPP|Exec=$LOWERAPP.wrapper|g" $LOWERAPP.desktop
135
136 # Go out of AppImage
137 cd ..
138
139 mkdir -p ../out/
140 ARCH="x86_64"
141 generate_appimage
142
143 ########################################################################
144 # Upload the AppDir
145 ########################################################################
146
147 transfer ../out/*
148 echo "AppImage has been uploaded to the URL above; use something like GitHub Releases for permanent storage"
0 # - Find ALGLIB
1 # Find the native ALGLIB includes and library
2 #
3 # ALGLIB_INCLUDES - where to find Alglib includes
4 # ALGLIB_LIBRARIES - List of libraries when using Alglib.
5 # ALGLIB_FOUND - True if Alglib found.
6
7 if (ALGLIB_INCLUDES)
8 # Already in cache, be silent
9 set (ALGLIB_FIND_QUIETLY TRUE)
10 endif (ALGLIB_INCLUDES)
11
12 find_path (ALGLIB_INCLUDES
13 alglibinternal.h
14 alglibmisc.h
15 ap.h
16 dataanalysis.h
17 diffequations.h
18 fasttransforms.h
19 integration.h
20 interpolation.h
21 linalg.h
22 optimization.h
23 solvers.h
24 specialfunctions.h
25 statistics.h
26 stdafx.h
27 PATHS
28 /usr/include/alglib/
29 /usr/include/libalglib/
30 /usr/local/include/alglib3/
31 /usr/local/include/libalglib/
32 )
33
34 find_library (ALGLIB_LIBRARIES NAMES alglib alglib3)
35
36 # handle the QUIETLY and REQUIRED arguments and set ALGLIB_FOUND to TRUE if
37 # all listed variables are TRUE
38 include (FindPackageHandleStandardArgs)
39 find_package_handle_standard_args (ALGLIB DEFAULT_MSG ALGLIB_LIBRARIES ALGLIB_INCLUDES)
40
41 mark_as_advanced (ALGLIB_LIBRARIES ALGLIB_INCLUDES)
0 # - Try to find Exiv2
1 # Once done this will define
2 #
3 # EXIV2_FOUND - system has Exiv2
4 # EXIV2_INCLUDE_DIR - the Exiv2 include directory
5 # EXIV2_LIBRARIES - Link these to use Exiv2
6 # EXIV2_DEFINITIONS - Compiler switches required for using Exiv2
7 #
8 #=============================================================================
9 # Copyright (c) 2019 Andres Schneider <asn@cryptomilk.org>
10 #
11 # Distributed under the OSI-approved BSD License (the "License");
12 # see accompanying file Copyright.txt for details.
13 #
14 # This software is distributed WITHOUT ANY WARRANTY; without even the
15 # implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 # See the License for more information.
17 #=============================================================================
18 #
19
20 if (UNIX)
21 find_package(PkgConfig)
22 if (PKG_CONFIG_FOUND)
23 pkg_check_modules(_EXIV2 exiv2)
24 endif (PKG_CONFIG_FOUND)
25 endif (UNIX)
26
27 find_path(EXIV2_INCLUDE_DIR
28 NAMES
29 exiv2/exif.hpp
30 PATHS
31 ${_EXIV2_INCLUDEDIR}
32 )
33
34 find_library(EXIV2_LIBRARY
35 NAMES
36 exiv2
37 PATHS
38 ${_EXIV2_LIBDIR}
39 )
40
41 if (EXIV2_LIBRARY)
42 set(EXIV2_LIBRARIES
43 ${EXIV2_LIBRARIES}
44 ${EXIV2_LIBRARY}
45 )
46 endif (EXIV2_LIBRARY)
47
48 # Get the version number from exiv2/version.hpp and store it in the cache:
49 if (EXIV2_INCLUDE_DIR AND NOT EXIV2_VERSION)
50 set(EXIV2_VERSION_STRING_FOUND FALSE)
51
52 if (EXISTS ${EXIV2_INCLUDE_DIR}/exiv2/version.hpp)
53 file(READ ${EXIV2_INCLUDE_DIR}/exiv2/version.hpp EXIV2_VERSION_CONTENT)
54
55 string(FIND "${EXIV2_VERSION_CONTENT}" "#define EXIV2_MAJOR_VERSION" EXIV2_MAJOR_FOUND)
56 if (${EXIV2_MAJOR_FOUND} GREATER 0)
57 set(EXIV2_VERSION_STRING_FOUND TRUE)
58 endif()
59 endif()
60
61 if (NOT EXIV2_VERSION_STRING_FOUND AND EXISTS ${EXIV2_INCLUDE_DIR}/exiv2/exv_conf.h)
62 file(READ ${EXIV2_INCLUDE_DIR}/exiv2/exv_conf.h EXIV2_VERSION_CONTENT)
63 endif()
64
65 string(REGEX MATCH "#define EXIV2_MAJOR_VERSION +\\( *([0-9]+) *\\)" _dummy "${EXIV2_VERSION_CONTENT}")
66 set(EXIV2_VERSION_MAJOR "${CMAKE_MATCH_1}")
67
68 string(REGEX MATCH "#define EXIV2_MINOR_VERSION +\\( *([0-9]+) *\\)" _dummy "${EXIV2_VERSION_CONTENT}")
69 set(EXIV2_VERSION_MINOR "${CMAKE_MATCH_1}")
70
71 string(REGEX MATCH "#define EXIV2_PATCH_VERSION +\\( *([0-9]+) *\\)" _dummy "${EXIV2_VERSION_CONTENT}")
72 set(EXIV2_VERSION_PATCH "${CMAKE_MATCH_1}")
73
74 set(EXIV2_VERSION "${EXIV2_VERSION_MAJOR}.${EXIV2_VERSION_MINOR}.${EXIV2_VERSION_PATCH}")
75 endif (EXIV2_INCLUDE_DIR AND NOT EXIV2_VERSION)
76
77 if (EXIV2_VERSION)
78 set(EXIV2_FOUND TRUE)
79 endif (EXIV2_VERSION)
80
81 include(FindPackageHandleStandardArgs)
82 find_package_handle_standard_args(Exiv2
83 FOUND_VAR EXIV2_FOUND
84 REQUIRED_VARS EXIV2_INCLUDE_DIR EXIV2_LIBRARY EXIV2_LIBRARIES
85 VERSION_VAR EXIV2_VERSION)
86
87 # show the EXIV2_INCLUDE_DIR and EXIV2_LIBRARIES variables only in the advanced view
88 mark_as_advanced(EXIV2_INCLUDE_DIR EXIV2_LIBRARY EXIV2_LIBRARIES)
0 # - Find LibRaw
1 # Find the LibRaw library <http://www.libraw.org>
2 # This module defines
3 # LibRaw_VERSION_STRING, the version string of LibRaw
4 # LibRaw_INCLUDE_DIR, where to find libraw.h
5 # LibRaw_LIBRARIES, the libraries needed to use LibRaw (non-thread-safe)
6 # LibRaw_r_LIBRARIES, the libraries needed to use LibRaw (thread-safe)
7 # LibRaw_DEFINITIONS, the definitions needed to use LibRaw (non-thread-safe)
8 # LibRaw_r_DEFINITIONS, the definitions needed to use LibRaw (thread-safe)
9 #
10 # Copyright (c) 2013, Pino Toscano <pino at kde dot org>
11 # Copyright (c) 2013, Gilles Caulier <caulier dot gilles at gmail dot com>
12 #
13 # Redistribution and use is allowed according to the terms of the BSD license.
14 # For details see the accompanying COPYING-CMAKE-SCRIPTS file.
15
16 FIND_PACKAGE(PkgConfig)
17
18 IF(PKG_CONFIG_FOUND)
19 PKG_CHECK_MODULES(PC_LIBRAW libraw)
20 SET(LibRaw_DEFINITIONS ${PC_LIBRAW_CFLAGS_OTHER})
21
22 PKG_CHECK_MODULES(PC_LIBRAW_R libraw_r)
23 SET(LibRaw_r_DEFINITIONS ${PC_LIBRAW_R_CFLAGS_OTHER})
24 ENDIF()
25
26 FIND_PATH(LibRaw_INCLUDE_DIR libraw.h
27 HINTS
28 ${PC_LIBRAW_INCLUDEDIR}
29 ${PC_LibRaw_INCLUDE_DIRS}
30 PATH_SUFFIXES libraw
31 )
32
33 FIND_LIBRARY(LibRaw_LIBRARIES NAMES raw
34 HINTS
35 ${PC_LIBRAW_LIBDIR}
36 ${PC_LIBRAW_LIBRARY_DIRS}
37 )
38
39 FIND_LIBRARY(LibRaw_r_LIBRARIES NAMES raw_r
40 HINTS
41 ${PC_LIBRAW_R_LIBDIR}
42 ${PC_LIBRAW_R_LIBRARY_DIRS}
43 )
44
45 IF(LibRaw_INCLUDE_DIR)
46 FILE(READ ${LibRaw_INCLUDE_DIR}/libraw_version.h _libraw_version_content)
47
48 STRING(REGEX MATCH "#define LIBRAW_MAJOR_VERSION[ \t]*([0-9]*)\n" _version_major_match ${_libraw_version_content})
49 SET(_libraw_version_major "${CMAKE_MATCH_1}")
50
51 STRING(REGEX MATCH "#define LIBRAW_MINOR_VERSION[ \t]*([0-9]*)\n" _version_minor_match ${_libraw_version_content})
52 SET(_libraw_version_minor "${CMAKE_MATCH_1}")
53
54 STRING(REGEX MATCH "#define LIBRAW_PATCH_VERSION[ \t]*([0-9]*)\n" _version_patch_match ${_libraw_version_content})
55 SET(_libraw_version_patch "${CMAKE_MATCH_1}")
56
57 IF(_version_major_match AND _version_minor_match AND _version_patch_match)
58 SET(LibRaw_VERSION_STRING "${_libraw_version_major}.${_libraw_version_minor}.${_libraw_version_patch}")
59 ELSE()
60 IF(NOT LibRaw_FIND_QUIETLY)
61 MESSAGE(STATUS "Failed to get version information from ${LibRaw_INCLUDE_DIR}/libraw_version.h")
62 ENDIF()
63 ENDIF()
64 ENDIF()
65
66 INCLUDE(FindPackageHandleStandardArgs)
67 FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibRaw
68 REQUIRED_VARS LibRaw_LIBRARIES LibRaw_INCLUDE_DIR
69 VERSION_VAR LibRaw_VERSION_STRING
70 )
71
72 MARK_AS_ADVANCED(LibRaw_VERSION_STRING
73 LibRaw_INCLUDE_DIR
74 LibRaw_LIBRARIES
75 LibRaw_r_LIBRARIES
76 LibRaw_DEFINITIONS
77 LibRaw_r_DEFINITIONS
78 )
0
1 find_package(Qt5Core QUIET)
2
3 if (Qt5Core_FOUND)
4 set(_allComponents
5 Core
6 Gui
7 DBus
8 Designer
9 Declarative
10 Script
11 ScriptTools
12 Network
13 Test
14 Xml
15 Svg
16 Sql
17 Widgets
18 PrintSupport
19 Concurrent
20 UiTools
21 WebKit
22 WebKitWidgets
23 OpenGL
24 X11Extras
25 Qml
26 Quick
27 )
28 if (NOT Qt5Transitional_FIND_COMPONENTS)
29 foreach(_component ${_allComponents})
30 find_package(Qt5${_component})
31
32 list(APPEND QT_LIBRARIES ${Qt5${_component}_LIBRARIES})
33 endforeach()
34 else()
35 set(_components ${Qt5Transitional_FIND_COMPONENTS})
36 foreach(_component ${Qt5Transitional_FIND_COMPONENTS})
37 find_package(Qt5${_component} REQUIRED)
38 if ("${_component}" STREQUAL "WebKit")
39 find_package(Qt5WebKitWidgets REQUIRED)
40 list(APPEND QT_LIBRARIES ${Qt5WebKitWidgets_LIBRARIES} )
41 endif()
42 if ("${_component}" STREQUAL "Gui")
43 find_package(Qt5Widgets REQUIRED)
44 find_package(Qt5PrintSupport REQUIRED)
45 find_package(Qt5Svg REQUIRED)
46 list(APPEND QT_LIBRARIES ${Qt5Widgets_LIBRARIES}
47 ${Qt5PrintSupport_LIBRARIES}
48 ${Qt5Svg_LIBRARIES} )
49 endif()
50 if ("${_component}" STREQUAL "Core")
51 find_package(Qt5Concurrent REQUIRED)
52 list(APPEND QT_LIBRARIES ${Qt5Concurrent_LIBRARIES} )
53 endif()
54 endforeach()
55 endif()
56
57 set(Qt5Transitional_FOUND TRUE)
58 set(QT5_BUILD TRUE)
59
60 include("${CMAKE_CURRENT_LIST_DIR}/ECMQt4To5Porting.cmake") # TODO: Port away from this.
61
62 else()
63 foreach(_component ${Qt5Transitional_FIND_COMPONENTS})
64 if("${_component}" STREQUAL "Widgets") # new in Qt5
65 set(_component Gui)
66 elseif("${_component}" STREQUAL "Concurrent") # new in Qt5
67 set(_component Core)
68 endif()
69 list(APPEND _components Qt${_component})
70 endforeach()
71 find_package(Qt4 ${QT_MIN_VERSION} REQUIRED ${_components})
72
73 if(QT4_FOUND)
74 set(Qt5Transitional_FOUND TRUE)
75 endif()
76 endif()
0 # This is an example toolchain file to cross-compile for Windows 32-bit targets.
1 # It is based on the Debian's mingw-w64 package.
2
3 # this one is important
4 SET(CMAKE_SYSTEM_NAME Windows)
5 SET(WIN_ARCH "")
6
7 # specify the cross compiler
8 SET(CMAKE_C_COMPILER /usr/bin/i686-w64-mingw32-gcc)
9 SET(CMAKE_CXX_COMPILER /usr/bin/i686-w64-mingw32-g++)
10 SET(CMAKE_RC_COMPILER /usr/bin/i686-w64-mingw32-windres)
11
12 # here is the target environment located
13 SET(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32 ${PREFIX})
14
15 # adjust the default behaviour of the FIND_XXX() commands:
16 # search headers and libraries in the target environment, search
17 # programs in the host environment
18 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
19 set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
20 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
21
22 # Some flags so that FindQt4.cmake finds the correct libs
23 if(QT_ROOT)
24 set(QT_QTCORE_LIBRARY_RELEASE ${QT_ROOT}/lib/libQtCore4.a)
25 set(QT_QTCORE_INCLUDE_DIR ${QT_ROOT}/include/QtCore)
26 set(QT_HEADERS_DIR ${QT_ROOT}/include)
27 endif(QT_ROOT)
28
0 # This is an example toolchain file to cross-compile for Windows 64-bit targets.
1 # It is based on the Debian's mingw-w64 package.
2
3 # this one is important
4 SET(CMAKE_SYSTEM_NAME Windows)
5 SET(WIN_ARCH 64)
6
7 # specify the cross compiler
8 SET(CMAKE_C_COMPILER /usr/bin/x86_64-w64-mingw32-gcc)
9 SET(CMAKE_CXX_COMPILER /usr/bin/x86_64-w64-mingw32-g++)
10 SET(CMAKE_RC_COMPILER /usr/bin/x86_64-w64-mingw32-windres)
11
12 # here is the target environment located
13 SET(CMAKE_FIND_ROOT_PATH /usr/x86_64-w64-mingw32 ${PREFIX})
14
15 # adjust the default behaviour of the FIND_XXX() commands:
16 # search headers and libraries in the target environment, search
17 # programs in the host environment
18 set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
19 set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
20 set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
21
22 # Some flags so that FindQt4.cmake finds the correct libs
23 if(QT_ROOT)
24 set(QT_QTCORE_LIBRARY_RELEASE ${QT_ROOT}/lib/libQtCore4.a)
25 set(QT_QTCORE_INCLUDE_DIR ${QT_ROOT}/include/QtCore)
26 set(QT_HEADERS_DIR ${QT_ROOT}/include)
27 endif(QT_ROOT)
28
0 [Desktop Entry]
1 Value=1.0
2 Type=Application
3 Name=HDRMerge
4 GenericName=HDR raw image merge
5 GenericName[es]=Mezcla de imágenes HDR raw
6 Comment=Merge several raw images into a single DNG raw image with high dynamic range.
7 Comment[es]=Mezcla varias imágenes raw en una única imagen DNG raw de alto rango dinámico.
8 Icon=hdrmerge-icon
9 Exec=@CMAKE_INSTALL_PREFIX@/bin/hdrmerge %F
10 Categories=Graphics
11 MimeType=image/x-dcraw;image/x-adobe-dng
12 Terminal=false
0 <?xml version="1.0" encoding="utf-8"?>
1 <!DOCTYPE TS>
2 <TS version="2.0">
3 <context>
4 <name>Help</name>
5 <message>
6 <source>Usage</source>
7 <translation type="finished">Uso</translation>
8 </message>
9 <message>
10 <source>Merges RAW_FILES into an HDR DNG raw image.</source>
11 <translation type="finished">Mezcla RAW_FILES en una imagen HDR DNG raw.</translation>
12 </message>
13 <message>
14 <source>If neither -a nor -o, nor --batch options are given, the GUI will be presented.</source>
15 <translation type="finished">Si no se dan las opciones -a, -o o --batch, se mostrará la GUI.</translation>
16 </message>
17 <message>
18 <source>If similar options are specified, only the last one prevails.</source>
19 <translation type="finished">Si se dan opciones similares, sólo la ultima de ellas se tiene en cuenta.</translation>
20 </message>
21 <message>
22 <source>Options:</source>
23 <translation type="finished">Opciones:</translation>
24 </message>
25 <message>
26 <source>Shows this message.</source>
27 <translation type="finished">Muestra este mensaje.</translation>
28 </message>
29 <message>
30 <source>Sets OUT_FILE as the output file name.</source>
31 <translation type="finished">Establece OUT_FILE como el nombre del fichero de salida.</translation>
32 </message>
33 <message>
34 <source>The following parameters are accepted, most useful in batch mode:</source>
35 <translation type="finished">Se aceptan los siguientes parámetros, especialmente útiles en modo lote:</translation>
36 </message>
37 <message>
38 <source>Replaced by the base file name of image n. Image file names</source>
39 <translation type="finished">Sustituido por el nombre de fichero base de la imagen n.</translation>
40 </message>
41 <message>
42 <source>are first sorted in lexicographical order. Besides, n = -1 is the</source>
43 <translation type="finished">Los nombres de los ficheros se ordenan primero en orden lexicográfico.</translation>
44 </message>
45 <message>
46 <source>last image, n = -2 is the previous to the last image, and so on.</source>
47 <translation type="finished">Además, n = -1 es la última imagen, n = -2 es la penúltima, etc.</translation>
48 </message>
49 <message>
50 <source>Replaced by the base file name of image n without the extension.</source>
51 <translation type="finished">Sustituido por el nombre de fichero base de la imagen n sin extensión.</translation>
52 </message>
53 <message>
54 <source>Replaced by the directory name of image n.</source>
55 <translation type="finished">Sustituido por el nombre del directorio de la imagen n.</translation>
56 </message>
57 <message>
58 <source>Replaced by the numerical suffix of image n, if it exists.</source>
59 <translation type="finished">Sustituido por el sufijo numérico de la imagen n, si existe.</translation>
60 </message>
61 <message>
62 <source>For instance, in IMG_1234.CR2, the numerical suffix would be 1234.</source>
63 <translation type="finished">Por ejemplo, en IMG_1234.CR2, el sufijo numérico sería 1234.</translation>
64 </message>
65 <message>
66 <source>Replaced by a single %.</source>
67 <translation type="finished">Sustituido por un único %.</translation>
68 </message>
69 <message>
70 <source>Calculates the output file name as</source>
71 <translation type="finished">Calcula el nombre del fichero de salida como</translation>
72 </message>
73 <message>
74 <source>Batch mode: Input images are automatically grouped into bracketed sets,</source>
75 <translation type="finished">Modo lote: las imágenes de entrada se agrupan automáticamente comparando su</translation>
76 </message>
77 <message>
78 <source>by comparing the creation time. Implies -a if no output file name is given.</source>
79 <translation type="finished">fecha de creación. Implica -a si no se da un nombre de fichero de salida.</translation>
80 </message>
81 <message>
82 <source>Batch gap, maximum difference in seconds between two images of the same set.</source>
83 <translation type="finished">Diferencia máxima en segundos entre dos imágenes del mismo grupo.</translation>
84 </message>
85 <message>
86 <source>Bits per sample, can be 16, 24 or 32.</source>
87 <translation type="finished">Bits por pixel, puede ser 16, 24 o 32.</translation>
88 </message>
89 <message>
90 <source>The input raw files.</source>
91 <translation type="finished">Los ficheros raw de entrada.</translation>
92 </message>
93 <message>
94 <source>Preview size. Can be full, half or none.</source>
95 <translation type="finished">Tamaño de la vista previa. Puede ser full, half o none.</translation>
96 </message>
97 <message>
98 <source>Mask blur radius, to soften transitions between images. Default is 3 pixels.</source>
99 <translation type="finished">Radio de difuminado de la máscara, para suavizar transiciones. 3 pixels por defecto.</translation>
100 </message>
101 <message>
102 <source>Saves the mask to MASK_FILE as a PNG image.</source>
103 <translation type="finished">Guarda la máscara en MASK_FILE como imagen PNG.</translation>
104 </message>
105 <message>
106 <source>Besides the parameters accepted by -o, it also accepts:</source>
107 <translation type="finished">Además de los parámetros aceptados por -o, también acepta:</translation>
108 </message>
109 <message>
110 <source>Replaced by the base file name of the output file.</source>
111 <translation type="finished">Sustituido por el nombre de fichero base del fichero de salida.</translation>
112 </message>
113 <message>
114 <source>Replaced by the directory name of the output file.</source>
115 <translation type="finished">Sustituido por el nombre del directorio de salida.</translation>
116 </message>
117 <message>
118 <source>Do not auto-align source images.</source>
119 <translation type="finished">No auto-alinear las imágenes de origen.</translation>
120 </message>
121 <message>
122 <source>Do not crop the output image to the optimum size.</source>
123 <translation type="finished">No recortar el resultado al tamaño óptimo.</translation>
124 </message>
125 <message>
126 <source>Verbose mode.</source>
127 <translation type="finished">Mostrar información de progreso.</translation>
128 </message>
129 <message>
130 <source>Debug mode.</source>
131 <translation type="finished">Modo depuración.</translation>
132 </message>
133 <message>
134 <source>Invalid %1 parameter, using default.</source>
135 <translation type="finished">Parámetro %1 inválido, se usará el valor por defecto.</translation>
136 </message>
137 <message>
138 <source>Include single images in batch mode (the default is to skip them.)</source>
139 <translation type="finished">Incluir imágenes sueltas en modo lote (por defecto se ignoran).</translation>
140 </message>
141 </context>
142 <context>
143 <name>LoadSave</name>
144 <message>
145 <source>Error loading %1, it has a different format.</source>
146 <translation type="finished">Error cargando %1, tiene un formato diferente.</translation>
147 </message>
148 <message>
149 <source>Error loading %1, file not found.</source>
150 <translation type="finished">Error cargando %1, no se encontró el fichero.</translation>
151 </message>
152 <message>
153 <source>Writing result to %1</source>
154 <translation type="finished">Escribiendo resultado en %1</translation>
155 </message>
156 <message>
157 <source>Loading %1</source>
158 <translation type="finished">Cargando %1</translation>
159 </message>
160 <message>
161 <source>Aligning</source>
162 <translation type="finished">Alineando</translation>
163 </message>
164 <message>
165 <source>Done loading!</source>
166 <translation type="finished">¡Carga completada!</translation>
167 </message>
168 <message>
169 <source>Initialize metadata</source>
170 <translation type="finished">Inicializar metadatos</translation>
171 </message>
172 <message>
173 <source>Rendering image</source>
174 <translation type="finished">Generando imagen</translation>
175 </message>
176 <message>
177 <source>Rendering preview</source>
178 <translation type="finished">Generando vista previa</translation>
179 </message>
180 <message>
181 <source>Writing output</source>
182 <translation type="finished">Escribiendo fichero de salida</translation>
183 </message>
184 <message>
185 <source>Done writing!</source>
186 <translation type="finished">¡Escritura finalizada!</translation>
187 </message>
188 <message>
189 <source>Skipping single image %1</source>
190 <translation type="finished">Ignorando la imagen suelta %1</translation>
191 </message>
192 </context>
193 <context>
194 <name>hdrmerge::AboutDialog</name>
195 <message>
196 <source>A software for the fusion of multiple raw images into a single high dynamic range image.</source>
197 <translation type="finished">Un software para la fusión de múltiples imágenes raw en una sola imagen de alto rango dinámico.</translation>
198 </message>
199 <message>
200 <source>About HDRMerge...</source>
201 <translation type="finished">Acerca de HDRMerge...</translation>
202 </message>
203 <message>
204 <source>Accept</source>
205 <translation type="finished">Aceptar</translation>
206 </message>
207 </context>
208 <context>
209 <name>hdrmerge::LoadOptionsDialog</name>
210 <message>
211 <source>Add</source>
212 <translation type="finished">Añadir</translation>
213 </message>
214 <message>
215 <source>Remove</source>
216 <translation type="finished">Borrar</translation>
217 </message>
218 <message>
219 <source>Align source images.</source>
220 <translation type="finished">Alinear imágenes.</translation>
221 </message>
222 <message>
223 <source>Crop result image to optimal size.</source>
224 <translation type="finished">Recortar resultado al tamaño óptimo.</translation>
225 </message>
226 <message>
227 <source>Accept</source>
228 <translation type="finished">Aceptar</translation>
229 </message>
230 <message>
231 <source>Cancel</source>
232 <translation type="finished">Cancelar</translation>
233 </message>
234 <message>
235 <source>Raw images (*.3fr *.ari *.arw *.bay *.crw *.cr2 *.cap *.dcs *.dcr *.dng *.drf *.eip *.erf *.fff *.iiq *.k25 *.kdc *.mdc *.mef *.mos *.mrw *.nef *.nrw *.obm *.orf *.pef *.ptx *.pxn *.r3d *.raf *.raw *.rwl *.rw2 *.rwz *.sr2 *.srf *.srw *.x3f)</source>
236 <translation type="finished">Images raw (*.3fr *.ari *.arw *.bay *.crw *.cr2 *.cap *.dcs *.dcr *.dng *.drf *.eip *.erf *.fff *.iiq *.k25 *.kdc *.mdc *.mef *.mos *.mrw *.nef *.nrw *.obm *.orf *.pef *.ptx *.pxn *.r3d *.raf *.raw *.rwl *.rw2 *.rwz *.sr2 *.srf *.srw *.x3f)</translation>
237 </message>
238 <message>
239 <source>Open raw images</source>
240 <translation type="finished">Abrir imágenes raw</translation>
241 </message>
242 </context>
243 <context>
244 <name>hdrmerge::DngPropertiesDialog</name>
245 <message>
246 <source>Bits per sample:</source>
247 <translation type="finished">Bits por muestra:</translation>
248 </message>
249 <message>
250 <source>Accept</source>
251 <translation type="finished">Aceptar</translation>
252 </message>
253 <message>
254 <source>DNG Properties</source>
255 <translation type="finished">Propiedades DNG</translation>
256 </message>
257 <message>
258 <source>Full</source>
259 <translation type="finished">Completa</translation>
260 </message>
261 <message>
262 <source>Half</source>
263 <translation type="finished">Media</translation>
264 </message>
265 <message>
266 <source>None</source>
267 <translation type="finished">Ninguna</translation>
268 </message>
269 <message>
270 <source>Preview size:</source>
271 <translation type="finished">Tamaño de la vista previa:</translation>
272 </message>
273 <message>
274 <source>Mask blur radius:</source>
275 <translation type="finished">Radio de difuminado de la máscara:</translation>
276 </message>
277 <message>
278 <source>Mask image:</source>
279 <translation type="finished">Imagen de máscara:</translation>
280 </message>
281 <message>
282 <source>Cancel</source>
283 <translation type="finished">Cancelar</translation>
284 </message>
285 <message>
286 <source>Save</source>
287 <translation type="finished">Guardar</translation>
288 </message>
289 <message>
290 <source>Save these options as the default values.</source>
291 <translation type="finished">Guardar estas opciones como los valores por defecto.</translation>
292 </message>
293 <message>
294 <source>You can use the following tokens:</source>
295 <translation type="finished">Puede utilizar los siguientes símbolos:</translation>
296 </message>
297 </context>
298 <context>
299 <name>hdrmerge::MainWindow</name>
300 <message>
301 <source>Pan</source>
302 <translation type="finished">Desplazar</translation>
303 </message>
304 <message>
305 <source>Add pixels to the current image</source>
306 <translation type="finished">Añadir pixels a la imagen actual</translation>
307 </message>
308 <message>
309 <source>Remove pixels from the current image</source>
310 <translation type="finished">Eliminar pixels de la imagen actual</translation>
311 </message>
312 <message>
313 <source>Radius:</source>
314 <translation type="finished">Radio:</translation>
315 </message>
316 <message>
317 <source>Brightness:</source>
318 <translation type="finished">Brillo:</translation>
319 </message>
320 <message>
321 <source>HDRMerge v%1.%2 - Raw image fusion</source>
322 <translation type="finished">HDRMerge v%1.%2 - Fusión de imágenes raw</translation>
323 </message>
324 <message>
325 <source>&amp;Open raw images...</source>
326 <translation type="finished">&amp;Abrir imágenes raw...</translation>
327 </message>
328 <message>
329 <source>Ctrl+O</source>
330 <translation type="finished">Ctrl+A</translation>
331 </message>
332 <message>
333 <source>&amp;Quit</source>
334 <translation type="finished">&amp;Salir</translation>
335 </message>
336 <message>
337 <source>Ctrl+Q</source>
338 <translation type="finished">Ctrl+Q</translation>
339 </message>
340 <message>
341 <source>Undo</source>
342 <translation type="finished">Deshacer</translation>
343 </message>
344 <message>
345 <source>Redo</source>
346 <translation type="finished">Rehacer</translation>
347 </message>
348 <message>
349 <source>&amp;About...</source>
350 <translation type="finished">&amp;Acerca de...</translation>
351 </message>
352 <message>
353 <source>&amp;Save HDR...</source>
354 <translation type="finished">&amp;Guardar HDR...</translation>
355 </message>
356 <message>
357 <source>Ctrl+S</source>
358 <translation type="finished">Ctrl+G</translation>
359 </message>
360 <message>
361 <source>&amp;File</source>
362 <translation type="finished">&amp;Archivo</translation>
363 </message>
364 <message>
365 <source>&amp;Edit</source>
366 <translation type="finished">Edición</translation>
367 </message>
368 <message>
369 <source>&amp;Help</source>
370 <translation type="finished">Ay&amp;uda</translation>
371 </message>
372 <message>
373 <source>Unable to open file %1.</source>
374 <translation type="finished">No se pudo abrir el fichero %1.</translation>
375 </message>
376 <message>
377 <source>File %1 has not the same format as the previous ones.</source>
378 <translation type="finished">El fichero %1 no tiene el mismo formato que los anteriores.</translation>
379 </message>
380 <message>
381 <source>Error opening file</source>
382 <translation type="finished">Error al abrir el fichero</translation>
383 </message>
384 <message>
385 <source>Save DNG file</source>
386 <translation type="finished">Guardar fichero DNG</translation>
387 </message>
388 <message>
389 <source>Open raw images</source>
390 <translation type="finished">Abrir imágenes raw</translation>
391 </message>
392 <message>
393 <source>Digital Negatives (*.dng)</source>
394 <translation type="finished">Negativos Digitales (*.dng)</translation>
395 </message>
396 <message>
397 <source>Brush radius of the add/remove tool.</source>
398 <translation type="finished">Radio de brocha de la herramienta de edición.</translation>
399 </message>
400 <message>
401 <source>Preview brightness. It does NOT affect the HDR result.</source>
402 <translation type="finished">Brillo de la vista previa. NO afecta al resultado HDR.</translation>
403 </message>
404 </context>
405 </TS>
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <!-- Created with Inkscape (http://www.inkscape.org/) -->
2
3 <svg
4 xmlns:dc="http://purl.org/dc/elements/1.1/"
5 xmlns:cc="http://creativecommons.org/ns#"
6 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
7 xmlns:svg="http://www.w3.org/2000/svg"
8 xmlns="http://www.w3.org/2000/svg"
9 xmlns:xlink="http://www.w3.org/1999/xlink"
10 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
11 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
12 width="1052.3622"
13 height="744.09448"
14 id="svg2"
15 version="1.1"
16 inkscape:version="0.48.4 r9939"
17 enable-background="new"
18 sodipodi:docname="logo.svg"
19 inkscape:export-filename="/home/javi/projects/hdrmerge/images/logo.png"
20 inkscape:export-xdpi="97.629997"
21 inkscape:export-ydpi="97.629997">
22 <defs
23 id="defs4">
24 <linearGradient
25 inkscape:collect="always"
26 id="linearGradient3927">
27 <stop
28 style="stop-color:#ffffff;stop-opacity:1;"
29 offset="0"
30 id="stop3929" />
31 <stop
32 style="stop-color:#ffffff;stop-opacity:0;"
33 offset="1"
34 id="stop3931" />
35 </linearGradient>
36 <linearGradient
37 id="linearGradient3831">
38 <stop
39 style="stop-color:#000000;stop-opacity:1;"
40 offset="0"
41 id="stop3833" />
42 <stop
43 id="stop3843"
44 offset="0.41747481"
45 style="stop-color:#000000;stop-opacity:1;" />
46 <stop
47 id="stop3839"
48 offset="0.41747481"
49 style="stop-color:#ffffff;stop-opacity:1;" />
50 <stop
51 style="stop-color:#ffffff;stop-opacity:1;"
52 offset="1"
53 id="stop3835" />
54 </linearGradient>
55 <linearGradient
56 id="linearGradient3813">
57 <stop
58 style="stop-color:#000000;stop-opacity:1;"
59 offset="0"
60 id="stop3815" />
61 <stop
62 id="stop3821"
63 offset="0.29915047"
64 style="stop-color:#000000;stop-opacity:1;" />
65 <stop
66 id="stop3825"
67 offset="0.29915047"
68 style="stop-color:#ffffff;stop-opacity:1;" />
69 <stop
70 style="stop-color:#ffffff;stop-opacity:1;"
71 offset="1"
72 id="stop3817" />
73 </linearGradient>
74 <linearGradient
75 id="linearGradient3797">
76 <stop
77 style="stop-color:#000000;stop-opacity:0;"
78 offset="0"
79 id="stop3799" />
80 <stop
81 style="stop-color:#000000;stop-opacity:1;"
82 offset="0.16129032"
83 id="stop3856" />
84 <stop
85 style="stop-color:#000000;stop-opacity:1;"
86 offset="1"
87 id="stop3801" />
88 </linearGradient>
89 <linearGradient
90 inkscape:collect="always"
91 xlink:href="#linearGradient3797"
92 id="linearGradient3854"
93 x1="62.5"
94 y1="400.34375"
95 x2="837.5"
96 y2="400.34375"
97 gradientUnits="userSpaceOnUse"
98 gradientTransform="matrix(0.83870968,0,0,0.96000234,135.08065,318.03085)" />
99 <linearGradient
100 inkscape:collect="always"
101 xlink:href="#linearGradient3813"
102 id="linearGradient3864"
103 x1="212.5"
104 y1="344.65775"
105 x2="687.49994"
106 y2="344.65775"
107 gradientUnits="userSpaceOnUse" />
108 <linearGradient
109 inkscape:collect="always"
110 xlink:href="#linearGradient3927"
111 id="linearGradient3933"
112 x1="215.90022"
113 y1="476.40956"
114 x2="609.60059"
115 y2="476.40956"
116 gradientUnits="userSpaceOnUse" />
117 </defs>
118 <sodipodi:namedview
119 id="base"
120 pagecolor="#ffffff"
121 bordercolor="#666666"
122 borderopacity="1.0"
123 inkscape:pageopacity="0.0"
124 inkscape:pageshadow="2"
125 inkscape:zoom="0.89417884"
126 inkscape:cx="444.89098"
127 inkscape:cy="445.8445"
128 inkscape:document-units="px"
129 inkscape:current-layer="layer1"
130 showgrid="true"
131 inkscape:snap-grids="true"
132 inkscape:snap-bbox="true"
133 inkscape:snap-global="true"
134 inkscape:window-width="1400"
135 inkscape:window-height="1000"
136 inkscape:window-x="-3"
137 inkscape:window-y="-3"
138 inkscape:window-maximized="1"
139 objecttolerance="20"
140 gridtolerance="10000"
141 inkscape:snap-page="true"
142 inkscape:bbox-nodes="false"
143 inkscape:object-nodes="false"
144 inkscape:snap-midpoints="false"
145 inkscape:snap-smooth-nodes="false"
146 inkscape:snap-bbox-edge-midpoints="false"
147 inkscape:snap-object-midpoints="false"
148 inkscape:snap-center="false"
149 inkscape:object-paths="false"
150 inkscape:snap-nodes="true"
151 showguides="true"
152 inkscape:guide-bbox="true">
153 <inkscape:grid
154 type="xygrid"
155 id="grid2995"
156 units="pt"
157 empspacing="2"
158 visible="true"
159 enabled="true"
160 snapvisiblegridlinesonly="true"
161 spacingx="5pt"
162 spacingy="5pt" />
163 </sodipodi:namedview>
164 <metadata
165 id="metadata7">
166 <rdf:RDF>
167 <cc:Work
168 rdf:about="">
169 <dc:format>image/svg+xml</dc:format>
170 <dc:type
171 rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
172 <dc:title></dc:title>
173 </cc:Work>
174 </rdf:RDF>
175 </metadata>
176 <g
177 inkscape:groupmode="layer"
178 id="layer4"
179 inkscape:label="Rays"
180 style="display:inline">
181 <path
182 style="fill:#000000;fill-opacity:1;stroke:none"
183 d="m 887.5,244.09375 c 0,11.47568 -0.62085,22.80004 -1.78125,33.96875 l 49.71875,5.21875 c 1.36859,-13.02124 2.0625,-26.09454 2.0625,-39.1875 l -50,0 z m -21.53125,116.5 c -4.08582,10.63524 -8.70598,21.0309 -13.84375,31.09375 l 44.5,22.65625 c 5.94408,-11.66591 11.27665,-23.65167 15.96875,-35.875 l -46.625,-17.875 z M 804,461.5625 c -7.59238,8.42643 -15.60482,16.43887 -24.03125,24.03125 l 33.46875,37.1875 c 9.72997,-8.7609 18.9891,-18.02003 27.75,-27.75 L 804,461.5625 z m -93.90625,72.15625 C 700.0309,538.85652 689.63524,543.47668 679,547.5625 l 17.875,46.625 c 12.22334,-4.6921 24.20908,-10.02467 35.875,-15.96875 l -22.65625,-44.5 z m -113.625,33.59375 c -11.16871,1.1604 -22.49307,1.78125 -33.96875,1.78125 l 0,50 c 13.09295,0 26.16627,-0.69391 39.1875,-2.0625 l -5.21875,-49.71875 z"
184 id="path3081"
185 inkscape:connector-curvature="0" />
186 <path
187 sodipodi:type="arc"
188 style="fill:#000000;fill-opacity:1;stroke:none;display:inline"
189 id="path3118"
190 sodipodi:cx="562.5"
191 sodipodi:cy="244.09448"
192 sodipodi:rx="300"
193 sodipodi:ry="300"
194 d="m 862.5,244.09448 a 300,300 0 0 1 -299.99999,300 l -10e-6,-300 z"
195 sodipodi:start="0"
196 sodipodi:end="1.5707963" />
197 </g>
198 <g
199 inkscape:label="Body"
200 inkscape:groupmode="layer"
201 id="layer1"
202 transform="translate(0,-308.26767)"
203 style="display:inline">
204 <path
205 style="fill:#000000;fill-opacity:1;stroke:none"
206 d="m 225,244.09375 c -13.84998,0 -25,11.15002 -25,25 l 0,250 c 0,13.84998 11.15002,25 25,25 l 212.5,0 125,0 c 165.68542,0 300,-134.31458 300,-300 l -300,0 -125,0 -212.5,0 z"
207 transform="translate(0,308.26767)"
208 id="path3915"
209 inkscape:connector-curvature="0" />
210 </g>
211 <g
212 inkscape:groupmode="layer"
213 id="layer2"
214 inkscape:label="Text"
215 style="display:inline">
216 <path
217 inkscape:connector-curvature="0"
218 id="path3143"
219 style="font-size:204.2159729px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bera Sans;-inkscape-font-specification:Bera Sans Bold"
220 d="m 225.00003,270.22104 0,148.87344 44.04912,0 0,-63.10273 64.90217,0 0,63.10273 44.04912,0 0,-148.87344 -44.04912,0 0,56.77204 -64.90217,0 0,-56.77204 -44.04912,0" />
221 <path
222 inkscape:connector-curvature="0"
223 id="path3145"
224 style="font-size:204.2159729px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bera Sans;-inkscape-font-specification:Bera Sans Bold"
225 d="m 436.85537,299.21971 14.19939,0 c 32.84929,0 50.22768,15.72466 50.22768,45.33594 0,29.61129 -17.80225,45.54016 -50.22768,45.54016 l -14.19939,0 0,-90.8761 m -39.84307,-28.99867 0,148.87344 41.96239,0 c 23.10047,0 40.47885,-1.63373 52.13506,-4.69696 11.6562,-3.26746 21.61698,-8.7813 29.8823,-16.5415 14.4113,-13.27402 21.40504,-31.04085 21.40504,-53.30037 0,-22.25952 -6.99374,-39.82213 -21.40504,-53.09615 -8.26532,-7.7602 -18.01417,-13.27404 -29.67037,-16.33728 -11.44428,-3.26745 -28.82266,-4.90118 -52.34699,-4.90118 l -41.96239,0" />
226 <path
227 inkscape:connector-curvature="0"
228 id="path3147"
229 style="font-size:204.2159729px;font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bera Sans;-inkscape-font-specification:Bera Sans Bold"
230 d="m 618.13183,336.1828 -17.44497,0 0,-38.18839 17.44497,0 c 17.44494,0 24.29045,5.30963 24.29045,18.99209 0,13.68245 -6.84551,19.1963 -24.29045,19.1963 m -17.44497,26.54807 11.70359,0 c 13.47014,0 20.31566,4.49277 28.26526,19.40052 l 19.65319,36.96309 44.16447,0 -22.52388,-42.2727 c -9.05371,-17.15413 -17.44498,-24.91436 -28.92773,-27.36494 20.53647,-5.92226 30.91513,-18.58368 30.91513,-38.18839 0,-28.38599 -18.99077,-41.04741 -61.38862,-41.04741 l -63.37602,0 0,148.87344 41.51461,0 0,-56.36361" />
231 <text
232 xml:space="preserve"
233 style="font-size:103.94461823px;font-style:normal;font-weight:bold;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#ffffff;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans;-inkscape-font-specification:Bitstream Vera Sans Bold"
234 x="219.05846"
235 y="501.9437"
236 id="text3827"
237 sodipodi:linespacing="125%"
238 transform="scale(0.98425099,1.016001)"><tspan
239 sodipodi:role="line"
240 id="tspan3829"
241 x="219.05846"
242 y="501.9437"
243 style="fill:#ffffff;fill-opacity:1">MERGE</tspan></text>
244 </g>
245 </svg>
0 <!DOCTYPE RCC><RCC version="1.0">
1 <qresource>
2 <file>images/logo.png</file>
3 <file>images/icon.png</file>
4 <file>images/transform-move.png</file>
5 <file>images/draw-brush.png</file>
6 <file>images/draw-eraser.png</file>
7 </qresource>
8 </RCC>
0 !include MUI2.nsh
1 !include FileFunc.nsh
2
3 !define APPNAME "HDRMerge@WIN_ARCH@"
4
5 ; The name of the installer
6 Name "${APPNAME} v@HDRMERGE_VERSION@"
7
8 ; The file to write
9 OutFile "@SETUP_PROG@"
10
11 ; The default installation directory
12 InstallDir $PROGRAMFILES@WIN_ARCH@\HDRMerge
13
14 ; Registry key to check for directory (so if you install again, it will
15 ; overwrite the old one automatically)
16 InstallDirRegKey HKLM "Software\${APPNAME}" "Install_Dir"
17
18 ; Request application privileges for Windows Vista
19 RequestExecutionLevel admin
20
21 ;--------------------------------
22 ;Version Information
23
24 VIProductVersion "@HDRMERGE_VERSION@.0"
25 VIAddVersionKey "ProductName" "${APPNAME}"
26 VIAddVersionKey "CompanyName" "Javier Celaya"
27 VIAddVersionKey "LegalCopyright" "Copyright Javier Celaya"
28 VIAddVersionKey "FileDescription" "${APPNAME}"
29 VIAddVersionKey "FileVersion" "@HDRMERGE_VERSION@"
30 VIAddVersionKey "ProductVersion" "@HDRMERGE_VERSION@"
31
32 !define MUI_ICON "@PROJ_SRC_DIR@\data\images\icon.ico"
33 !define MUI_HEADERIMAGE
34 !define MUI_HEADERIMAGE_BITMAP "@PROJ_SRC_DIR@\data\images\logo.bmp"
35 !define MUI_HEADERIMAGE_RIGHT
36
37 ;--------------------------------
38
39 ; Pages
40
41 !insertmacro MUI_PAGE_WELCOME
42 !insertmacro MUI_PAGE_LICENSE "@PROJ_SRC_DIR@\LICENSE"
43 !insertmacro MUI_PAGE_COMPONENTS
44 !insertmacro MUI_PAGE_DIRECTORY
45 !insertmacro MUI_PAGE_INSTFILES
46
47 !insertmacro MUI_UNPAGE_CONFIRM
48 !insertmacro MUI_UNPAGE_INSTFILES
49
50 ;--------------------------------
51 ;Languages
52
53 !insertmacro MUI_LANGUAGE "English"
54
55 ;--------------------------------
56
57 ; The stuff to install
58 Section "HDRMerge (required)"
59
60 SectionIn RO
61
62 ; Set output path to the installation directory.
63 SetOutPath $INSTDIR
64
65 ; Put file there
66 File "hdrmerge.exe" \
67 "@PROJ_SRC_DIR@\LICENSE" \
68 "@PROJ_SRC_DIR@\LICENSE_icons" \
69 "@PROJ_SRC_DIR@\README.md"\
70 "@MINGW_LIB_DIR@\libbz2-1.dll"\
71 "@MINGW_LIB_DIR@\libexiv2.dll"\
72 "@MINGW_LIB_DIR@\libexpat-1.dll"\
73 "@MINGW_LIB_DIR@\libfreetype-6.dll"\
74 "@MINGW_LIB_DIR@\libgcc_s_seh-1.dll"\
75 "@MINGW_LIB_DIR@\libglib-2.0-0.dll"\
76 "@MINGW_LIB_DIR@\libgomp-1.dll"\
77 "@MINGW_LIB_DIR@\libgraphite2.dll"\
78 "@MINGW_LIB_DIR@\libharfbuzz-0.dll"\
79 "@MINGW_LIB_DIR@\libiconv-2.dll"\
80 "@MINGW_LIB_DIR@\libicudt61.dll"\
81 "@MINGW_LIB_DIR@\libicuin61.dll"\
82 "@MINGW_LIB_DIR@\libicuuc61.dll"\
83 "@MINGW_LIB_DIR@\libintl-8.dll"\
84 "@MINGW_LIB_DIR@\libjasper-4.dll"\
85 "@MINGW_LIB_DIR@\libjpeg-8.dll"\
86 "@MINGW_LIB_DIR@\liblcms2-2.dll"\
87 "@MINGW_LIB_DIR@\libpcre-1.dll"\
88 "@MINGW_LIB_DIR@\libpcre2-16-0.dll"\
89 "@MINGW_LIB_DIR@\libpng16-16.dll"\
90 "@MINGW_LIB_DIR@\libraw_r-16.dll"\
91 "@MINGW_LIB_DIR@\libstdc++-6.dll"\
92 "@MINGW_LIB_DIR@\libwinpthread-1.dll"\
93 "@MINGW_LIB_DIR@\Qt5Core.dll"\
94 "@MINGW_LIB_DIR@\Qt5Gui.dll"\
95 "@MINGW_LIB_DIR@\Qt5Widgets.dll"\
96 "@MINGW_LIB_DIR@\zlib1.dll"
97 File /oname=hdrmerge.com "hdrmerge-nogui.exe"
98
99 SetOutPath $INSTDIR\platforms
100
101 File "@QT5_PLUGINS_DIR@\platforms\qwindows.dll"
102
103 ; Write the installation path into the registry
104 WriteRegStr HKLM SOFTWARE\${APPNAME} "Install_Dir" "$INSTDIR"
105
106 ; Write the uninstall keys for Windows
107 !define ARP "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
108 WriteRegStr HKLM "${ARP}" "DisplayName" "${APPNAME}"
109 WriteRegStr HKLM "${ARP}" "DisplayIcon" "$INSTDIR\hdrmerge.exe"
110 WriteRegStr HKLM "${ARP}" "DisplayVersion" "@HDRMERGE_VERSION@"
111 WriteRegStr HKLM "${ARP}" "InstallLocation" "$INSTDIR"
112 ; Compute installed size
113 ${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
114 IntFmt $0 "0x%08X" $0
115 WriteRegDWORD HKLM "${ARP}" "EstimatedSize" "$0"
116 WriteRegStr HKLM "${ARP}" "Publisher" "Javier Celaya"
117 WriteRegStr HKLM "${ARP}" "UninstallString" '"$INSTDIR\uninstall.exe"'
118 WriteRegDWORD HKLM "${ARP}" "NoModify" 1
119 WriteRegDWORD HKLM "${ARP}" "NoRepair" 1
120 WriteUninstaller "uninstall.exe"
121
122 SectionEnd
123
124 ; Optional section (can be disabled by the user)
125 Section "Start Menu Shortcuts"
126
127 CreateDirectory "$SMPROGRAMS\${APPNAME}"
128 CreateShortCut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\uninstall.exe" "" "$INSTDIR\uninstall.exe" 0
129 CreateShortCut "$SMPROGRAMS\${APPNAME}\HDRMerge.lnk" "$INSTDIR\hdrmerge.exe" "" "$INSTDIR\hdrmerge.exe" 0
130
131 SectionEnd
132
133 ;--------------------------------
134
135 ; Uninstaller
136
137 Section "Uninstall"
138
139 ; Remove registry keys
140 DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
141 DeleteRegKey HKLM SOFTWARE\${APPNAME}
142
143 ; Remove shortcuts, if any
144 Delete "$SMPROGRAMS\${APPNAME}\*.*"
145
146 ; Remove directories used
147 RMDir "$SMPROGRAMS\${APPNAME}"
148 Delete "$INSTDIR\hdrmerge.exe"
149 Delete "$INSTDIR\hdrmerge.com"
150 Delete "$INSTDIR\LICENSE"
151 Delete "$INSTDIR\LICENSE_icons"
152 Delete "$INSTDIR\README.md"
153 Delete "$INSTDIR\uninstall.exe"
154 Delete "$INSTDIR\platforms\qwindows.dll"
155 RMDir "$INSTDIR\platforms"
156 Delete "$INSTDIR\libbz2-1.dll"
157 Delete "$INSTDIR\libexiv2.dll"
158 Delete "$INSTDIR\libexpat-1.dll"
159 Delete "$INSTDIR\libfreetype-6.dll"
160 Delete "$INSTDIR\libgcc_s_seh-1.dll"
161 Delete "$INSTDIR\libglib-2.0-0.dll"
162 Delete "$INSTDIR\libgomp-1.dll"
163 Delete "$INSTDIR\libgraphite2.dll"
164 Delete "$INSTDIR\libharfbuzz-0.dll"
165 Delete "$INSTDIR\libiconv-2.dll"
166 Delete "$INSTDIR\libicudt61.dll"
167 Delete "$INSTDIR\libicuin61.dll"
168 Delete "$INSTDIR\libicuuc61.dll"
169 Delete "$INSTDIR\libintl-8.dll"
170 Delete "$INSTDIR\libjasper-4.dll"
171 Delete "$INSTDIR\libjpeg-8.dll"
172 Delete "$INSTDIR\liblcms2-2.dll"
173 Delete "$INSTDIR\libpcre-1.dll"
174 Delete "$INSTDIR\libpcre2-16-0.dll"
175 Delete "$INSTDIR\libpng16-16.dll"
176 Delete "$INSTDIR\libraw_r-16.dll"
177 Delete "$INSTDIR\libstdc++-6.dll"
178 Delete "$INSTDIR\libwinpthread-1.dll"
179 Delete "$INSTDIR\Qt5Core.dll"
180 Delete "$INSTDIR\Qt5Gui.dll"
181 Delete "$INSTDIR\Qt5Widgets.dll"
182 Delete "$INSTDIR\zlib1.dll"
183 RMDir /REBOOTOK $INSTDIR
184
185 SectionEnd
0 <!DOCTYPE RCC><RCC version="1.0">
1 <qresource prefix="/translators">@HDRMERGE_QM_XML@
2 </qresource>
3 </RCC>
4
0 iconId ICON "@PROJECT_SOURCE_DIR@/data/images/icon.ico"
1
2 1 VERSIONINFO
3 FILEVERSION @HDRMERGE_VERSION_MAJOR@,@HDRMERGE_VERSION_MINOR@,@HDRMERGE_VERSION_REV@,0
4 PRODUCTVERSION @HDRMERGE_VERSION_MAJOR@,@HDRMERGE_VERSION_MINOR@,@HDRMERGE_VERSION_REV@,0
5 BEGIN
6 BLOCK "StringFileInfo"
7 BEGIN
8 BLOCK "040904E4"
9 BEGIN
10 VALUE "CompanyName", "Javier Celaya"
11 VALUE "FileDescription", "HDRMerge@WIN_ARCH@"
12 VALUE "FileVersion", "@HDRMERGE_VERSION@"
13 VALUE "InternalName", "hdrmerge"
14 VALUE "LegalCopyright", "Javier Celaya"
15 VALUE "OriginalFilename", "hdrmerge.exe"
16 VALUE "ProductName", "HDRMerge"
17 VALUE "ProductVersion", "@HDRMERGE_VERSION@"
18 END
19 END
20
21 BLOCK "VarFileInfo"
22 BEGIN
23 VALUE "Translation", 0x409, 1252
24 END
25 END
0 #!/usr/bin/env bash
1 # By Morgan Hardwood
2 # Version 2018-03-04
3 # This script gets the latest source code for the given program and compiles it.
4
5 # The name of the program, used for the folder names:
6 prog="hdrmerge"
7
8 # The name of the compiled executable:
9 exe="${prog}"
10
11 # The name of the sub-folder, if any, relative to the folder into which the
12 # compiled executable is placed.
13 # e.g. If the executable ends up in:
14 # ~/programs/someProgram/foo/bar/someExecutable
15 # then set it to:
16 # exeRelativePath="foo/bar"
17 # or if the executable ends up in
18 # ~/programs/someProgram/someExecutable
19 # then leave it empty:
20 # exeRelativePath=""
21 exeRelativePath=""
22
23 # The path to the repository:
24 repo="https://github.com/jcelaya/hdrmerge.git"
25
26 # No touching below this line, with the exception of the "Compile" section
27 # -----------------------------------------------------------------------------
28
29 # The name of the project's standard branch, typically "master":
30 master="master"
31
32 buildOnly="false"
33 buildType="release"
34
35 # Removes the trailing forward-slash if one is present
36 exeRelativePath="${exeRelativePath/%\/}"
37 # Append forward-slash to exeRelativePath only if it is not empty.
38 exePath="${exeRelativePath:+${exeRelativePath}/}${exe}"
39
40 # Command-line arguments
41 OPTIND=1
42 while getopts "bdh?-" opt; do
43 case "${opt}" in
44 b) buildOnly="true"
45 ;;
46 d) buildType="debug"
47 ;;
48 h|\?|-) printf '%s\n' "This script gets the latest source code for ${prog} and compiles it." \
49 "" \
50 " -b" \
51 " Optional. If specified, the script only compiles the source, it does not try to update the source. If not specified, the source will be updated first." \
52 " -d" \
53 " Optional. Compile a \"debug\" build. If not specified, a \"release\" build will be made." \
54 ""
55 exit 0
56 ;;
57 esac
58 done
59 shift $((OPTIND-1))
60 [ "$1" = "--" ] && shift
61
62 printf '%s\n' "" "Program name: ${prog}" "Build type: ${buildType}" "Build without updating: ${buildOnly}" ""
63
64 # Clone if needed
65 cloned="false"
66 updates="false"
67 if [[ ! -d "$HOME/programs/code-${prog}" ]]; then
68 mkdir -p "$HOME/programs" || exit 1
69 git clone "$repo" "$HOME/programs/code-${prog}" || exit 1
70 pushd "$HOME/programs/code-${prog}" 1>/dev/null || exit 1
71 cloned="true"
72 else
73 pushd "$HOME/programs/code-${prog}" 1>/dev/null || exit 1
74 git fetch
75 if [[ $(git rev-parse HEAD) != $(git rev-parse '@{u}') ]]; then
76 updates="true"
77 fi
78 fi
79
80 # Pull updates if necessary
81 if [[ "$updates" = "true" && "$buildOnly" = "false" ]]; then
82 git pull || exit 1
83 fi
84
85 # Find out which branch git is on
86 branch="$(git rev-parse --abbrev-ref HEAD)"
87
88 # Set build and install folder names
89 if [[ $branch = $master && $buildType = release ]]; then
90 buildDir="$HOME/programs/code-${prog}/build"
91 installDir="$HOME/programs/${prog}"
92 else
93 buildDir="$HOME/programs/code-${prog}/build-${branch}-${buildType}"
94 installDir="$HOME/programs/${prog}-${branch}-${buildType}"
95 fi
96
97 existsExe="false"
98 if [[ -e "${installDir}/${exePath}" ]]; then
99 existsExe="true"
100 fi
101
102 # Quit if no updates and build-only flag not set
103 if [[ "$cloned" = "false" && "$buildOnly" = "false" && "$updates" = "false" && "$existsExe" = "true" ]]; then
104 printf '%s\n' "No updates, nothing to do."
105 exit 0
106 fi
107
108 # Determine CPU count
109 cpuCount="fail"
110 if command -v nproc >/dev/null 2>&1; then
111 cpuCount="$(nproc --all)"
112 fi
113 if [[ ! ( $cpuCount -ge 1 && $cpuCount -le 64 ) ]]; then
114 cpuCount=1
115 fi
116
117 # Prepare folders
118 rm -rf "${installDir}"
119 mkdir -p "${buildDir}" "${installDir}" || exit 1
120 cd "${buildDir}" || exit 1
121
122 # -----------------------------------------------------------------------------
123 # Compile
124
125 export QT_SELECT="qt5"
126
127 cmake \
128 -DCMAKE_CXX_FLAGS="-std=c++11 -Wno-deprecated-declarations -Wno-unused-result" \
129 -DCMAKE_INSTALL_BINDIR:STRING="${installDir}" \
130 -DCMAKE_BUILD_TYPE="$buildType" \
131 -DCMAKE_C_FLAGS="-O2 -pipe" \
132 -DCMAKE_CXX_FLAGS="${CMAKE_C_FLAGS}" \
133 "$HOME/programs/code-${prog}" || exit 1
134
135 make --jobs="$cpuCount" || exit 1
136 make install || exit 1
137
138 # Finished
139 printf '%s\n' "" "To run ${prog} type:" "${installDir}/${exePath}" ""
140
141 popd 1>/dev/null
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "config.h"
23 #include <QLabel>
24 #include <QHBoxLayout>
25 #include <QVBoxLayout>
26 #include <QPixmap>
27 #include <QPushButton>
28 #include "AboutDialog.hpp"
29
30 namespace hdrmerge {
31
32 AboutDialog::AboutDialog(QWidget * parent, Qt::WindowFlags f) : QDialog(parent, f) {
33 QVBoxLayout * buttonLayout = new QVBoxLayout(this);
34 QWidget * logoText = new QWidget(this);
35 QHBoxLayout * layout = new QHBoxLayout(logoText);
36 QLabel * logoLabel = new QLabel(logoText);
37 logoLabel->setPixmap(QPixmap(":/images/logo.png").scaledToWidth(400, Qt::SmoothTransformation));
38 layout->addWidget(logoLabel);
39 layout->addSpacing(12);
40 QLabel * text = new QLabel("<h1>HDRMerge " HDRMERGE_VERSION_STRING "</h1>"
41 "<p><a href=\"http://jcelaya.github.io/hdrmerge/\">http://jcelaya.github.io/hdrmerge/</a></p>"
42 "<p>" + tr("A software for the fusion of multiple raw images into a single high dynamic range image.") + "</p>"
43 "<p>Copyright &copy; 2012 Javier Celaya (jcelaya@gmail.com)</p>"
44 "<p>This is free software: you can redistribute it and/or modify it under the terms of the GNU "
45 "General Public License as published by the Free Software Foundation, either version 3 of the License, "
46 "or (at your option) any later version.</p>", logoText);
47 text->setWordWrap(true);
48 layout->addWidget(text);
49 layout->setAlignment(text, Qt::AlignTop);
50 QPushButton * acceptButton = new QPushButton(tr("Accept"), this);
51 acceptButton->setDefault(true);
52 connect(acceptButton, SIGNAL(clicked(bool)), this, SLOT(accept()));
53 buttonLayout->addWidget(logoText);
54 buttonLayout->addWidget(acceptButton);
55 buttonLayout->setAlignment(acceptButton, Qt::AlignCenter);
56 setWindowTitle(tr("About HDRMerge..."));
57 buttonLayout->setSizeConstraint(QLayout::SetFixedSize);
58 }
59
60 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _ABOUTDIALOG_H_
23 #define _ABOUTDIALOG_H_
24
25 #include <QDialog>
26
27 namespace hdrmerge {
28
29 class AboutDialog : public QDialog {
30 public:
31 explicit AboutDialog(QWidget * parent = 0, Qt::WindowFlags f = 0);
32 void closeEvent(QCloseEvent * event) { accept(); }
33
34 Q_OBJECT
35 };
36
37 } // namespace hdrmerge
38
39 #endif // _ABOUTDIALOG_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _ARRAY2D_HPP_
23 #define _ARRAY2D_HPP_
24
25 #include <memory>
26 #include <cstdint>
27 #include <algorithm>
28
29 namespace hdrmerge {
30
31 template <typename T>
32 class Array2D {
33 public:
34 Array2D(size_t w, size_t h) { resize(w, h); }
35 Array2D() : Array2D(0, 0) {}
36 Array2D(const Array2D<T> & copy) {
37 (*this) = copy;
38 }
39 template <typename Y> Array2D(const Array2D<Y> & copy) {
40 (*this) = copy;
41 }
42 Array2D(Array2D<T> && move) noexcept {
43 (*this) = std::move(move);
44 }
45 virtual ~Array2D() {}
46
47 Array2D<T> & operator=(Array2D<T> && move) noexcept {
48 data = std::move(move.data);
49 alignedData = move.alignedData;
50 width = move.width;
51 height = move.height;
52 dx = move.dx;
53 dy = move.dy;
54 move.resize(0, 0);
55 return *this;
56 }
57 Array2D<T> & operator=(const Array2D<T> & copy) {
58 resize(copy.getWidth(), copy.getHeight());
59 std::copy_n(copy.data.get(), width*height, data.get());
60 displace(copy.getDeltaX(), copy.getDeltaY());
61 return *this;
62 }
63 template <typename Y> Array2D<T> & operator=(const Array2D<Y> & copy) {
64 resize(copy.getWidth(), copy.getHeight());
65 for (size_t i = 0; i < width*height; ++i) {
66 data[i] = copy[i];
67 }
68 displace(copy.getDeltaX(), copy.getDeltaY());
69 return *this;
70 }
71
72 void resize(size_t w, size_t h) {
73 width = w;
74 height = h;
75 dx = dy = 0;
76 data.reset(new T[w*h]);
77 alignedData = data.get();
78 }
79
80 size_t getWidth() const {
81 return width;
82 }
83 size_t getHeight() const {
84 return height;
85 }
86 size_t size() const {
87 return width*height;
88 }
89 int getDeltaX() const {
90 return dx;
91 }
92 int getDeltaY() const {
93 return dy;
94 }
95 const T & operator[](size_t i) const {
96 return data[i];
97 }
98 T & operator[](size_t i) {
99 return data[i];
100 }
101 const T & operator()(size_t x, size_t y) const {
102 return alignedData[y*width + x];
103 }
104 T & operator()(size_t x, size_t y) {
105 return alignedData[y*width + x];
106 }
107 bool contains(int x, int y) const {
108 return x >= dx && x < (int)width + dx && y >= dy && y < (int)height + dy;
109 }
110 void displace(int newDx, int newDy) {
111 dx += newDx;
112 dy += newDy;
113 alignedData = &data[-dy*width - dx];
114 }
115 void fillBorders( T val ) {
116 if(dy > 0) {
117 for(size_t i = 0; i < dy; ++i)
118 for(size_t j = 0; j < width; ++j)
119 data[i*width + j] = val;
120 }
121 if(dx > 0) {
122 for(size_t i = 0; i < height; ++i)
123 for(size_t j = 0; j < dx; ++j)
124 data[i*width + j] = val;
125 }
126 }
127
128 typedef T * iterator;
129 typedef const T * const_iterator;
130 iterator begin() { return data.get(); }
131 iterator end() { return data.get() + width*height; }
132 const_iterator cbegin() const { return data.get(); }
133 const_iterator cend() const { return data.get() + width*height; }
134
135 template <typename F> void traceCircle(int x, int y, int radius, const F & function) {
136 int r2 = radius * radius;
137 int ymin = std::max(-y, -radius), ymax = std::min((int)height - y, radius + 1);
138 int xmin = std::max(-x, -radius), xmax = std::min((int)width - x, radius + 1);
139 for (int row = ymin, rrow = y + row; row < ymax; ++row, ++rrow) {
140 for (int col = xmin, rcol = x + col; col < xmax; ++col, ++rcol) {
141 if (row*row + col*col <= r2) {
142 function(rcol, rrow, operator()(rcol, rrow));
143 }
144 }
145 }
146 }
147
148 template <typename F> void traceSquare(int x, int y, int radius, const F & function) {
149 int ymin = std::max(-y, -radius), ymax = std::min((int)height - y, radius + 1);
150 int xmin = std::max(-x, -radius), xmax = std::min((int)width - x, radius + 1);
151 for (int rrow = y + ymin; rrow < y + ymax; ++rrow) {
152 for (int rcol = x + xmin; rcol < x + xmax; ++rcol) {
153 function(rcol, rrow, operator()(rcol, rrow));
154 }
155 }
156 }
157
158 protected:
159 std::unique_ptr<T[]> data;
160 T * alignedData;
161 size_t width, height;
162 int dx, dy;
163 };
164
165 } // namespace hdrmerge
166
167 #endif // _ARRAY2D_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <fstream>
23 #include <sstream>
24 #include "Bitmap.hpp"
25 using namespace hdrmerge;
26
27 const int Bitmap::ones[256] = {
28 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4,
29 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
30 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
31 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
32 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
33 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
34 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
35 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
36 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5,
37 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
38 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
39 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
40 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6,
41 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
42 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7,
43 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8
44 };
45
46
47 Bitmap::Bitmap(size_t w, size_t h) {
48 resize(w, h);
49 }
50
51
52 void Bitmap::resize(size_t w, size_t h) {
53 rowWidth = w;
54 numBits = w * h;
55 size_t extra = numBits & 31 ? 1 : 0;
56 size = (numBits >> 5) + extra;
57 bits.reset(new uint32_t[size]);
58 bits[size-1] &= allOnes >> (32 - (numBits & 31));
59 }
60
61
62 void Bitmap::shift(const Bitmap & src, int dx, int dy) {
63 int pos = -dy*rowWidth - dx;
64 int div = pos >> 5;
65 size_t b = pos & 31;
66 uint32_t mask1 = allOnes << b, mask2 = ~mask1;
67 for (size_t i = 0; i < size; ++i) {
68 bits[i] = 0;
69 if (div >= 0 && div < (int)size) {
70 bits[i] += (src.bits[div] & mask1) >> b;
71 }
72 ++div;
73 if (div >= 0 && div < (int)size) {
74 bits[i] += (src.bits[div] & mask2) << (32-b);
75 }
76 }
77 bits[size-1] &= allOnes >> (32 - (numBits & 31));
78 applyRowMask(dx);
79 }
80
81
82 void Bitmap::applyRowMask(int dx) {
83 size_t a, b;
84 if (dx > 0 && dx <= (int)rowWidth) {
85 a = 0;
86 b = dx;
87 } else if (dx < 0 && dx >= -(int)rowWidth) {
88 a = rowWidth + dx;
89 b = rowWidth;
90 } else return;
91 for (size_t disp = 0; disp < numBits; disp += rowWidth) {
92 size_t padiv = (disp + a) >> 5, pbdiv = (disp + b) >> 5;
93 size_t pamod = (disp + a) & 31, pbmod = (disp + b) & 31;
94 uint32_t amask = pamod ? allOnes >> (32 - pamod) : 0;
95 uint32_t bmask = allOnes << pbmod;
96 if (padiv == pbdiv) {
97 uint32_t mask = amask | bmask;
98 bits[padiv] &= mask;
99 } else {
100 bits[padiv] &= amask;
101 for (size_t i = padiv + 1; i < pbdiv; ++i)
102 bits[i] &= 0;
103 if (pbdiv < size)
104 bits[pbdiv] &= bmask;
105 }
106 }
107 }
108
109
110 void Bitmap::bitwiseXor(const Bitmap & r) {
111 for (size_t i = 0; i < size; ++i) {
112 bits[i] ^= r.bits[i];
113 }
114 }
115
116
117 void Bitmap::bitwiseAnd(const Bitmap & r) {
118 for (size_t i = 0; i < size; ++i) {
119 bits[i] &= r.bits[i];
120 }
121 }
122
123
124 void Bitmap::mtb(const uint16_t * pixels, uint16_t mth) {
125 size_t i = 0;
126 for (iterator p = position(0, 0); p != end(); ++p) {
127 p.set(pixels[i++] > mth);
128 }
129 bits[size-1] &= allOnes >> (32 - (numBits & 31));
130 }
131
132
133 void Bitmap::exclusion(const uint16_t * pixels, uint16_t mth, uint16_t tolerance) {
134 size_t i = 0;
135 uint16_t min = mth - tolerance, max = mth + tolerance;
136 for (iterator p = position(0, 0); p != end(); ++p) {
137 p.set(pixels[i] <= min || pixels[i] > max);
138 ++i;
139 }
140 bits[size-1] &= allOnes >> (32 - (numBits & 31));
141 }
142
143
144 size_t Bitmap::count() const {
145 size_t c = 0;
146 for (size_t i = 0; i < size; ++i) {
147 if (bits[i]) {
148 uint8_t * b = (uint8_t *)&bits[i];
149 c += ones[b[0]] + ones[b[1]] + ones[b[2]] + ones[b[3]];
150 }
151 }
152 return c;
153 }
154
155
156 std::string Bitmap::dumpInfo() {
157 std::ostringstream oss;
158 iterator it = position(0, 0);
159 int height = numBits / rowWidth;
160 for (int row = 0; row < height; ++row) {
161 for (int col = 0; col < (int)rowWidth; ++col, ++it) {
162 oss << (it.get() ? '1' : '0');
163 }
164 oss << std::endl;
165 }
166 return oss.str();
167 }
168
169 void Bitmap::dumpFile(const std::string & fileName) {
170 std::ofstream of(fileName + ".pbm");
171 of << "P1\n# Foo\n" << rowWidth << " " << (numBits/rowWidth) << "\n";
172 size_t tb = 0;
173 for (size_t i = 0; i < size; ++i) {
174 uint32_t value = bits[i];
175 for (int b = 0; tb < numBits && b < 32; ++tb, ++b) {
176 of << ' ' << (value % 2);
177 value >>= 1;
178 }
179 of << "\n";
180 }
181 }
182
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _BITMAP_HPP_
23 #define _BITMAP_HPP_
24
25 #include <cstdint>
26 #include <memory>
27
28 namespace hdrmerge {
29
30 class Bitmap {
31 public:
32 Bitmap() : Bitmap(0, 0) {}
33 Bitmap(size_t w, size_t h);
34
35 void shift(const Bitmap & src, int dx, int dy);
36 void bitwiseXor(const Bitmap & r);
37 void bitwiseAnd(const Bitmap & r);
38 void mtb(const uint16_t * pixels, uint16_t mth);
39 void exclusion(const uint16_t * pixels, uint16_t mth, uint16_t tolerance);
40 void reset() {
41 for (size_t i = 0; i < size; ++i) {
42 bits[i] = 0;
43 }
44 }
45
46 class iterator {
47 public:
48 void set(bool v = true) const {
49 *div = v ? *div | mask : *div & ~mask;
50 }
51 void reset() {
52 *div &= ~mask;
53 }
54 bool get() const {
55 return *div & mask;
56 }
57 bool operator==(const iterator & r) const {
58 return div == r.div && mask == r.mask;
59 }
60 bool operator!=(const iterator & r) const {
61 return !(*this == r);
62 }
63 iterator & operator++() {
64 mask <<= 1;
65 if (!mask) {
66 mask = 1;
67 div++;
68 }
69 return *this;
70 }
71 iterator & operator+=(size_t h) {
72 size_t rem = h & 31;
73 div += h >> 5;
74 uint32_t newmask = mask << rem;
75 if (newmask) {
76 mask = newmask;
77 } else {
78 mask >>= 32 - rem;
79 ++div;
80 }
81 return *this;
82 }
83 private:
84 friend class Bitmap;
85 uint32_t * div;
86 uint32_t mask;
87 iterator(Bitmap & b, size_t pos) {
88 div = &b.bits[pos >> 5];
89 mask = 1 << (pos & 31);
90 }
91 };
92
93 iterator position(size_t x, size_t y) {
94 return iterator(*this, y*rowWidth + x);
95 }
96 const iterator position(size_t x, size_t y) const {
97 return iterator(*const_cast<Bitmap *>(this), y*rowWidth + x);
98 }
99 const iterator end() const {
100 return iterator(*const_cast<Bitmap *>(this), numBits);
101 }
102
103 size_t count() const;
104 size_t getWidth() const {
105 return rowWidth;
106 }
107 void resize(size_t w, size_t h);
108
109 std::string dumpInfo();
110 void dumpFile(const std::string & fileName);
111
112 private:
113 static const int ones[256];
114 static const uint32_t allOnes = -1;
115
116 std::unique_ptr<uint32_t[]> bits;
117 size_t rowWidth, size, numBits;
118
119 void applyRowMask(int dx);
120 };
121
122 } // namespace hdrmerge
123
124 #endif // _BITMAP_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <cmath>
23 #include "BoxBlur.hpp"
24
25 namespace hdrmerge {
26
27 void BoxBlur::blur(size_t radius) {
28 // From http://blog.ivank.net/fastest-gaussian-blur.html
29 tmp.reset(new float[width*height]);
30 size_t hr = std::round(radius*0.39);
31 boxBlur(hr);
32 boxBlur(hr);
33 boxBlur(hr);
34 tmp.reset();
35 }
36
37
38 void BoxBlur::boxBlur(size_t radius) {
39 boxBlurH(radius);
40 data.swap(tmp);
41 boxBlurT(radius);
42 data.swap(tmp);
43 }
44
45
46 void BoxBlur::boxBlurH(size_t r) {
47 float iarr = 1.0 / (r+r+1);
48 #pragma omp parallel for schedule(dynamic)
49 for (size_t i = 0; i < height; ++i) {
50 size_t ti = i * width, li = ti, ri = ti + r;
51 float val = data[li] * (r + 1);
52 for (size_t j = 0; j < r; ++j) {
53 val += data[li + j];
54 }
55 for (size_t j = 0; j <= r; ++j) {
56 val += data[ri++] - data[li];
57 tmp[ti++] = val*iarr;
58 }
59 for (size_t j = r + 1; j < width - r; ++j) {
60 val += data[ri++] - data[li++];
61 tmp[ti++] = val*iarr;
62 }
63 for (size_t j = width - r; j < width; ++j) {
64 val += data[ri - 1] - data[li++];
65 tmp[ti++] = val*iarr;
66 }
67 }
68 }
69
70
71 void BoxBlur::boxBlurT(size_t r) {
72 float iarr = 1.0 / (r+r+1);
73 const int numCols = 8; // process numCols columns at once for better usage of L1 cpu cache
74 #pragma omp parallel for schedule(dynamic,4)
75 for (size_t i = 0; i < width-numCols+1; i+=numCols) {
76 size_t ti = i, li = ti, ri = ti + r*width;
77 float val[numCols];
78 for(size_t k=0;k<numCols;++k)
79 val[k] = data[li+k] * (r + 1);
80 for(size_t k=0;k<numCols;++k)
81 for (size_t j = 0; j < r; ++j) {
82 val[k] += data[li + j*width + k];
83 }
84 for (size_t j = 0; j <= r; ++j) {
85 for(size_t k=0;k<numCols;++k) {
86 val[k] += data[ri+k] - data[li+k];
87 tmp[ti+k] = val[k]*iarr;
88 }
89 ri += width;
90 ti += width;
91 }
92 for (size_t j = r + 1; j < height - r; ++j) {
93 for(size_t k=0;k<numCols;++k) {
94 val[k] += data[ri+k] - data[li+k];
95 tmp[ti+k] = val[k]*iarr;
96 }
97 li += width;
98 ri += width;
99 ti += width;
100 }
101 for (size_t j = height - r; j < height; ++j) {
102 for(size_t k=0;k<numCols;++k) {
103 val[k] += data[ri - width + k] - data[li+ k];
104 tmp[ti+k] = val[k]*iarr;
105 }
106 li += width;
107 ti += width;
108 }
109 }
110 // process the remaining columns
111 for (size_t i = width - (width%numCols); i < width; ++i) {
112 size_t ti = i, li = ti, ri = ti + r*width;
113 float val = data[li] * (r + 1);
114 for (size_t j = 0; j < r; ++j) {
115 val += data[li + j*width];
116 }
117 for (size_t j = 0; j <= r; ++j) {
118 val += data[ri] - data[li];
119 tmp[ti] = val*iarr;
120 ri += width;
121 ti += width;
122 }
123 for (size_t j = r + 1; j < height - r; ++j) {
124 val += data[ri] - data[li];
125 tmp[ti] = val*iarr;
126 li += width;
127 ri += width;
128 ti += width;
129 }
130 for (size_t j = height - r; j < height; ++j) {
131 val += data[ri - width] - data[li];
132 tmp[ti] = val*iarr;
133 li += width;
134 ti += width;
135 }
136 }
137
138 }
139
140
141 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _BOXBLUR_HPP_
23 #define _BOXBLUR_HPP_
24
25 #include <memory>
26 #include "Array2D.hpp"
27
28 namespace hdrmerge {
29
30 class BoxBlur : public Array2D<float>{
31 public:
32 template <typename T> BoxBlur(const Array2D<T> & src) : Array2D<float>(src) {}
33 void blur(size_t radius);
34
35 private:
36 void boxBlur(size_t radius);
37 void boxBlurH(size_t radius);
38 void boxBlurT(size_t radius);
39 std::unique_ptr<float[]> tmp;
40 };
41 } // namespace hdrmerge
42
43 #endif // _BOXBLUR_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _CFAPATTERN_HPP_
23 #define _CFAPATTERN_HPP_
24
25 #include <functional>
26
27 namespace hdrmerge {
28
29 class CFAPattern {
30 public:
31 void setPattern(uint32_t f, std::function<int(int, int)> fcol) {
32 filters = f;
33 if (filters == 9) {
34 // Fujifilm X-Trans sensor
35 for (int row = 0; row < 6; ++row) {
36 for (int col = 0; col < 6; ++col) {
37 xtrans[row][col] = fcol(row, col);
38 }
39 }
40 }
41 }
42
43 bool operator==(const CFAPattern & r) const {
44 return filters == r.filters;
45 }
46
47 uint8_t operator()(int x, int y) const {
48 // (x, y) is relative to the ACTIVE AREA
49 if (filters == 9) {
50 return xtrans[(y + 6) % 6][(x + 6) % 6];
51 } else {
52 return (filters >> (((y << 1 & 14) | (x & 1)) << 1) & 3);
53 }
54 }
55
56 bool canAlign() const {
57 uint8_t * f = (uint8_t *)&filters;
58 return f[0] == f[1] && f[0] == f[2] && f[0] == f[3];
59 }
60
61 uint32_t getFilters() const { return filters; }
62
63 int getRows() const {
64 if (filters == 9) return 6;
65 else if ((filters & 255) == ((filters >> 8) & 255)) return 2;
66 else return 8;
67 }
68
69 int getColumns() const {
70 return filters == 9 ? 6 : 2;
71 }
72
73 private:
74 uint32_t filters;
75 uint8_t xtrans[6][6];
76 };
77
78 } // namespace hdrmerge
79
80 #endif // _CFAPATTERN_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <iostream>
23 #include <cmath>
24 #include <QBuffer>
25 #include <QDateTime>
26 #include <QImageWriter>
27 #include <zlib.h>
28 #ifdef __SSE2__
29 #include <x86intrin.h>
30 #endif
31
32 #include "config.h"
33 #include "DngFloatWriter.hpp"
34 #include "RawParameters.hpp"
35 #include "Log.hpp"
36 #include "ExifTransfer.hpp"
37 using namespace std;
38
39
40 namespace hdrmerge {
41
42 enum {
43 DNGVERSION = 50706,
44 DNGBACKVERSION = 50707,
45
46 CALIBRATIONILLUMINANT = 50778,
47 COLORMATRIX = 50721,
48 PROFILENAME = 50936,
49
50 NEWSUBFILETYPE = 254,
51 IMAGEWIDTH = 256,
52 IMAGELENGTH = 257,
53 PHOTOINTERPRETATION = 262,
54 SAMPLESPERPIXEL = 277,
55 BITSPERSAMPLE = 258,
56 FILLORDER = 266,
57 ACTIVEAREA = 50829,
58 MASKEDAREAS = 50830,
59 CROPORIGIN = 50719,
60 CROPSIZE = 50720,
61
62 TILEWIDTH = 322,
63 TILELENGTH = 323,
64 TILEOFFSETS = 324,
65 TILEBYTES = 325,
66 ROWSPERSTRIP = 278,
67 STRIPOFFSETS = 273,
68 STRIPBYTES = 279,
69
70 PLANARCONFIG = 284,
71 COMPRESSION = 259,
72 PREDICTOR = 317,
73 SAMPLEFORMAT = 339,
74 BLACKLEVELREP = 50713,
75 BLACKLEVEL = 50714,
76 WHITELEVEL = 50717,
77 CFAPATTERNDIM = 33421,
78 CFAPATTERN = 33422,
79 CFAPLANECOLOR = 50710,
80 CFALAYOUT = 50711,
81
82 ANALOGBALANCE = 50727,
83 CAMERANEUTRAL = 50728,
84 ORIENTATION = 274,
85 UNIQUENAME = 50708,
86 SUBIFDS = 330,
87
88 TIFFEPSTD = 37398,
89 MAKE = 271,
90 MODEL = 272,
91 SOFTWARE = 305,
92 DATETIMEORIGINAL = 36867,
93 DATETIME = 306,
94 IMAGEDESCRIPTION = 270,
95 RESOLUTIONUNIT = 269,
96 XRESOLUTION = 282,
97 YRESOLUTION = 283,
98 COPYRIGHT = 33432,
99
100 YCBCRCOEFFS = 529,
101 YCBCRSUBSAMPLING = 530,
102 YCBCRPOSITIONING = 531,
103 REFBLACKWHITE = 532,
104 };
105
106
107 enum {
108 TIFF_CM = 3,
109 TIFF_D65 = 21,
110 TIFF_RGB = 2,
111 TIFF_UNCOMPRESSED = 1,
112 TIFF_DEFLATE = 8,
113 TIFF_JPEG = 7,
114 TIFF_FP2XPREDICTOR = 34894,
115 TIFF_FPFORMAT = 3,
116 TIFF_CFA = 32803,
117 TIFF_YCBCR = 6,
118 };
119
120
121 void DngFloatWriter::write(Array2D<float> && rawPixels, const RawParameters & p, const QString & dstFileName) {
122 params = &p;
123 rawData = std::move(rawPixels);
124 width = rawData.getWidth();
125 height = rawData.getHeight();
126
127 renderPreviews();
128
129 createMainIFD();
130 subIFDoffsets[0] = 8 + mainIFD.length();
131 createRawIFD();
132 size_t dataOffset = subIFDoffsets[0] + rawIFD.length();
133 if (previewWidth > 0) {
134 createPreviewIFD();
135 subIFDoffsets[1] = subIFDoffsets[0] + rawIFD.length();
136 dataOffset += previewIFD.length();
137 }
138 mainIFD.setValue(SUBIFDS, (const void *)subIFDoffsets);
139 pos = dataOffset;
140 size_t dataSize = dataOffset + thumbSize() + previewSize() + rawSize();
141 fileData.reset(new uint8_t[dataSize]);
142
143 Timer t("Write output");
144 writePreviews();
145 writeRawData();
146 dataSize = pos;
147 pos = 0;
148 TiffHeader().write(fileData.get(), pos);
149 mainIFD.write(fileData.get(), pos, false);
150 rawIFD.write(fileData.get(), pos, false);
151 if (previewWidth > 0) {
152 previewIFD.write(fileData.get(), pos, false);
153 }
154
155 Exif::transfer(p.fileName, dstFileName, fileData.get(), dataSize);
156 }
157
158
159 void DngFloatWriter::createMainIFD() {
160 uint8_t dngVersion[] = { 1, 4, 0, 0 };
161 mainIFD.addEntry(DNGVERSION, IFD::BYTE, 4, dngVersion);
162 mainIFD.addEntry(DNGBACKVERSION, IFD::BYTE, 4, dngVersion);
163 uint8_t tiffep[] = { 1, 0, 0, 0 };
164 mainIFD.addEntry(TIFFEPSTD, IFD::BYTE, 4, tiffep);
165 mainIFD.addEntry(MAKE, params->maker);
166 mainIFD.addEntry(MODEL, params->model);
167 mainIFD.addEntry(SOFTWARE, "HDRMerge " HDRMERGE_VERSION_STRING);
168 mainIFD.addEntry(RESOLUTIONUNIT, IFD::SHORT, TIFF_CM);
169 uint32_t resolution[] = { 100, 1 };
170 mainIFD.addEntry(XRESOLUTION, IFD::RATIONAL, 1, resolution);
171 mainIFD.addEntry(YRESOLUTION, IFD::RATIONAL, 1, resolution);
172 mainIFD.addEntry(COPYRIGHT, "");
173 mainIFD.addEntry(IMAGEDESCRIPTION, params->description);
174 QDateTime currentTime = QDateTime::currentDateTime();
175 QString currentTimeText = currentTime.toString("yyyy:MM:dd hh:mm:ss");
176 mainIFD.addEntry(DATETIME, currentTimeText.toLatin1().constData());
177 mainIFD.addEntry(DATETIMEORIGINAL, params->dateTime);
178
179 // Profile
180 mainIFD.addEntry(CALIBRATIONILLUMINANT, IFD::SHORT, TIFF_D65);
181 string profName(params->maker + " " + params->model);
182 mainIFD.addEntry(PROFILENAME, profName);
183 int32_t colorMatrix[24];
184 for (int row = 0, i = 0; row < params->colors; ++row) {
185 for (int col = 0; col < 3; ++col) {
186 colorMatrix[i++] = std::round(params->camXyz[row][col] * 10000.0f);
187 colorMatrix[i++] = 10000;
188 }
189 }
190 mainIFD.addEntry(COLORMATRIX, IFD::SRATIONAL, params->colors * 3, colorMatrix);
191
192 // Color
193 uint32_t analogBalance[] = { 1, 1, 1, 1, 1, 1, 1, 1 };
194 mainIFD.addEntry(ANALOGBALANCE, IFD::RATIONAL, params->colors, analogBalance);
195 double wb[] = { 1.0/params->camMul[0], 1.0/params->camMul[1], 1.0/params->camMul[2], 1.0/params->camMul[3] };
196 uint32_t cameraNeutral[] = {
197 (uint32_t)std::round(1000000.0 * wb[0]), 1000000,
198 (uint32_t)std::round(1000000.0 * wb[1]), 1000000,
199 (uint32_t)std::round(1000000.0 * wb[2]), 1000000,
200 (uint32_t)std::round(1000000.0 * wb[3]), 1000000};
201 mainIFD.addEntry(CAMERANEUTRAL, IFD::RATIONAL, params->colors, cameraNeutral);
202 mainIFD.addEntry(ORIENTATION, IFD::SHORT, params->tiffOrientation);
203 mainIFD.addEntry(UNIQUENAME, params->maker + " " + params->model);
204 // TODO: Add Digest and Unique ID
205 mainIFD.addEntry(SUBIFDS, IFD::LONG, previewWidth > 0 ? 2 : 1, subIFDoffsets);
206
207 // Thumbnail
208 mainIFD.addEntry(NEWSUBFILETYPE, IFD::LONG, 1);
209 mainIFD.addEntry(IMAGEWIDTH, IFD::LONG, thumbnail.width());
210 mainIFD.addEntry(IMAGELENGTH, IFD::LONG, thumbnail.height());
211 mainIFD.addEntry(SAMPLESPERPIXEL, IFD::SHORT, 3);
212 uint16_t bpsthumb[] = {8, 8, 8};
213 mainIFD.addEntry(BITSPERSAMPLE, IFD::SHORT, 3, bpsthumb);
214 mainIFD.addEntry(PLANARCONFIG, IFD::SHORT, 1);
215 mainIFD.addEntry(PHOTOINTERPRETATION, IFD::SHORT, TIFF_RGB);
216 mainIFD.addEntry(COMPRESSION, IFD::SHORT, TIFF_UNCOMPRESSED);
217 mainIFD.addEntry(ROWSPERSTRIP, IFD::LONG, thumbnail.height());
218 mainIFD.addEntry(STRIPBYTES, IFD::LONG, 0);
219 mainIFD.addEntry(STRIPOFFSETS, IFD::LONG, 0);
220 }
221
222
223 void DngFloatWriter::calculateTiles() {
224 int bytesPerTile = 512 * 1024;
225 int cellSize = 16;
226 uint32_t bytesPerSample = bps >> 3;
227 uint32_t samplesPerTile = bytesPerTile / bytesPerSample;
228 uint32_t tileSide = std::round(std::sqrt(samplesPerTile));
229 tileWidth = std::min(width, tileSide);
230 tilesAcross = (width + tileWidth - 1) / tileWidth;;
231 tileWidth = (width + tilesAcross - 1) / tilesAcross;
232 tileWidth = ((tileWidth + cellSize - 1) / cellSize) * cellSize;
233 tileLength = std::min(samplesPerTile / tileWidth, height);
234 tilesDown = (height + tileLength - 1) / tileLength;
235 tileLength = (height + tilesDown - 1) / tilesDown;
236 tileLength = ((tileLength + cellSize - 1) / cellSize) * cellSize;
237 }
238
239
240 void DngFloatWriter::createRawIFD() {
241 uint16_t cfaRows = params->FC.getRows(), cfaCols = params->FC.getColumns();
242 uint16_t cfaPatternDim[] = { cfaRows, cfaCols };
243
244 rawIFD.addEntry(NEWSUBFILETYPE, IFD::LONG, 0x10001); // Fix it later in ExifTransfer.cpp
245 rawIFD.addEntry(IMAGEWIDTH, IFD::LONG, width);
246 rawIFD.addEntry(IMAGELENGTH, IFD::LONG, height);
247
248 // Areas
249 uint32_t crop[2];
250 crop[0] = crop[1] = 0;
251 rawIFD.addEntry(CROPORIGIN, IFD::LONG, 2, crop);
252 crop[0] = params->width;
253 crop[1] = params->height;
254 rawIFD.addEntry(CROPSIZE, IFD::LONG, 2, crop);
255 uint32_t aa[4];
256 aa[0] = params->topMargin;
257 aa[1] = params->leftMargin;
258 aa[2] = aa[0] + params->height;
259 aa[3] = aa[1] + params->width;
260 rawIFD.addEntry(ACTIVEAREA, IFD::LONG, 4, aa);
261 rawIFD.addEntry(BLACKLEVELREP, IFD::SHORT, 2, cfaPatternDim);
262 uint16_t cblack[cfaRows * cfaCols];
263 for (int row = 0; row < cfaRows; ++row) {
264 for (int col = 0; col < cfaCols; ++col) {
265 cblack[row*cfaCols + col] = params->blackAt(col, row);
266 }
267 }
268 rawIFD.addEntry(BLACKLEVEL, IFD::SHORT, cfaRows * cfaCols, cblack);
269 rawIFD.addEntry(WHITELEVEL, IFD::SHORT, params->max);
270 rawIFD.addEntry(SAMPLESPERPIXEL, IFD::SHORT, 1);
271 rawIFD.addEntry(BITSPERSAMPLE, IFD::SHORT, bps);
272 if (bps == 24) {
273 rawIFD.addEntry(FILLORDER, IFD::SHORT, 1);
274 }
275 rawIFD.addEntry(PLANARCONFIG, IFD::SHORT, 1);
276 rawIFD.addEntry(COMPRESSION, IFD::SHORT, TIFF_DEFLATE);
277 rawIFD.addEntry(PREDICTOR, IFD::SHORT, TIFF_FP2XPREDICTOR);
278 rawIFD.addEntry(SAMPLEFORMAT, IFD::SHORT, TIFF_FPFORMAT);
279
280 calculateTiles();
281 uint32_t numTiles = tilesAcross * tilesDown;
282 uint32_t buffer[numTiles];
283 rawIFD.addEntry(TILEWIDTH, IFD::LONG, tileWidth);
284 rawIFD.addEntry(TILELENGTH, IFD::LONG, tileLength);
285 rawIFD.addEntry(TILEOFFSETS, IFD::LONG, numTiles, buffer);
286 rawIFD.addEntry(TILEBYTES, IFD::LONG, numTiles, buffer);
287
288 rawIFD.addEntry(PHOTOINTERPRETATION, IFD::SHORT, TIFF_CFA);
289 rawIFD.addEntry(CFAPATTERNDIM, IFD::SHORT, 2, cfaPatternDim);
290 uint8_t cfaPattern[cfaRows * cfaCols];
291 for (int row = 0; row < cfaRows; ++row) {
292 for (int col = 0; col < cfaCols; ++col) {
293 cfaPattern[row*cfaCols + col] = params->FC(col, row);
294 }
295 }
296 if (params->colors == 3) {
297 for (uint8_t & i : cfaPattern) {
298 if (i == 3) i = 1;
299 }
300 }
301 rawIFD.addEntry(CFAPATTERN, IFD::BYTE, cfaRows * cfaCols, cfaPattern);
302 uint8_t cfaPlaneColor[] = { 0, 1, 2, 3 };
303 rawIFD.addEntry(CFAPLANECOLOR, IFD::BYTE, params->colors, cfaPlaneColor);
304 rawIFD.addEntry(CFALAYOUT, IFD::SHORT, 1);
305 }
306
307
308 void DngFloatWriter::createPreviewIFD() {
309 previewIFD.addEntry(NEWSUBFILETYPE, IFD::LONG, 1);
310 previewIFD.addEntry(IMAGEWIDTH, IFD::LONG, preview.width());
311 previewIFD.addEntry(IMAGELENGTH, IFD::LONG, preview.height());
312 previewIFD.addEntry(SAMPLESPERPIXEL, IFD::SHORT, 3);
313 uint16_t bpspre[] = {8, 8, 8};
314 previewIFD.addEntry(BITSPERSAMPLE, IFD::SHORT, 3, bpspre);
315 previewIFD.addEntry(PLANARCONFIG, IFD::SHORT, 1);
316 previewIFD.addEntry(PHOTOINTERPRETATION, IFD::SHORT, TIFF_YCBCR);
317 previewIFD.addEntry(COMPRESSION, IFD::SHORT, TIFF_JPEG);
318 previewIFD.addEntry(ROWSPERSTRIP, IFD::LONG, preview.height());
319 previewIFD.addEntry(STRIPBYTES, IFD::LONG, 0);
320 previewIFD.addEntry(STRIPOFFSETS, IFD::LONG, 0);
321 uint16_t subsampling[] = { 2, 2 };
322 previewIFD.addEntry(YCBCRSUBSAMPLING, IFD::SHORT, 2, subsampling);
323 previewIFD.addEntry(YCBCRPOSITIONING, IFD::SHORT, 2);
324 uint32_t coefficients[] = { 299, 1000, 587, 1000, 114, 1000 };
325 previewIFD.addEntry(YCBCRCOEFFS, IFD::RATIONAL, 3, coefficients);
326 uint32_t refs[] = { 0, 1, 255, 1, 128, 1, 255, 1, 128, 1, 255, 1};
327 previewIFD.addEntry(REFBLACKWHITE, IFD::RATIONAL, 6, refs);
328 }
329
330
331 void DngFloatWriter::renderPreviews() {
332 if (previewWidth > 0) {
333 QBuffer buffer(&jpegPreviewData);
334 buffer.open(QIODevice::WriteOnly);
335 QImageWriter writer(&buffer, "JPG");
336 writer.setQuality(85);
337 if (!writer.write(preview)) {
338 cerr << "Error converting the preview to JPEG: " << writer.errorString() << endl;
339 previewWidth = 0;
340 }
341 }
342 }
343
344
345 void DngFloatWriter::setPreview(const QImage & p) {
346 thumbnail = p.scaledToWidth(256, Qt::SmoothTransformation).convertToFormat(QImage::Format_RGB888);
347 if (previewWidth != p.width()) {
348 preview = p.scaledToWidth(previewWidth, Qt::SmoothTransformation);
349 } else {
350 preview = p;
351 }
352 }
353
354
355 size_t DngFloatWriter::thumbSize() {
356 return thumbnail.width() * thumbnail.height() * 3;
357 }
358
359
360 size_t DngFloatWriter::previewSize() {
361 return previewWidth > 0 ? jpegPreviewData.size() : 0;
362 }
363
364
365 void DngFloatWriter::writePreviews() {
366 size_t ts = thumbSize();
367 mainIFD.setValue(STRIPBYTES, ts);
368 mainIFD.setValue(STRIPOFFSETS, pos);
369 pos = std::copy_n((const uint8_t *)thumbnail.bits(), ts, &fileData[pos]) - fileData.get();
370 if (previewWidth > 0) {
371 ts = previewSize();
372 previewIFD.setValue(STRIPBYTES, ts);
373 previewIFD.setValue(STRIPOFFSETS, pos);
374 pos = std::copy_n((const uint8_t *)jpegPreviewData.constData(), ts, &fileData[pos]) - fileData.get();
375 }
376 }
377
378
379 static void encodeFPDeltaRow(Bytef * src, Bytef * dst, size_t tileWidth, size_t realTileWidth, int bytesps, int factor) {
380 // Reorder bytes into the image
381 // 16 and 32-bit versions depend on local architecture, 24-bit does not
382 if (bytesps == 3) {
383 for (size_t col = 0; col < tileWidth; ++col) {
384 dst[col] = src[col*3];
385 dst[col + realTileWidth] = src[col*3 + 1];
386 dst[col + realTileWidth*2] = src[col*3 + 2];
387 }
388 } else {
389 for (size_t col = 0; col < tileWidth; ++col) {
390 for (int byte = 0; byte < bytesps; ++byte)
391 #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN
392 dst[col + realTileWidth*(bytesps-byte-1)] = src[col*bytesps + byte];
393 #else
394 dst[col + realTileWidth*byte] = src[col*bytesps + byte];
395 #endif
396 }
397 }
398 // EncodeDeltaBytes
399 for (int col = realTileWidth*bytesps - 1; col >= factor; --col) {
400 dst[col] -= dst[col - factor];
401 }
402 }
403
404
405 // From DNG SDK dng_utils.h
406 inline uint16_t DNG_FloatToHalf(uint32_t i) {
407 int32_t sign = (i >> 16) & 0x00008000;
408 int32_t exponent = ((i >> 23) & 0x000000ff) - (127 - 15);
409 int32_t mantissa = i & 0x007fffff;
410 if (exponent <= 0) {
411 if (exponent < -10) {
412 return (uint16_t)sign;
413 }
414 mantissa = (mantissa | 0x00800000) >> (1 - exponent);
415 if (mantissa & 0x00001000)
416 mantissa += 0x00002000;
417 return (uint16_t)(sign | (mantissa >> 13));
418 } else if (exponent == 0xff - (127 - 15)) {
419 if (mantissa == 0) {
420 return (uint16_t)(sign | 0x7c00);
421 } else {
422 return (uint16_t)(sign | 0x7c00 | (mantissa >> 13));
423 }
424 }
425 if (mantissa & 0x00001000) {
426 mantissa += 0x00002000;
427 if (mantissa & 0x00800000) {
428 mantissa = 0; // overflow in significand,
429 exponent += 1; // adjust exponent
430 }
431 }
432 if (exponent > 30) {
433 return (uint16_t)(sign | 0x7c00); // infinity with the same sign as f.
434 }
435 return (uint16_t)(sign | (exponent << 10) | (mantissa >> 13));
436 }
437
438
439 inline void DNG_FloatToFP24(uint32_t input, uint8_t *output) {
440 int32_t exponent = (int32_t) ((input >> 23) & 0xFF) - 128;
441 int32_t mantissa = input & 0x007FFFFF;
442 if (exponent == 127) {
443 if (mantissa != 0x007FFFFF && ((mantissa >> 7) == 0xFFFF)) {
444 mantissa &= 0x003FFFFF; // knock out msb to make it a NaN
445 }
446 } else if (exponent > 63) {
447 exponent = 63;
448 mantissa = 0x007FFFFF;
449 } else if (exponent <= -64) {
450 if (exponent >= -79) {
451 mantissa = (mantissa | 0x00800000) >> (-63 - exponent);
452 } else {
453 mantissa = 0;
454 }
455 exponent = -64;
456 }
457 output [0] = (uint8_t)(((input >> 24) & 0x80) | (uint32_t) (exponent + 64));
458 output [1] = (mantissa >> 15) & 0x00FF;
459 output [2] = (mantissa >> 7) & 0x00FF;
460 }
461
462
463 static void compressFloats(Bytef * dst, int tileWidth, int bytesps) {
464 if (bytesps == 2) {
465 uint16_t * dst16 = (uint16_t *) dst;
466 #ifndef __F16C__
467 uint32_t * dst32 = (uint32_t *) dst;
468 for (int i = 0; i < tileWidth; ++i) {
469 dst16[i] = DNG_FloatToHalf(dst32[i]);
470 }
471 #else
472 float * dst32 = (float *) dst;
473 int i = 0;
474 for (; i < tileWidth - 7; i += 8) {
475 __m128 singleFloat1 = _mm_loadu_ps(&dst32[i]);
476 __m128i halfFloat1 = _mm_cvtps_ph(singleFloat1, 0);
477 __m128 singleFloat2 = _mm_loadu_ps(&dst32[i + 4]);
478 __m128i halfFloat2 = _mm_cvtps_ph(singleFloat2, 0);
479 _mm_storeu_si128((__m128i*)&dst16[i], (__m128i)_mm_shuffle_ps((__m128)halfFloat1, (__m128)halfFloat2, _MM_SHUFFLE(1, 0, 1, 0)));
480 }
481 for (; i < tileWidth - 3; i += 4) {
482 __m128 singleFloat1 = _mm_loadu_ps(&dst32[i]);
483 __m128i halfFloat1 = _mm_cvtps_ph(singleFloat1, 0);
484 _mm_storeu_si128((__m128i*)&dst16[i], halfFloat1);
485 }
486 for (; i < tileWidth; ++i) {
487 dst16[i] = _cvtss_sh(dst32[i], 0);
488 }
489
490 #endif
491 } else if (bytesps == 3) {
492 uint8_t * dst8 = (uint8_t *) dst;
493 uint32_t * dst32 = (uint32_t *) dst;
494 for (int i = 0; i < tileWidth; ++i) {
495 DNG_FloatToFP24(dst32[i], dst8);
496 dst8 += 3;
497 }
498 }
499 }
500
501
502 size_t DngFloatWriter::rawSize() {
503 // Worst case size
504 return tilesAcross * tilesDown * tileWidth * tileLength * (bps >> 3);
505 }
506
507
508 void DngFloatWriter::writeRawData() {
509 size_t tileCount = tilesAcross * tilesDown;
510 uint32_t tileOffsets[tileCount];
511 uint32_t tileBytes[tileCount];
512 int bytesps = bps >> 3;
513 uLongf dstLen = tileWidth * tileLength * bytesps;
514
515 #pragma omp parallel
516 {
517 Bytef * cBuffer = new Bytef[dstLen];
518 Bytef * uBuffer = new Bytef[dstLen];
519
520 #pragma omp for collapse(2) schedule(dynamic)
521 for (size_t y = 0; y < height; y += tileLength) {
522 for (size_t x = 0; x < width; x += tileWidth) {
523 size_t t = (y / tileLength) * tilesAcross + (x / tileWidth);
524 size_t thisTileLength = y + tileLength > height ? height - y : tileLength;
525 size_t thisTileWidth = x + tileWidth > width ? width - x : tileWidth;
526 if (thisTileLength != tileLength || thisTileWidth != tileWidth) {
527 fill_n(uBuffer, dstLen, 0);
528 }
529 for (size_t row = 0; row < thisTileLength; ++row) {
530 Bytef * dst = uBuffer + row*tileWidth*bytesps;
531 Bytef * src = (Bytef *)&rawData(x, y+row);
532 compressFloats(src, thisTileWidth, bytesps);
533 encodeFPDeltaRow(src, dst, thisTileWidth, tileWidth, bytesps, 2);
534 }
535 uLongf conpressedLength = dstLen;
536 int err = compress(cBuffer, &conpressedLength, uBuffer, dstLen);
537 tileBytes[t] = conpressedLength;
538 if (err != Z_OK) {
539 std::cerr << "DNG Deflate: Failed compressing tile " << t << ", with error " << err << std::endl;
540 } else {
541 #pragma omp critical
542 {
543 tileOffsets[t] = pos;
544 std::copy_n((const uint8_t *)cBuffer, tileBytes[t], &fileData[pos]);
545 pos += tileBytes[t];
546 }
547 }
548 }
549 }
550
551 delete [] cBuffer;
552 delete [] uBuffer;
553 }
554
555 rawIFD.setValue(TILEOFFSETS, tileOffsets);
556 rawIFD.setValue(TILEBYTES, tileBytes);
557 }
558
559 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _DNGFLOATWRITER_HPP_
23 #define _DNGFLOATWRITER_HPP_
24
25 #include <QString>
26 #include <QImage>
27 #include "config.h"
28 #include "Array2D.hpp"
29 #include "TiffDirectory.hpp"
30
31 namespace hdrmerge {
32
33 class RawParameters;
34
35 class DngFloatWriter {
36 public:
37 DngFloatWriter() : previewWidth(0), bps(16) {}
38
39 void setPreviewWidth(size_t w) {
40 previewWidth = w;
41 }
42 void setBitsPerSample(int b) {
43 bps = b;
44 }
45 void setPreview(const QImage & p);
46 void write(Array2D<float> && rawPixels, const RawParameters & p, const QString & dstFileName);
47
48 private:
49 int previewWidth;
50 int bps;
51 const RawParameters * params;
52 Array2D<float> rawData;
53 std::unique_ptr<uint8_t[]> fileData;
54 size_t pos;
55 IFD mainIFD, rawIFD, previewIFD;
56 uint32_t width, height;
57 uint32_t tileWidth, tileLength;
58 uint32_t tilesAcross, tilesDown;
59 QImage thumbnail;
60 QImage preview;
61 QByteArray jpegPreviewData;
62 uint32_t subIFDoffsets[2];
63
64 void createMainIFD();
65 void createRawIFD();
66 void calculateTiles();
67 void writeRawData();
68 void renderPreviews();
69 void writePreviews();
70 void createPreviewIFD();
71 size_t thumbSize();
72 size_t previewSize();
73 size_t rawSize();
74 };
75
76 } // namespace hdrmerge
77
78 #endif // _DNGFLOATWRITER_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <QCoreApplication>
23 #include <QFormLayout>
24 #include <QVBoxLayout>
25 #include <QRadioButton>
26 #include <QButtonGroup>
27 #include <QPushButton>
28 #include <QFileDialog>
29 #include <QSettings>
30 #include <QSpinBox>
31 #include "DngPropertiesDialog.hpp"
32
33 namespace hdrmerge {
34
35 DngPropertiesDialog::DngPropertiesDialog(QWidget * parent, Qt::WindowFlags f)
36 : QDialog(parent, f), SaveOptions() {
37 loadDefaultOptions();
38
39 QVBoxLayout * layout = new QVBoxLayout(this);
40
41 QWidget * bpsSelector = new QWidget(this);
42 QHBoxLayout * bpsSelectorLayout = new QHBoxLayout(bpsSelector);
43 bpsSelectorLayout->setMargin(0);
44 QButtonGroup * bpsGroup = new QButtonGroup(this);
45 const char * buttonLabels[] = { "16", "24", "32" };
46 int bpsIndex = (bps - 16) / 8;;
47 for (int i = 0; i < 3; ++i) {
48 QRadioButton * button = new QRadioButton(buttonLabels[i], this);
49 button->setChecked(i == bpsIndex);
50 bpsGroup->addButton(button, i);
51 bpsSelectorLayout->addWidget(button);
52 }
53 connect(bpsGroup, SIGNAL(buttonClicked(int)), this, SLOT(setBps(int)));
54
55 QWidget * previewSelector = new QWidget(this);
56 QHBoxLayout * previewSelectorLayout = new QHBoxLayout(previewSelector);
57 previewSelectorLayout->setMargin(0);
58 QButtonGroup * previewGroup = new QButtonGroup(this);
59 const char * previewLabels[] = { "Full", "Half", "None" };
60 for (int i = 0; i < 3; ++i) {
61 QRadioButton * button = new QRadioButton(tr(previewLabels[i]), this);
62 button->setChecked(i == (2 - previewSize));
63 previewGroup->addButton(button, 2 - i);
64 previewSelectorLayout->addWidget(button);
65 }
66 connect(previewGroup, SIGNAL(buttonClicked( int)), this, SLOT(setPreviewSize(int)));
67
68 QSpinBox * radiusSelector = new QSpinBox(this);
69 radiusSelector->setRange(0, 1000);
70 radiusSelector->setValue(featherRadius);
71 connect(radiusSelector, SIGNAL(valueChanged(int)), this, SLOT(setFeatherRadius(int)));
72
73 QCheckBox * saveMaskFile = new QCheckBox(tr("Save"), this);
74
75 maskFileSelector = new QWidget(this);
76 QHBoxLayout * maskFileSelectorLayout = new QHBoxLayout(maskFileSelector);
77 maskFileSelectorLayout->setMargin(0);
78 maskFileEditor = new QLineEdit(maskFileSelector);
79 maskFileEditor->setMinimumWidth(200);
80 auto trHelp = [&] (const char * text) { return QCoreApplication::translate("Help", text); };
81 maskFileEditor->setToolTip(tr("You can use the following tokens:") + "\n" +
82 "- %if[n]: " + trHelp("Replaced by the base file name of image n. Image file names") + "\n" +
83 " " + trHelp("are first sorted in lexicographical order. Besides, n = -1 is the") + "\n" +
84 " " + trHelp("last image, n = -2 is the previous to the last image, and so on.") + "\n" +
85 "- %iF[n]: " + trHelp("Replaced by the base file name of image n without the extension.") + "\n" +
86 "- %id[n]: " + trHelp("Replaced by the directory name of image n.") + "\n" +
87 "- %in[n]: " + trHelp("Replaced by the numerical suffix of image n, if it exists.") + "\n" +
88 " " + trHelp("For instance, in IMG_1234.CR2, the numerical suffix would be 1234.") + "\n" +
89 "- %of " + trHelp("Replaced by the base file name of the output file.") + "\n" +
90 "- %od " + trHelp("Replaced by the directory name of the output file.") + "\n" +
91 "- %%: " + trHelp("Replaced by a single %.") + "\n");
92 maskFileEditor->setText(maskFileName);
93 QPushButton * showFileDialog = new QPushButton("...", maskFileSelector);
94 connect(showFileDialog, SIGNAL(clicked(bool)), this, SLOT(setMaskFileName()));
95 maskFileSelectorLayout->addWidget(maskFileEditor);
96 maskFileSelectorLayout->addWidget(showFileDialog);
97 connect(saveMaskFile, SIGNAL(stateChanged(int)), this, SLOT(setMaskFileSelectorEnabled(int)));
98 saveMaskFile->setChecked(saveMask);
99 maskFileSelector->setEnabled(saveMask);
100
101 QWidget * formWidget = new QWidget(this);
102 QFormLayout * formLayout = new QFormLayout(formWidget);
103 formLayout->addRow(tr("Bits per sample:"), bpsSelector);
104 formLayout->addRow(tr("Preview size:"), previewSelector);
105 formLayout->addRow(tr("Mask blur radius:"), radiusSelector);
106 formLayout->addRow(tr("Mask image:"), saveMaskFile);
107 formLayout->addRow("", maskFileSelector);
108 formWidget->setLayout(formLayout);
109 layout->addWidget(formWidget, 1);
110
111 saveOptions = new QCheckBox(tr("Save these options as the default values."));
112 layout->addWidget(saveOptions, 0, Qt::AlignLeft);
113
114 QWidget * buttons = new QWidget(this);
115 QHBoxLayout * buttonsLayout = new QHBoxLayout(buttons);
116 QPushButton * acceptButton = new QPushButton(tr("Accept"), this);
117 acceptButton->setDefault(true);
118 connect(acceptButton, SIGNAL(clicked(bool)), this, SLOT(accept()));
119 QPushButton * cancelButton = new QPushButton(tr("Cancel"), this);
120 connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject()));
121 buttonsLayout->addWidget(acceptButton);
122 buttonsLayout->addWidget(cancelButton);
123 layout->addWidget(buttons, 0, Qt::AlignHCenter);
124
125 setLayout(layout);
126 setWindowTitle(tr("DNG Properties"));
127 }
128
129
130 void DngPropertiesDialog::accept() {
131 saveMask = maskFileSelector->isEnabled();
132 maskFileName = QDir::toNativeSeparators(maskFileEditor->text()).toLocal8Bit().constData();
133 if (saveOptions->isChecked()) {
134 QSettings settings;
135 settings.setValue("bps", bps);
136 settings.setValue("previewSize", previewSize);
137 settings.setValue("saveMask", saveMask);
138 settings.setValue("maskFileName", maskFileName);
139 settings.setValue("featherRadius", featherRadius);
140 }
141 QDialog::accept();
142 }
143
144
145 void DngPropertiesDialog::loadDefaultOptions() {
146 QSettings settings;
147 bps = settings.value("bps", 16).toInt();
148 previewSize = settings.value("previewSize", 2).toInt();
149 saveMask = settings.value("saveMask", false).toBool();
150 maskFileName = settings.value("maskFileName", "%od/%of_mask.png").toString().toLocal8Bit().constData();
151 featherRadius = settings.value("featherRadius", 3).toInt();
152 }
153
154
155 void DngPropertiesDialog::setBps(int i) {
156 switch (i) {
157 case 0: bps = 16; break;
158 case 1: bps = 24; break;
159 case 2: bps = 32; break;
160 }
161 }
162
163
164 void DngPropertiesDialog::setPreviewSize(int i) {
165 previewSize = i;
166 }
167
168
169 void DngPropertiesDialog::setMaskFileName() {
170 QString file = QFileDialog::getSaveFileName(this, tr("Save DNG file"), "",
171 tr("PNG Images (*.png)"), NULL, QFileDialog::DontUseNativeDialog);
172 maskFileEditor->setText(file);
173 }
174
175
176 void DngPropertiesDialog::setMaskFileSelectorEnabled(int state) {
177 maskFileSelector->setEnabled(state == 2);
178 }
179
180
181 void DngPropertiesDialog::setFeatherRadius(int r) {
182 featherRadius = r;
183 }
184
185
186 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _DNGPROPERTIESDIALOG_H_
23 #define _DNGPROPERTIESDIALOG_H_
24
25 #include <QDialog>
26 #include <QLineEdit>
27 #include <QCheckBox>
28 #include "LoadSaveOptions.hpp"
29
30 namespace hdrmerge {
31
32 class DngPropertiesDialog : public QDialog, public SaveOptions {
33 public:
34 DngPropertiesDialog(QWidget * parent = 0, Qt::WindowFlags f = 0);
35
36 void closeEvent(QCloseEvent * event) { reject(); }
37
38 void setMaskFileName(const QString & name) {
39 maskFileEditor->setText(name);
40 }
41
42 public slots:
43 virtual void accept();
44
45 private slots:
46 void setBps(int i);
47 void setPreviewSize(int i);
48 void setMaskFileName();
49 void setMaskFileSelectorEnabled(int state);
50 void setFeatherRadius(int r);
51
52 private:
53 Q_OBJECT
54
55 void loadDefaultOptions();
56
57 QLineEdit * maskFileEditor;
58 QWidget * maskFileSelector;
59 QCheckBox * saveOptions;
60 };
61
62 } // namespace hdrmerge
63
64 #endif // _DNGPROPERTIESDIALOG_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "DraggableScrollArea.hpp"
23 #include <QScrollBar>
24 #include <QMouseEvent>
25 #include <QCursor>
26 using namespace hdrmerge;
27
28
29 void DraggableScrollArea::show(int x, int y) {
30 ensureVisible(x, y, 0, 0);
31 ensureVisible(x + viewport()->width(), y + viewport()->height(), 0, 0);
32 }
33
34
35 void DraggableScrollArea::toggleMoveViewport(bool toggle) {
36 moveViewport = toggle;
37 if (toggle)
38 widget()->setCursor(QCursor(Qt::CrossCursor));
39 }
40
41
42 void DraggableScrollArea::mousePressEvent(QMouseEvent * event) {
43 if (moveViewport && event->button() == Qt::LeftButton) {
44 mousePos = QCursor::pos();
45 widget()->setCursor(QCursor(Qt::OpenHandCursor));
46 dragging = true;
47 }
48 }
49
50
51 void DraggableScrollArea::mouseReleaseEvent(QMouseEvent * event) {
52 if (moveViewport && event->button() == Qt::LeftButton) {
53 widget()->setCursor(QCursor(Qt::CrossCursor));
54 dragging = false;
55 }
56 }
57
58
59 void DraggableScrollArea::mouseMoveEvent(QMouseEvent * event) {
60 if (dragging) {
61 QPoint delta = QCursor::pos() - mousePos;
62 // x5 panning speed
63 delta.setX(delta.x() * 5);
64 delta.setY(delta.y() * 5);
65 horizontalScrollBar()->setValue(horizontalScrollBar()->value() - delta.x());
66 verticalScrollBar()->setValue(verticalScrollBar()->value() - delta.y());
67 mousePos = QCursor::pos();
68 }
69 }
70
71 bool DraggableScrollArea::isDragging() {
72 return dragging;
73 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _DRAGGABLESCROLLAREA_H_
23 #define _DRAGGABLESCROLLAREA_H_
24
25 #include <QScrollArea>
26 #include <QMouseEvent>
27
28 namespace hdrmerge {
29
30 class DraggableScrollArea : public QScrollArea {
31 public:
32 DraggableScrollArea(QWidget * parent) : QScrollArea(parent), moveViewport(true), dragging(false) {}
33
34 public slots:
35 void show(int x, int y);
36 void toggleMoveViewport(bool toggle);
37 bool isDragging();
38
39 protected:
40 void mousePressEvent(QMouseEvent * event);
41 void mouseReleaseEvent(QMouseEvent * event);
42 void mouseMoveEvent(QMouseEvent * event);
43
44 private:
45 Q_OBJECT
46
47 QPoint mousePos;
48 QPoint lastScrollPos;
49 bool moveViewport;
50 bool dragging;
51 };
52
53 } // namespace hdrmerge
54
55 #endif // _DRAGGABLESCROLLAREA_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "EditableMask.hpp"
23 using namespace hdrmerge;
24
25
26 void EditableMask::startAction(bool add, int layer) {
27 editActions.erase(nextAction, editActions.end());
28 editActions.emplace_back();
29 nextAction = editActions.end();
30 editActions.back().oldLayer = add ? layer + 1 : layer;
31 editActions.back().newLayer = add ? layer : layer + 1;
32 }
33
34
35 void EditableMask::editPixels(int x, int y, size_t radius) {
36 EditAction & e = editActions.back();
37 traceCircle(x, y, radius, [&] (int col, int row, uint8_t & layer) {
38 if (layer == e.oldLayer && isLayerValidAt(e.newLayer, col, row)) {
39 e.points.push_back({col, row});
40 layer = e.newLayer;
41 }
42 });
43 }
44
45
46 QRect EditableMask::undo() {
47 QRect result;
48 if (nextAction != editActions.begin()) {
49 --nextAction;
50 result = modifyLayer(nextAction->points, nextAction->oldLayer);
51 }
52 return result;
53 }
54
55
56 QRect EditableMask::redo() {
57 QRect result;
58 if (nextAction != editActions.end()) {
59 result = modifyLayer(nextAction->points, nextAction->newLayer);
60 ++nextAction;
61 }
62 return result;
63 }
64
65
66 QRect EditableMask::modifyLayer(const std::list<QPoint> & points, int layer) {
67 if (points.empty()) {
68 return QRect(0, 0, 0, 0);
69 } else {
70 QRect a(points.front(), points.front());
71 for (auto p : points) {
72 operator()(p.x(), p.y()) = layer;
73 a = a.united(QRect(p, p));
74 }
75 return a;
76 }
77 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _EDITABLEMASK_HPP_
23 #define _EDITABLEMASK_HPP_
24
25 #include <cstdint>
26 #include <list>
27 #include <QRect>
28 #include "Array2D.hpp"
29
30 namespace hdrmerge {
31
32 class EditableMask : public Array2D<uint8_t> {
33 public:
34 EditableMask() : nextAction(editActions.end()) {}
35 void reset() {
36 editActions.clear();
37 nextAction = editActions.end();
38 }
39
40 void startAction(bool add, int layer);
41 void editPixels(int x, int y, size_t radius);
42 bool canUndo() const {
43 return nextAction != editActions.begin();
44 }
45 bool canRedo() const {
46 return nextAction != editActions.end();
47 }
48 QRect undo();
49 QRect redo();
50
51 private:
52 struct EditAction {
53 int oldLayer, newLayer;
54 std::list<QPoint> points;
55 };
56
57 std::list<EditAction> editActions;
58 std::list<EditAction>::iterator nextAction;
59
60 QRect modifyLayer(const std::list<QPoint> & points, int oldayer);
61 virtual bool isLayerValidAt(int layer, int x, int y) const = 0;
62 };
63
64 } // namespace hdrmerge
65
66 #endif // _EDITABLEMASK_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <exiv2/exiv2.hpp>
23 #include <iostream>
24 #include "ExifTransfer.hpp"
25 #include "Log.hpp"
26 using namespace hdrmerge;
27 using namespace Exiv2;
28 using namespace std;
29
30
31 class ExifTransfer {
32 public:
33 ExifTransfer(const QString & srcFile, const QString & dstFile,
34 const uint8_t * data, size_t dataSize)
35 : srcFile(srcFile), dstFile(dstFile), data(data), dataSize(dataSize) {}
36
37 void copyMetadata();
38
39 private:
40 QString srcFile, dstFile;
41 const uint8_t * data;
42 size_t dataSize;
43 Exiv2::Image::AutoPtr src, dst;
44
45 void copyXMP();
46 void copyIPTC();
47 void copyEXIF();
48 };
49
50
51 void hdrmerge::Exif::transfer(const QString & srcFile, const QString & dstFile,
52 const uint8_t * data, size_t dataSize) {
53 ExifTransfer exif(srcFile, dstFile, data, dataSize);
54 exif.copyMetadata();
55 }
56
57
58 void ExifTransfer::copyMetadata() {
59 try {
60 dst = Exiv2::ImageFactory::open(BasicIo::AutoPtr(new MemIo(data, dataSize)));
61 dst->readMetadata();
62 } catch (Exiv2::Error & e) {
63 std::cerr << "Exiv2 error: " << e.what() << std::endl;
64 return;
65 }
66 try {
67 src = Exiv2::ImageFactory::open(srcFile.toLocal8Bit().constData());
68 src->readMetadata();
69 copyXMP();
70 copyIPTC();
71 copyEXIF();
72 } catch (Exiv2::Error & e) {
73 std::cerr << "Exiv2 error: " << e.what() << std::endl;
74 // At least we have to set the SubImage1 file type to Primary Image
75 dst->exifData()["Exif.SubImage1.NewSubfileType"] = 0;
76 }
77 try {
78 dst->writeMetadata();
79 FileIo fileIo(dstFile.toLocal8Bit().constData());
80 fileIo.open("wb");
81 fileIo.write(dst->io());
82 fileIo.close();
83 } catch (Exiv2::Error & e) {
84 std::cerr << "Exiv2 error: " << e.what() << std::endl;
85 }
86 }
87
88
89 void ExifTransfer::copyXMP() {
90 const Exiv2::XmpData & srcXmp = src->xmpData();
91 Exiv2::XmpData & dstXmp = dst->xmpData();
92 for (const auto & datum : srcXmp) {
93 if (datum.groupName() != "tiff" && dstXmp.findKey(Exiv2::XmpKey(datum.key())) == dstXmp.end()) {
94 dstXmp.add(datum);
95 }
96 }
97 }
98
99
100 void ExifTransfer::copyIPTC() {
101 const Exiv2::IptcData & srcIptc = src->iptcData();
102 Exiv2::IptcData & dstIptc = dst->iptcData();
103 for (const auto & datum : srcIptc) {
104 if (dstIptc.findKey(Exiv2::IptcKey(datum.key())) == dstIptc.end()) {
105 dstIptc.add(datum);
106 }
107 }
108 }
109
110
111 static bool excludeExifDatum(const Exifdatum & datum) {
112 static const char * previewKeys[] {
113 "Exif.OlympusCs.PreviewImageStart",
114 "Exif.OlympusCs.PreviewImageLength",
115 "Exif.Thumbnail.JPEGInterchangeFormat",
116 "Exif.Thumbnail.JPEGInterchangeFormatLength",
117 "Exif.NikonPreview.JPEGInterchangeFormat",
118 "Exif.NikonPreview.JPEGInterchangeFormatLength",
119 "Exif.Pentax.PreviewOffset",
120 "Exif.Pentax.PreviewLength",
121 "Exif.PentaxDng.PreviewOffset",
122 "Exif.PentaxDng.PreviewLength",
123 "Exif.Minolta.ThumbnailOffset",
124 "Exif.Minolta.ThumbnailLength",
125 "Exif.SonyMinolta.ThumbnailOffset",
126 "Exif.SonyMinolta.ThumbnailLength",
127 "Exif.Olympus.ThumbnailImage",
128 "Exif.Olympus2.ThumbnailImage",
129 "Exif.Minolta.Thumbnail",
130 "Exif.PanasonicRaw.PreviewImage",
131 "Exif.SamsungPreview.JPEGInterchangeFormat",
132 "Exif.SamsungPreview.JPEGInterchangeFormatLength"
133 };
134 for (const char * pkey : previewKeys) {
135 if (datum.key() == pkey) {
136 return true;
137 }
138 }
139 return
140 datum.groupName().substr(0, 5) == "Thumb" ||
141 datum.groupName().substr(0, 8) == "SubThumb" ||
142 datum.groupName().substr(0, 5) == "Image" ||
143 datum.groupName().substr(0, 8) == "SubImage";
144 }
145
146
147 void ExifTransfer::copyEXIF() {
148 static const char * includeImageKeys[] = {
149 // Correct Make and Model, from the input files
150 // It is needed so that makernote tags are correctly copied
151 "Exif.Image.Make",
152 "Exif.Image.Model",
153 "Exif.Image.Artist",
154 "Exif.Image.Copyright",
155 "Exif.Image.DNGPrivateData",
156 // Opcodes generated by Adobe DNG converter
157 "Exif.SubImage1.OpcodeList1",
158 "Exif.SubImage1.OpcodeList2",
159 "Exif.SubImage1.OpcodeList3"
160 };
161
162 const Exiv2::ExifData & srcExif = src->exifData();
163 Exiv2::ExifData & dstExif = dst->exifData();
164
165 for (const char * keyName : includeImageKeys) {
166 auto iterator = srcExif.findKey(Exiv2::ExifKey(keyName));
167 if (iterator != srcExif.end()) {
168 dstExif[keyName] = *iterator;
169 }
170 }
171 // Now we set the SubImage1 file type to Primary Image
172 // Exiv2 wouldn't modify SubImage1 tags if it was set before
173 dstExif["Exif.SubImage1.NewSubfileType"] = 0u;
174
175 for (const auto & datum : srcExif) {
176 if (!excludeExifDatum(datum) && dstExif.findKey(Exiv2::ExifKey(datum.key())) == dstExif.end()) {
177 dstExif.add(datum);
178 }
179 }
180 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _EXIFTRANSFER_HPP_
23 #define _EXIFTRANSFER_HPP_
24
25 #include <QString>
26
27 namespace hdrmerge {
28
29 namespace Exif {
30 void transfer(const QString & srcFile, const QString & dstFile,
31 const uint8_t * data, size_t dataSize);
32 }
33
34 }
35
36 #endif // _EXIFTRANSFER_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2018 Jean-Christophe FRISCH
3 * natureh.510@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "FileSystem.hpp"
23 #include <QString>
24 #include <QStandardPaths>
25 #include <QStorageInfo>
26
27 using namespace std;
28
29 namespace hdrmerge {
30
31 QList<QUrl> getStdUrls(const QString additionalPath)
32 {
33
34 QList<QUrl> urls;
35 QStringList stdLocations;
36 if (!additionalPath.isEmpty()) {
37 urls.append(QUrl::fromLocalFile(additionalPath));
38 }
39
40 stdLocations = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::HomeLocation);
41 for (auto & i : stdLocations) {
42 urls.append(QUrl::fromLocalFile(i));
43 }
44 stdLocations = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::DesktopLocation);
45 for (auto & i : stdLocations) {
46 urls.append(QUrl::fromLocalFile(i));
47 }
48 stdLocations = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::DocumentsLocation);
49 for (auto & i : stdLocations) {
50 urls.append(QUrl::fromLocalFile(i));
51 }
52 stdLocations = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::PicturesLocation);
53 for (auto & i : stdLocations) {
54 urls.append(QUrl::fromLocalFile(i));
55 }
56
57 QList<QStorageInfo> volumes = QStorageInfo::mountedVolumes();
58 for (auto & volume : volumes) {
59 if (volume.isValid() && volume.isReady() && !volume.isReadOnly()) {
60 urls.append(QUrl::fromLocalFile(volume.rootPath()));
61 }
62 }
63
64 return urls;
65 }
66
67 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2018 Jean-Christophe FRISCH
3 * natureh.510@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _FILESYSTEM_HPP_
23 #define _FILESYSTEM_HPP_
24
25 #include <QUrl>
26 #include <QList>
27
28 namespace hdrmerge {
29
30 // additionalPath: a non standard path that will be inserted in the url list
31 QList<QUrl> getStdUrls(const QString additionalPath = "");
32
33 }
34
35 #endif
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _HISTOGRAM_H_
23 #define _HISTOGRAM_H_
24
25 #include <cmath>
26 #include <cstdint>
27 #include <vector>
28
29 namespace hdrmerge {
30
31 class Histogram {
32 public:
33 Histogram() : bins(65536), numSamples(0) {}
34 template <typename Iterator> Histogram(Iterator start, Iterator end) : Histogram() {
35 while (start != end) {
36 addValue((uint16_t)*start++);
37 }
38 }
39
40 void addValue(uint16_t v) {
41 ++bins[v];
42 ++numSamples;
43 }
44 std::size_t getNumSamples() const {
45 return numSamples;
46 }
47 uint16_t getPercentile(double frac) const {
48 std::size_t current = bins[0], limit = std::floor(numSamples * frac);
49 uint16_t result = 0;
50 while (current < limit)
51 current += bins[++result];
52 return result;
53 }
54 double getFraction(uint16_t value) const {
55 double result = 0.0;
56 for (uint16_t i = 0; i <= value; ++i) {
57 result += bins[i];
58 }
59 return result / numSamples;
60 }
61
62 private:
63 std::vector<unsigned int> bins; // 65536 elements
64 std::size_t numSamples;
65 };
66
67 }
68
69 #endif // _HISTOGRAM_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "Image.hpp"
23 #include "Bitmap.hpp"
24 #include "Histogram.hpp"
25 #include "Log.hpp"
26 #include "RawParameters.hpp"
27 using namespace std;
28 using namespace hdrmerge;
29
30
31 void Image::ResponseFunction::setLinear(double slope) {
32 threshold = 65535;
33 linear = slope;
34 alglib::real_1d_array x = "[0.0, 0.0]";
35 alglib::real_1d_array f = "[0.0, 65535.0]";
36 x[1] = 65535.0 / linear;
37 alglib::spline1dbuildlinear(x, f, 2, nonLinear);
38 }
39
40
41 void Image::buildImage(uint16_t * rawImage, const RawParameters & params) {
42 resize(params.width, params.height);
43 size_t size = width*height;
44 brightness = 0.0;
45 max = 0;
46 for (size_t y = 0, ry = params.topMargin; y < height; ++y, ++ry) {
47 for (size_t x = 0, rx = params.leftMargin; x < width; ++x, ++rx) {
48 uint16_t v = rawImage[ry*params.rawWidth + rx];
49 (*this)(x, y) = v;
50 brightness += v;
51 if (v > max) max = v;
52 }
53 }
54 brightness /= size;
55 response.setLinear(params.max == 0 ? 1.0 : 65535.0 / params.max);
56 subtractBlack(params);
57 }
58
59
60 Image & Image::operator=(Image && move) {
61 *static_cast<Array2D<uint16_t> *>(this) = (Array2D<uint16_t> &&)std::move(move);
62 filename = move.filename;
63 scaled.swap(move.scaled);
64 satThreshold = move.satThreshold;
65 max = move.max;
66 brightness = move.brightness;
67 response = move.response;
68 halfLightPercent = move.halfLightPercent;
69 return *this;
70 }
71
72
73 void Image::setSaturationThreshold(uint16_t sat) {
74 satThreshold = sat;
75 response.threshold = 0.9*sat;
76 }
77
78
79 void Image::subtractBlack(const RawParameters & params) {
80 if (params.hasBlack()) {
81 for (size_t y = 0, pos = 0; y < height; ++y) {
82 for (size_t x = 0; x < width; ++x, ++pos) {
83 if ((*this)[pos] > params.blackAt(x, y)) {
84 (*this)[pos] -= params.blackAt(x, y);
85 } else {
86 (*this)[pos] = 0;
87 }
88 }
89 }
90 }
91 }
92
93
94 double Image::getRelativeExposure() const {
95 return response.linear;
96 }
97
98
99 void Image::computeResponseFunction(const Image & r) {
100 int reldx = dx - std::max(dx, r.dx);
101 int relrdx = r.dx - std::max(dx, r.dx);
102 int w = width + reldx + relrdx;
103 int reldy = dy - std::max(dy, r.dy);
104 int relrdy = r.dy - std::max(dy, r.dy);
105 int h = height + reldy + relrdy;
106 uint16_t * usePixels = &data[-reldy*width - reldx];
107 const uint16_t * rUsePixels = &r.data[-relrdy*width - relrdx];
108
109 // Get average relative values between this image and the last one
110 std::vector<std::pair<int, double>> histogram(max + 1);
111 for (auto & i : histogram) i = { 0, 0.0 };
112 #pragma omp parallel
113 {
114 // use one histogram per thread
115 std::vector<std::pair<int, double>> histogramThr(max + 1);
116 for (auto & i : histogramThr) i = { 0, 0.0 };
117 #pragma omp for nowait
118 for (int y = 0; y < h; ++y) {
119 for (int x = 0; x < w; ++x) {
120 int pos = y * width + x;
121 uint16_t v = usePixels[pos];
122 uint16_t nv = rUsePixels[pos];
123 if (v >= nv && v < satThreshold) {
124 histogramThr[v].first++;
125 histogramThr[v].second += r.response(nv);
126 }
127 }
128 }
129 #pragma omp critical
130 {
131 // join per thread histogram to global one
132 for(int i=0;i<max+1;i++) {
133 histogram[i].first += histogramThr[i].first;
134 histogram[i].second += histogramThr[i].second;
135 }
136 }
137 }
138 alglib::real_1d_array values, adjValues;
139 values.setlength(max);
140 adjValues.setlength(max);
141 values[0] = 0;
142 adjValues[0] = 0;
143 int i = 1;
144 for (int v = max - 1; v >= max*0.75; --v) {
145 if (histogram[v].first > 2) {
146 values[i] = v;
147 adjValues[i] = histogram[v].second / histogram[v].first;
148 ++i;
149 }
150 }
151 if (i >= max/8) {
152 alglib::ae_int_t info;
153 alglib::spline1dfitreport rep;
154 alglib::spline1dfitpenalized(values, adjValues, i, 200, 3, info, response.nonLinear, rep);
155 response.linear = alglib::spline1dcalc(response.nonLinear, response.threshold) / response.threshold;
156 } else {
157 response.threshold = 65535;
158 // Fallback method for dark images:
159 // Minimize square error between images:
160 // min. C(n) = sum(n*f(x) - g(x))^2 -> n = sum(f(x)*g(x)) / sum(f(x)^2)
161 double numerator = 0, denom = 0;
162 for (int y = 0; y < h; ++y) {
163 for (int x = 0; x < w; ++x) {
164 int pos = y * width + x;
165 double v = usePixels[pos];
166 double nv = rUsePixels[pos];
167 if (v >= nv && v < satThreshold) {
168 numerator += v * r.response(nv);
169 denom += v * v;
170 }
171 }
172 }
173 response.linear = numerator / denom;
174 }
175 }
176
177
178 size_t Image::alignWith(const Image & r) {
179 dx = dy = 0;
180 const double tolerance = 1.0/16;
181 Histogram histFull(begin(), end());
182 double halfLightPercent = histFull.getFraction(satThreshold) / 2.0;
183 size_t totalError = 0;
184 for (int s = scaleSteps - 1; s >= 0; --s) {
185 size_t curWidth = width >> (s + 1);
186 size_t curHeight = height >> (s + 1);
187 size_t minError = curWidth*curHeight;
188 Histogram hist1(r.scaled[s].begin(), r.scaled[s].end());
189 Histogram hist2(scaled[s].begin(), scaled[s].end());
190 uint16_t mth1 = hist1.getPercentile(halfLightPercent);
191 uint16_t mth2 = hist2.getPercentile(halfLightPercent);
192 uint16_t tolPixels1 = (uint16_t)std::floor(mth1*tolerance);
193 uint16_t tolPixels2 = (uint16_t)std::floor(mth2*tolerance);
194 Bitmap mtb1(curWidth, curHeight), mtb2(curWidth, curHeight),
195 excl1(curWidth, curHeight), excl2(curWidth, curHeight);
196 mtb1.mtb(r.scaled[s].begin(), mth1);
197 mtb2.mtb(scaled[s].begin(), mth2);
198 excl1.exclusion(r.scaled[s].begin(), mth1, tolPixels1);
199 excl2.exclusion(scaled[s].begin(), mth2, tolPixels2);
200 Bitmap shiftMtb(curWidth, curHeight), shiftExcl(curWidth, curHeight);
201 int curDx = dx, curDy = dy;
202 for (int i = -1; i <= 1; ++i) {
203 for (int j = -1; j <= 1; ++j) {
204 shiftMtb.shift(mtb2, curDx + i, curDy + j);
205 shiftExcl.shift(excl2, curDx + i, curDy + j);
206 shiftMtb.bitwiseXor(mtb1);
207 shiftMtb.bitwiseAnd(excl1);
208 shiftMtb.bitwiseAnd(shiftExcl);
209 size_t err = shiftMtb.count();
210 if (err < minError) {
211 dx = curDx + i;
212 dy = curDy + j;
213 minError = err;
214 }
215 }
216 }
217 dx <<= 1;
218 dy <<= 1;
219 totalError += minError;
220 }
221 return totalError;
222 }
223
224
225 void Image::preScale() {
226 size_t curWidth = width;
227 size_t curHeight = height;
228 Array2D<uint16_t> * r2 = this;
229
230 scaled.reset(new Array2D<uint16_t>[scaleSteps]);
231 for (int s = 0; s < scaleSteps; ++s) {
232 scaled[s].resize(curWidth >>= 1, curHeight >>= 1);
233 for (size_t y = 0, prevY = 0; y < curHeight; ++y, prevY += 2) {
234 for (size_t x = 0, prevX = 0; x < curWidth; ++x, prevX += 2) {
235 uint32_t value1 = (*r2)(prevX, prevY),
236 value2 = (*r2)(prevX + 1, prevY),
237 value3 = (*r2)(prevX, prevY + 1),
238 value4 = (*r2)(prevX + 1, prevY + 1);
239 scaled[s](x, y) = (value1 + value2 + value3 + value4) >> 2;
240 }
241 }
242 r2 = &scaled[s];
243 }
244 }
245
246
247 uint16_t Image::getMaxAround(size_t x, size_t y) const {
248 uint16_t result = 0;
249 if ((int)y > dy) {
250 if ((int)x > dx) result = std::max(result, (*this)(x - 1, y - 1));
251 result = std::max(result, (*this)(x, y - 1));
252 if (x < width + dx - 1) result = std::max(result, (*this)(x + 1, y - 1));
253 }
254 if ((int)x > dx) result = std::max(result, (*this)(x - 1, y));
255 result = std::max(result, (*this)(x, y));
256 if (x < width + dx - 1) result = std::max(result, (*this)(x + 1, y));
257 if (y < height + dy - 1) {
258 if ((int)x > dx) result = std::max(result, (*this)(x - 1, y + 1));
259 result = std::max(result, (*this)(x, y + 1));
260 if (x < width + dx - 1) result = std::max(result, (*this)(x + 1, y + 1));
261 }
262 return result;
263 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _IMAGE_H_
23 #define _IMAGE_H_
24
25 #include <memory>
26
27 #include <QString>
28
29 #include <interpolation.h>
30
31 #include "Array2D.hpp"
32
33
34 namespace hdrmerge {
35
36 class RawParameters;
37
38 class Image : public Array2D<uint16_t> {
39 public:
40 static const int scaleSteps = 6;
41
42 Image() : Array2D<uint16_t>() {}
43 Image(uint16_t * rawImage, const RawParameters & params, const QString& _filename) :
44 filename(_filename)
45 {
46 buildImage(rawImage, params);
47 }
48 Image(const Image & copy) = delete;
49 Image & operator=(const Image & copy) = delete;
50 Image(Image && move) {
51 (*this) = std::move(move);
52 }
53 Image & operator=(Image && move);
54
55 const QString& getFilename() const
56 {
57 return filename;
58 }
59
60 bool good() const {
61 return width > 0;
62 }
63 double exposureAt(size_t x, size_t y) const {
64 return response((*this)(x, y));
65 }
66 uint16_t getMaxAround(size_t x, size_t y) const;
67 bool isSaturated(uint16_t v) const {
68 return v >= satThreshold;
69 }
70 bool isSaturated(size_t x, size_t y) const {
71 return isSaturated((*this)(x, y));
72 }
73 bool isSaturatedAround(size_t x, size_t y) const {
74 return isSaturated(getMaxAround(x, y));
75 }
76 double getRelativeExposure() const;
77 size_t alignWith(const Image & r);
78 void preScale();
79 void releaseAlignData() {
80 scaled.reset();
81 }
82 void computeResponseFunction(const Image & nextImage);
83 bool operator<(const Image & r) {
84 return brightness > r.brightness;
85 }
86 void setSaturationThreshold(uint16_t sat);
87 uint16_t getMax() const
88 {
89 return max;
90 }
91
92 private:
93 struct ResponseFunction {
94 uint16_t threshold;
95 double linear;
96 alglib::spline1dinterpolant nonLinear;
97 double operator()(uint16_t v) const {
98 return v <= threshold ? v * linear : alglib::spline1dcalc(nonLinear, v);
99 }
100 void setLinear(double slope);
101 };
102
103 QString filename;
104
105 std::unique_ptr<Array2D<uint16_t>[]> scaled;
106 uint16_t satThreshold, max;
107 double brightness;
108 ResponseFunction response;
109 double halfLightPercent;
110
111 void subtractBlack(const RawParameters & params);
112 void buildImage(uint16_t * rawImage, const RawParameters & params);
113 };
114
115 } // namespace hdrmerge
116
117 #endif // _IMAGE_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <cstdlib>
23 #include <algorithm>
24 #include <QImage>
25 #include <QString>
26 #include <QRegExp>
27 #include <QFileInfo>
28 #include <libraw.h>
29 #include "ImageIO.hpp"
30 #include "DngFloatWriter.hpp"
31 #include "Log.hpp"
32 using namespace std;
33 using namespace hdrmerge;
34
35 Image ImageIO::loadRawImage(const QString& filename, RawParameters & rawParameters, int shot_select) {
36 std::unique_ptr<LibRaw> rawProcessor(new LibRaw);
37 auto & d = rawProcessor->imgdata;
38 d.params.shot_select = shot_select;
39 if (rawProcessor->open_file(rawParameters.fileName.toLocal8Bit().constData()) == LIBRAW_SUCCESS) {
40 libraw_decoder_info_t decoder_info;
41 rawProcessor->get_decoder_info(&decoder_info);
42 if (d.idata.filters <= 1000 && d.idata.filters != 9) {
43 Log::msg(Log::DEBUG, "Unsupported filter array (", d.idata.filters, ").");
44 #ifdef LIBRAW_DECODER_FLATFIELD
45 } else if (!decoder_info.decoder_flags & LIBRAW_DECODER_FLATFIELD) {
46 Log::msg(Log::DEBUG, "LibRaw decoder is not flatfield (", ios::hex, decoder_info.decoder_flags, ").");
47 #endif
48 } else if (rawProcessor->unpack() != LIBRAW_SUCCESS) {
49 Log::msg(Log::DEBUG, "LibRaw::unpack() failed.");
50 } else {
51 rawParameters.fromLibRaw(*(rawProcessor.get()));
52 }
53 } else {
54 Log::msg(Log::DEBUG, "LibRaw::open_file(", rawParameters.fileName, ") failed.");
55 }
56 return Image(d.rawdata.raw_image, rawParameters, filename);
57 }
58
59 int ImageIO::getFrameCount(RawParameters & rawParameters) {
60 std::unique_ptr<LibRaw> rawProcessor(new LibRaw);
61 auto & d = rawProcessor->imgdata;
62 if (rawProcessor->open_file(rawParameters.fileName.toLocal8Bit().constData()) == LIBRAW_SUCCESS) {
63 Log::msg(Log::DEBUG, "Number of frames : ", d.idata.raw_count);
64 return d.idata.raw_count;
65 } else {
66 return 0;
67 }
68
69 }
70
71 ImageIO::QDateInterval ImageIO::getImageCreationInterval(const QString & fileName) {
72 std::unique_ptr<LibRaw> rawProcessor(new LibRaw);
73 QDateInterval result;
74 if (rawProcessor->open_file(fileName.toLocal8Bit().constData()) == LIBRAW_SUCCESS) {
75 result.end = QDateTime::fromTime_t(rawProcessor->imgdata.other.timestamp);
76 result.start = result.end.addMSecs(-rawProcessor->imgdata.other.shutter * 1000.0);
77 }
78 return result;
79 }
80
81
82 int ImageIO::load(const LoadOptions & options, ProgressIndicator & progress) {
83 int numImages = options.fileNames.size();
84 int step;
85 int p = 0;
86 int error = 0, failedImage = 0;
87 stack.clear();
88 rawParameters.clear();
89 {
90 Timer t("Load files");
91 if(numImages == 1) { // check for multiframe raw files
92 const QString name = options.fileNames[0];
93 unique_ptr<RawParameters> params(new RawParameters(name));
94 int frameCount = getFrameCount(*params);
95 step = 100 / (frameCount + 1);
96 p = 0;
97 if(frameCount > 0 && frameCount <= 4) {
98 // framecount == 1 => create a dng from a single file with a single frame
99 // framecount == 2 => create a merged dng from a fuji exr file
100 // framecount == 3 => create a merged dng from a pentax hdr file
101 for (int i = 0; i < frameCount; ++i) {
102 progress.advance(p, "Loading %1", name.toLocal8Bit().constData());
103 p += step;
104 unique_ptr<RawParameters> params(new RawParameters(name));
105
106 Image image = loadRawImage(name, *params, i);
107 if (!image.good()) {
108 error = 1;
109 failedImage = i;
110 break;
111 } else if (stack.size() && !params->isSameFormat(*rawParameters.front())) {
112 error = 2;
113 failedImage = i;
114 break;
115 } else {
116 int pos = stack.addImage(std::move(image));
117 rawParameters.emplace_back(std::move(params));
118 for (int j = rawParameters.size() - 1; j > pos; --j)
119 rawParameters[j - 1].swap(rawParameters[j]);
120 }
121 }
122 }
123 } else {
124 step = 100 / (numImages + 1);
125 for (int i = 0; i < numImages; ++i) {
126 const QString name = options.fileNames[i];
127 progress.advance(p, "Loading %1", name.toLocal8Bit().constData());
128 p += step;
129 unique_ptr<RawParameters> params(new RawParameters(name));
130
131 Image image = loadRawImage(name, *params);
132 if (!image.good()) {
133 error = 1;
134 failedImage = i;
135 break;
136 } else if (stack.size() && !params->isSameFormat(*rawParameters.front())) {
137 error = 2;
138 failedImage = i;
139 break;
140 } else {
141 int pos = stack.addImage(std::move(image));
142 rawParameters.emplace_back(std::move(params));
143 for (int j = rawParameters.size() - 1; j > pos; --j)
144 rawParameters[j - 1].swap(rawParameters[j]);
145 }
146 }
147 }
148 }
149 if (error) {
150 stack.clear();
151 rawParameters.clear();
152 return (failedImage << 1) + error - 1;
153 }
154
155 progress.advance(p, "Processing stack");
156
157 RawParameters & params = *rawParameters.front();
158 stack.setFlip(params.flip);
159 if(options.useCustomWl)
160 // Use custom white level, but only if it's not greater than the value provided by libraw
161 params.max = std::min(params.max, options.customWl);
162 stack.calculateSaturationLevel(params, options.useCustomWl);
163 if (options.align && params.canAlign()) {
164 stack.align();
165 if (options.crop) {
166 stack.crop();
167 }
168 }
169 stack.computeResponseFunctions();
170 stack.generateMask();
171 progress.advance(100, "Done loading!");
172 return numImages << 1;
173 }
174
175
176 void ImageIO::save(const SaveOptions & options, ProgressIndicator & progress) {
177 string cropped = stack.isCropped() ? " cropped" : "";
178 Log::msg(2, "Writing ", options.fileName, ", ", options.bps, "-bit, ", stack.getWidth(), 'x', stack.getHeight(), cropped);
179
180 progress.advance(0, "Rendering image");
181 RawParameters params = *rawParameters.back();
182 params.width = stack.getWidth();
183 params.height = stack.getHeight();
184 params.adjustWhite(stack.getImage(stack.size() - 1));
185 Array2D<float> composedImage = stack.compose(params, options.featherRadius);
186
187 progress.advance(33, "Rendering preview");
188 QImage preview = renderPreview(composedImage, params, stack.getMaxExposure(), options.previewSize <= 1);
189
190 progress.advance(66, "Writing output");
191 DngFloatWriter writer;
192 writer.setBitsPerSample(options.bps);
193 writer.setPreviewWidth((options.previewSize * stack.getWidth()) / 2);
194 writer.setPreview(preview);
195 writer.write(std::move(composedImage), params, options.fileName);
196 progress.advance(100, "Done writing!");
197
198 if (options.saveMask) {
199 QString name = replaceArguments(options.maskFileName, options.fileName);
200 writeMaskImage(name);
201 }
202 }
203
204
205 void ImageIO::writeMaskImage(const QString & maskFile) {
206 Log::debug("Saving mask to ", maskFile);
207 EditableMask & mask = stack.getMask();
208 QImage maskImage(mask.getWidth(), mask.getHeight(), QImage::Format_Indexed8);
209 int numColors = stack.size() - 1;
210 for (int c = 0; c < numColors; ++c) {
211 int gray = (256 * c) / numColors;
212 maskImage.setColor(c, qRgb(gray, gray, gray));
213 }
214 maskImage.setColor(numColors, qRgb(255, 255, 255));
215 for (size_t y = 0, pos = 0; y < mask.getHeight(); ++y) {
216 for (size_t x = 0; x < mask.getWidth(); ++x, ++pos) {
217 maskImage.setPixel(x, y, mask[pos]);
218 }
219 }
220 if (!maskImage.save(maskFile)) {
221 Log::progress("Cannot save mask image to ", maskFile);
222 }
223 }
224
225
226 static void prepareRawBuffer(LibRaw & rawProcessor) {
227 rawProcessor.imgdata.progress_flags |= LIBRAW_PROGRESS_LOAD_RAW;
228 auto & i = rawProcessor.imgdata;
229 auto & r = i.rawdata;
230 auto & s = i.sizes;
231 r.color4_image = nullptr;
232 r.color3_image = nullptr;
233 size_t numPixels = s.raw_width * (s.raw_height + 7);
234 r.raw_alloc = std::malloc(numPixels * sizeof(ushort));
235 r.raw_image = (ushort*) r.raw_alloc;
236 s.raw_pitch = s.raw_width*2;
237 copy_n(&i.color, 1, &r.color);
238 copy_n(&i.sizes, 1, &r.sizes);
239 copy_n(&i.idata, 1, &r.iparams);
240 }
241
242
243 QImage ImageIO::renderPreview(const Array2D<float> & rawData, const RawParameters & params, float expShift, bool halfSize) {
244 Timer t("Render preview");
245 std::unique_ptr<LibRaw> rawProcessor(new LibRaw);
246 auto & d = rawProcessor->imgdata;
247 d.params.user_sat = 65535;
248 d.params.user_black = 0;
249 for (int c = 0; c < 4; ++c) {
250 d.params.user_cblack[c] = 0;
251 }
252 d.params.highlight = 2;
253 d.params.user_qual = 3;
254 d.params.med_passes = 0;
255 copy_n(params.camMul, 4, d.params.user_mul);
256 d.params.user_flip = 0;
257 d.params.exp_correc = 1;
258 d.params.exp_shift = expShift;
259 d.params.exp_preser = 1.0;
260 d.params.half_size = halfSize ? 1 : 0; // much faster, will be used for preview size 'half' or 'none'
261 if (rawProcessor->open_file(params.fileName.toLocal8Bit().constData()) == LIBRAW_SUCCESS) {
262 // && rawProcessor.unpack() == LIBRAW_SUCCESS) {
263 prepareRawBuffer(*(rawProcessor.get()));
264 // Assume the other sizes are the same as in the raw parameters
265 d.sizes.width = params.width;
266 d.sizes.height = params.height;
267 float scale = d.params.user_sat / (float)(params.max - params.black);
268 for (size_t y = 0; y < params.rawHeight; ++y) {
269 for (size_t x = 0; x < params.rawWidth; ++x) {
270 size_t pos = y*params.rawWidth + x;
271 int v = (rawData[pos] - params.blackAt(x - params.leftMargin, y - params.topMargin)) * scale;
272 if (v < 0) v = 0;
273 else if (v > 65535) v = 65535;
274 d.rawdata.raw_image[pos] = v;
275 }
276 }
277 rawProcessor->dcraw_process();
278 libraw_processed_image_t * image = rawProcessor->dcraw_make_mem_image();
279 if (image == nullptr) {
280 Log::msg(2, "dcraw_make_mem_image() returned NULL");
281 } else {
282 QImage interpolated(image->width, image->height, QImage::Format_RGB32);
283 if (interpolated.isNull()) return QImage();
284 for (int y = 0; y < image->height; ++y) {
285 QRgb* scanline = (QRgb*)interpolated.scanLine(y);
286 int pos = (y*image->width)*3;
287 for (int x = 0; x < image->width; ++x) {
288 int r = image->data[pos++], g = image->data[pos++], b = image->data[pos++];
289 scanline[x] = qRgb(r, g, b);
290 }
291 }
292 LibRaw::dcraw_clear_mem(image);
293 // The result may be some pixels bigger than the original...
294 return interpolated.copy(0, 0, params.width/(halfSize ? 2 : 1 ), params.height/(halfSize ? 2 : 1 ));
295 }
296 }
297 return QImage();
298 }
299
300
301 class FileNameManipulator {
302 public:
303 FileNameManipulator(const vector<unique_ptr<RawParameters>> & paramList) {
304 names.reserve(paramList.size());
305 for (auto & rp : paramList) {
306 names.push_back(rp->fileName);
307 }
308 sort(names.begin(), names.end());
309 }
310
311 QString getInputBaseName(int i) {
312 i = adjustIndex(i);
313 if (i == -1) return QString();
314 else return getBaseName(names[i]);
315 }
316
317 QString getInputBaseNameNoExt(int i) {
318 QString name = getInputBaseName(i);
319 return name.mid(0, name.lastIndexOf('.'));
320 }
321
322 QString getInputDirName(int i) {
323 i = adjustIndex(i);
324 if (i == -1) return QString();
325 else return getDirName(names[i]);
326 }
327
328 QString getInputNumberSuffix(int i) {
329 QString name = getInputBaseNameNoExt(i);
330 int pos = name.length() - 1;
331 while (pos >= 0 && name[pos] >= '0' && name[pos] <= '9') pos--;
332 return name.mid(pos + 1);
333 }
334
335 static QString getBaseName(const QString & name) {
336 return QFileInfo(name).fileName();
337 }
338
339 static QString getDirName(const QString & name) {
340 return QFileInfo(name).canonicalPath();
341 }
342
343 private:
344 vector<QString> names;
345 int adjustIndex(int i) {
346 if (i < 0)
347 i = names.size() + i;
348 return i < 0 || i >= (int)names.size() ? -1 : i;
349 }
350 };
351
352
353 QString ImageIO::buildOutputFileName() const {
354 if (rawParameters.size() > 1)
355 return replaceArguments("%id[-1]/%iF[0]-%in[-1].dng", "");
356 else
357 return replaceArguments("%id[-1]/%iF[0].dng", "");
358 }
359
360
361 QString ImageIO::getInputPath() const {
362 return FileNameManipulator::getDirName(rawParameters[0]->fileName);
363 }
364
365
366 QString ImageIO::replaceArguments(const QString & pattern, const QString & outFileName) const {
367 QString result(pattern);
368 QRegExp re;
369 if (outFileName == "") {
370 re = QRegExp("%(?:i[fFdn]\\[(-?[0-9]+)\\]|%)");
371 } else {
372 re = QRegExp("%(?:o[fd]|i[fFdn]\\[(-?[0-9]+)\\]|%)");
373 }
374 int index = 0;
375 FileNameManipulator fnm(rawParameters);
376 while ((index = result.indexOf(re, index)) != -1) {
377 // What was matched?
378 QString token = re.cap();
379 if (token[1] == '%') {
380 result.replace(index, 2, '%');
381 } else if (token[1] == 'o') {
382 if (token[2] == 'f') {
383 result.replace(index, 3, fnm.getBaseName(outFileName));
384 } else {
385 result.replace(index, 3, fnm.getDirName(outFileName));
386 }
387 } else { // 'i'
388 int imageIndex = re.cap(1).toInt();
389 int length = re.cap(1).length() + 5;
390 if (token[2] == 'f') {
391 result.replace(index, length, fnm.getInputBaseName(imageIndex));
392 } else if (token[2] == 'F') {
393 result.replace(index, length, fnm.getInputBaseNameNoExt(imageIndex));
394 } else if (token[2] == 'd') {
395 result.replace(index, length, fnm.getInputDirName(imageIndex));
396 } else { // 'n'
397 result.replace(index, length, fnm.getInputNumberSuffix(imageIndex));
398 }
399 }
400 index++;
401 }
402 return result;
403 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _IMAGEIO_H_
23 #define _IMAGEIO_H_
24
25 #include <vector>
26 #include <QImage>
27 #include <QDateTime>
28 #include <QString>
29 #include "ImageStack.hpp"
30 #include "ProgressIndicator.hpp"
31 #include "LoadSaveOptions.hpp"
32 #include "RawParameters.hpp"
33
34 namespace hdrmerge {
35
36 class ImageIO {
37 public:
38 ImageIO() {}
39
40 int load(const LoadOptions & options, ProgressIndicator & progress);
41 void save(const SaveOptions & options, ProgressIndicator & progress);
42
43 const ImageStack & getImageStack() const {
44 return stack;
45 }
46 ImageStack & getImageStack() {
47 return stack;
48 }
49
50 QString buildOutputFileName() const;
51 QString getInputPath() const;
52 QString replaceArguments(const QString & pattern, const QString & outFileName) const;
53 static int getFrameCount(RawParameters & rawParameters) ;
54 static Image loadRawImage(const QString& filename, RawParameters & rawParameters, int shot_select = 0);
55 static QImage renderPreview(const Array2D<float> & rawData, const RawParameters & rawParameters, float expShift, bool halfsize = false);
56
57 struct QDateInterval {
58 QDateTime start, end;
59 bool operator<(const QDateInterval & r) const {
60 return start < r.start;
61 }
62 double difference(const QDateInterval & r) const {
63 return end.msecsTo(r.start) / 1000.0;
64 }
65 };
66 static QDateInterval getImageCreationInterval(const QString & fileName);
67
68 private:
69 ImageStack stack;
70 std::vector<std::unique_ptr<RawParameters>> rawParameters;
71
72 void writeMaskImage(const QString & maskFile);
73 };
74
75 } // namespace hdrmerge
76
77 #endif // _IMAGEIO_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <algorithm>
23
24 #include "BoxBlur.hpp"
25 #include "ImageStack.hpp"
26 #include "Log.hpp"
27 #include "RawParameters.hpp"
28
29 #ifdef __SSE2__
30 #include <x86intrin.h>
31 #endif
32
33 using namespace std;
34 using namespace hdrmerge;
35
36
37 int ImageStack::addImage(Image && i) {
38 if (images.empty()) {
39 width = i.getWidth();
40 height = i.getHeight();
41 }
42 images.push_back(std::move(i));
43 int n = images.size() - 1;
44 while (n > 0 && images[n] < images[n - 1]) {
45 std::swap(images[n], images[n - 1]);
46 --n;
47 }
48 return n;
49 }
50
51
52 void ImageStack::calculateSaturationLevel(const RawParameters & params, bool useCustomWl) {
53 // Calculate max value of brightest image and assume it is saturated
54 Image& brightest = images.front();
55
56 std::vector<std::vector<size_t>> histograms(4, std::vector<size_t>(brightest.getMax() + 1));
57
58 #pragma omp parallel
59 {
60 std::vector<std::vector<size_t>> histogramsThr(4, std::vector<size_t>(brightest.getMax() + 1));
61 #pragma omp for schedule(dynamic,16) nowait
62 for (size_t y = 0; y < height; ++y) {
63 // get the color codes from x = 0 to 5, works for bayer and xtrans
64 uint16_t fcrow[6];
65 for (size_t i = 0; i < 6; ++i) {
66 fcrow[i] = params.FC(i, y);
67 }
68 size_t x = 0;
69 for (; x < width - 5; x+=6) {
70 for(size_t j = 0; j < 6; ++j) {
71 uint16_t v = brightest(x + j, y);
72 ++histogramsThr[fcrow[j]][v];
73 }
74 }
75 // remaining pixels
76 for (size_t j = 0; x < width; ++x, ++j) {
77 uint16_t v = brightest(x, y);
78 ++histogramsThr[fcrow[j]][v];
79 }
80 }
81 #pragma omp critical
82 {
83 for (int c = 0; c < 4; ++c) {
84 for (std::vector<size_t>::size_type i = 0; i < histograms[c].size(); ++i) {
85 histograms[c][i] += histogramsThr[c][i];
86 }
87 }
88 }
89 }
90
91 const size_t threshold = width * height / 10000;
92
93 uint16_t maxPerColor[4] = {0, 0, 0, 0};
94
95 for (int c = 0; c < 4; ++c) {
96 for (int i = histograms[c].size() - 1; i >= 0; --i) {
97 const size_t v = histograms[c][i];
98 if (v > threshold) {
99 maxPerColor[c] = i;
100 break;
101 }
102 }
103 }
104
105
106 uint16_t maxPerColors = std::max(maxPerColor[0], std::max(maxPerColor[1],std::max(maxPerColor[2], maxPerColor[3])));
107 satThreshold = params.max == 0 ? maxPerColors : params.max;
108
109 if(maxPerColors > 0) {
110 satThreshold = std::min(satThreshold, maxPerColors);
111 }
112
113 if (!useCustomWl) { // only scale when no custom white level was specified
114 satThreshold *= 0.99;
115 }
116
117 Log::debug( "Using white level ", satThreshold );
118
119 for (auto& i : images) {
120 i.setSaturationThreshold(satThreshold);
121 }
122 }
123
124
125 void ImageStack::align() {
126 if (images.size() > 1) {
127 Timer t("Align");
128 size_t errors[images.size()];
129 #pragma omp parallel for schedule(dynamic)
130 for (size_t i = 0; i < images.size(); ++i) {
131 images[i].preScale();
132 }
133 #pragma omp parallel for schedule(dynamic)
134 for (size_t i = 0; i < images.size() - 1; ++i) {
135 errors[i] = images[i].alignWith(images[i + 1]);
136 }
137 for (size_t i = images.size() - 1; i > 0; --i) {
138 images[i - 1].displace(images[i].getDeltaX(), images[i].getDeltaY());
139 Log::debug("Image ", i - 1, " displaced to (", images[i - 1].getDeltaX(),
140 ", ", images[i - 1].getDeltaY(), ") with error ", errors[i - 1]);
141 }
142 for (auto & i : images) {
143 i.releaseAlignData();
144 }
145 }
146 }
147
148 void ImageStack::crop() {
149 int dx = 0, dy = 0;
150 for (auto & i : images) {
151 int newDx = max(dx, i.getDeltaX());
152 int bound = min(dx + width, i.getDeltaX() + i.getWidth());
153 width = bound > newDx ? bound - newDx : 0;
154 dx = newDx;
155 int newDy = max(dy, i.getDeltaY());
156 bound = min(dy + height, i.getDeltaY() + i.getHeight());
157 height = bound > newDy ? bound - newDy : 0;
158 dy = newDy;
159 }
160 for (auto & i : images) {
161 i.displace(-dx, -dy);
162 }
163 }
164
165
166 void ImageStack::computeResponseFunctions() {
167 Timer t("Compute response functions");
168 for (int i = images.size() - 2; i >= 0; --i) {
169 images[i].computeResponseFunction(images[i + 1]);
170 }
171 }
172
173
174 void ImageStack::generateMask() {
175 Timer t("Generate mask");
176 mask.resize(width, height);
177 if(images.size() == 1) {
178 // single image, fill in zero values
179 std::fill_n(&mask[0], width*height, 0);
180 } else {
181 // multiple images, no need to prefill mask with zeroes. It will be filled correctly on the fly
182 #pragma omp parallel for schedule(dynamic)
183 for (size_t y = 0; y < height; ++y) {
184 for (size_t x = 0; x < width; ++x) {
185 size_t i = 0;
186 while (i < images.size() - 1 &&
187 (!images[i].contains(x, y) ||
188 images[i].isSaturatedAround(x, y))) ++i;
189 mask(x, y) = i;
190 }
191 }
192 }
193 // The mask can be used in compose to get the information about saturated pixels
194 // but the mask can be modified in gui, so we have to make a copy to represent the original state
195 origMask = mask;
196 }
197
198
199 double ImageStack::value(size_t x, size_t y) const {
200 const Image & img = images[mask(x, y)];
201 return img.exposureAt(x, y);
202 }
203
204 #ifndef __SSE2__
205 // From The GIMP: app/paint-funcs/paint-funcs.c:fatten_region
206 static Array2D<uint8_t> fattenMask(const Array2D<uint8_t> & mask, int radius) {
207 Timer t("Fatten mask");
208 size_t width = mask.getWidth(), height = mask.getHeight();
209 Array2D<uint8_t> result(width, height);
210
211 int circArray[2 * radius + 1]; // holds the y coords of the filter's mask
212 // compute_border(circArray, radius)
213 for (int i = 0; i < radius * 2 + 1; i++) {
214 double tmp;
215 if (i > radius)
216 tmp = (i - radius) - 0.5;
217 else if (i < radius)
218 tmp = (radius - i) - 0.5;
219 else
220 tmp = 0.0;
221 circArray[i] = int(std::sqrt(radius*radius - tmp*tmp));
222 }
223 // offset the circ pointer by radius so the range of the array
224 // is [-radius] to [radius]
225 int * circ = circArray + radius;
226
227 const uint8_t * bufArray[height + 2*radius];
228 for (int i = 0; i < radius; i++) {
229 bufArray[i] = &mask[0];
230 }
231 for (size_t i = 0; i < height; i++) {
232 bufArray[i + radius] = &mask[i * width];
233 }
234 for (int i = 0; i < radius; i++) {
235 bufArray[i + height + radius] = &mask[(height - 1) * width];
236 }
237 // offset the buf pointer
238 const uint8_t ** buf = bufArray + radius;
239
240 #pragma omp parallel
241 {
242 unique_ptr<uint8_t[]> buffer(new uint8_t[width * (radius + 1)]);
243 unique_ptr<uint8_t *[]> maxArray; // caches the largest values for each column
244 maxArray.reset(new uint8_t *[width + 2 * radius]);
245 for (int i = 0; i < radius; i++) {
246 maxArray[i] = buffer.get();
247 }
248 for (size_t i = 0; i < width; i++) {
249 maxArray[i + radius] = &buffer[(radius + 1) * i];
250 }
251 for (int i = 0; i < radius; i++) {
252 maxArray[i + width + radius] = &buffer[(radius + 1) * (width - 1)];
253 }
254 // offset the max pointer
255 uint8_t ** max = maxArray.get() + radius;
256
257 #pragma omp for schedule(dynamic)
258 for (size_t y = 0; y < height; y++) {
259 uint8_t rowMax = 0;
260 for (size_t x = 0; x < width; x++) { // compute max array
261 max[x][0] = buf[y][x];
262 for (int i = 1; i <= radius; i++) {
263 max[x][i] = std::max(std::max(max[x][i - 1], buf[y + i][x]), buf[y - i][x]);
264 rowMax = std::max(max[x][i], rowMax);
265 }
266 }
267
268 uint8_t last_max = max[0][circ[-1]];
269 int last_index = 1;
270 for (size_t x = 0; x < width; x++) { // render scan line
271 last_index--;
272 if (last_index >= 0) {
273 if (last_max == rowMax) {
274 result(x, y) = rowMax;
275 } else {
276 last_max = 0;
277 for (int i = radius; i >= 0; i--)
278 if (last_max < max[x + i][circ[i]]) {
279 last_max = max[x + i][circ[i]];
280 last_index = i;
281 }
282 result(x, y) = last_max;
283 }
284 } else {
285 last_index = radius;
286 last_max = max[x + radius][circ[radius]];
287
288 for (int i = radius - 1; i >= -radius; i--)
289 if (last_max < max[x + i][circ[i]]) {
290 last_max = max[x + i][circ[i]];
291 last_index = i;
292 }
293 result(x, y) = last_max;
294 }
295 }
296 }
297 }
298
299 return result;
300 }
301 #else // use faster SSE version, crunch 16 bytes at once
302 // From The GIMP: app/paint-funcs/paint-funcs.c:fatten_region
303 // SSE version by Ingo Weyrich
304 static Array2D<uint8_t> fattenMask(const Array2D<uint8_t> & mask, int radius) {
305 Timer t("Fatten mask (SSE version)");
306 size_t width = mask.getWidth(), height = mask.getHeight();
307 Array2D<uint8_t> result(width, height);
308
309 int circArray[2 * radius + 1]; // holds the y coords of the filter's mask
310 // compute_border(circArray, radius)
311 for (int i = 0; i < radius * 2 + 1; i++) {
312 double tmp;
313 if (i > radius)
314 tmp = (i - radius) - 0.5;
315 else if (i < radius)
316 tmp = (radius - i) - 0.5;
317 else
318 tmp = 0.0;
319 circArray[i] = int(std::sqrt(radius*radius - tmp*tmp));
320 }
321 // offset the circ pointer by radius so the range of the array
322 // is [-radius] to [radius]
323 int * circ = circArray + radius;
324
325 const uint8_t * bufArray[height + 2*radius];
326 for (int i = 0; i < radius; i++) {
327 bufArray[i] = &mask[0];
328 }
329 for (size_t i = 0; i < height; i++) {
330 bufArray[i + radius] = &mask[i * width];
331 }
332 for (int i = 0; i < radius; i++) {
333 bufArray[i + height + radius] = &mask[(height - 1) * width];
334 }
335 // offset the buf pointer
336 const uint8_t ** buf = bufArray + radius;
337
338 #pragma omp parallel
339 {
340 uint8_t buffer[width * (radius + 1)];
341 uint8_t *maxArray[radius+1];
342 for (int i = 0; i <= radius; i++) {
343 maxArray[i] = &buffer[i*width];
344 }
345
346 #pragma omp for schedule(dynamic,16)
347 for (size_t y = 0; y < height; y++) {
348 size_t x = 0;
349 for (; x < width-15; x+=16) { // compute max array, use SSE to process 16 bytes at once
350 __m128i lmax = _mm_loadu_si128((__m128i*)&buf[y][x]);
351 if(radius<2) // max[0] is only used when radius < 2
352 _mm_storeu_si128((__m128i*)&maxArray[0][x],lmax);
353 for (int i = 1; i <= radius; i++) {
354 lmax = _mm_max_epu8(_mm_loadu_si128((__m128i*)&buf[y + i][x]),lmax);
355 lmax = _mm_max_epu8(_mm_loadu_si128((__m128i*)&buf[y - i][x]),lmax);
356 _mm_storeu_si128((__m128i*)&maxArray[i][x],lmax);
357 }
358 }
359 for (; x < width; x++) { // compute max array, remaining columns
360 uint8_t lmax = buf[y][x];
361 if(radius<2) // max[0] is only used when radius < 2
362 maxArray[0][x] = lmax;
363 for (int i = 1; i <= radius; i++) {
364 lmax = std::max(std::max(lmax, buf[y + i][x]), buf[y - i][x]);
365 maxArray[i][x] = lmax;
366 }
367 }
368
369 for (x = 0; (int)x < radius; x++) { // render scan line, first columns without SSE
370 uint8_t last_max = maxArray[circ[radius]][x+radius];
371 for (int i = radius - 1; i >= -(int)x; i--)
372 last_max = std::max(last_max,maxArray[circ[i]][x + i]);
373 result(x, y) = last_max;
374 }
375 for (; x < width-15-radius+1; x += 16) { // render scan line, use SSE to process 16 bytes at once
376 __m128i last_maxv = _mm_loadu_si128((__m128i*)&maxArray[circ[radius]][x+radius]);
377 for (int i = radius - 1; i >= -radius; i--)
378 last_maxv = _mm_max_epu8(last_maxv,_mm_loadu_si128((__m128i*)&maxArray[circ[i]][x+i]));
379 _mm_storeu_si128((__m128i*)&result(x,y),last_maxv);
380 }
381
382 for (; x < width; x++) { // render scan line, last columns without SSE
383 int maxRadius = std::min(radius,(int)((int)width-1-(int)x));
384 uint8_t last_max = maxArray[circ[maxRadius]][x+maxRadius];
385 for (int i = maxRadius-1; i >= -radius; i--)
386 last_max = std::max(last_max,maxArray[circ[i]][x + i]);
387 result(x, y) = last_max;
388 }
389 }
390 }
391
392 return result;
393 }
394 #endif
395
396 Array2D<float> ImageStack::compose(const RawParameters & params, int featherRadius) const {
397 int imageMax = images.size() - 1;
398 BoxBlur map(fattenMask(mask, featherRadius));
399 measureTime("Blur", [&] () {
400 map.blur(featherRadius);
401 });
402 Timer t("Compose");
403 Array2D<float> dst(params.rawWidth, params.rawHeight);
404 dst.displace(-(int)params.leftMargin, -(int)params.topMargin);
405 dst.fillBorders(0.f);
406
407 float max = 0.0;
408 double saturatedRange = params.max - satThreshold;
409 #pragma omp parallel
410 {
411 float maxthr = 0.0;
412 #pragma omp for schedule(dynamic,16) nowait
413 for (size_t y = 0; y < height; ++y) {
414 for (size_t x = 0; x < width; ++x) {
415 double v, vv;
416 double p = map(x,y);
417 p = p < 0.0 ? 0.0 : p;
418 int j = p;
419 if (images[j].contains(x, y)) {
420 p = p - j;
421 v = images[j].exposureAt(x, y);
422 // Adjust false highlights
423 if (j < origMask(x,y)) { // SaturatedAround
424 v /= params.whiteMultAt(x, y);
425 if(p > 0.0001) {
426 uint16_t rawV = images[j].getMaxAround(x, y);
427 double k = (rawV - satThreshold) / saturatedRange;
428 if (k > 1.0)
429 k = 1.0;
430 p += (1.0 - p) * k;
431 }
432 }
433 } else {
434 v = 0.0;
435 p = 1.0;
436 }
437 if (p > 0.0001 && j < imageMax && images[j + 1].contains(x, y)) {
438 vv = images[j + 1].exposureAt(x, y);
439 if (j + 1 < origMask(x,y)) { // SaturatedAround
440 vv /= params.whiteMultAt(x, y);
441 }
442 } else {
443 vv = 0.0;
444 p = 0.0;
445 }
446 v -= p * (v - vv);
447 dst(x, y) = v;
448 if (v > maxthr) {
449 maxthr = v;
450 }
451 }
452 }
453 #pragma omp critical
454 if (maxthr > max) {
455 max = maxthr;
456 }
457 }
458
459 dst.displace(params.leftMargin, params.topMargin);
460 // Scale to params.max and recover the black levels
461 float mult = (params.max - params.maxBlack) / max;
462 #pragma omp parallel for
463 for (size_t y = 0; y < params.rawHeight; ++y) {
464 for (size_t x = 0; x < params.rawWidth; ++x) {
465 dst(x, y) *= mult;
466 dst(x, y) += params.blackAt(x - params.leftMargin, y - params.topMargin);
467 }
468 }
469
470 return dst;
471 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _IMAGESTACK_H_
23 #define _IMAGESTACK_H_
24
25 #include <vector>
26 #include <string>
27 #include <memory>
28 #include <cmath>
29 #include "Image.hpp"
30 #include "Array2D.hpp"
31 #include "EditableMask.hpp"
32 #include "LoadSaveOptions.hpp"
33
34 namespace hdrmerge {
35
36 class ImageStack {
37 public:
38 ImageStack() : mask(this), width(0), height(0), flip(0) {}
39 void clear() {
40 images.clear();
41 width = height = 0;
42 mask.reset();
43 }
44
45 int addImage(Image && i);
46 void align();
47 void crop();
48 void computeResponseFunctions();
49 void generateMask();
50 Array2D<float> compose(const RawParameters & md, int featherRadius) const;
51
52 size_t size() const { return images.size(); }
53
54 size_t getWidth() const {
55 return width;
56 }
57 size_t getHeight() const {
58 return height;
59 }
60 int getFlip() const {
61 return flip;
62 }
63 void setFlip(int f) {
64 flip = f;
65 }
66 double getMaxExposure() const {
67 return images.back().getRelativeExposure() / images[0].getRelativeExposure();
68 }
69 bool isCropped() const {
70 return width != images[0].getWidth() || height != images[0].getHeight();
71 }
72
73 Image & getImage(unsigned int i) {
74 return images[i];
75 }
76 const Image & getImage(unsigned int i) const {
77 return images[i];
78 }
79 uint8_t getImageAt(size_t x, size_t y) const {
80 return mask(x, y);
81 }
82 EditableMask & getMask() {
83 return mask;
84 }
85
86 double value(size_t x, size_t y) const;
87
88 bool isLayerValidAt(int layer, size_t x, size_t y) const {
89 return images[layer].contains(x, y);
90 }
91 void calculateSaturationLevel(const RawParameters & params, bool useCustomWl = false);
92
93 private:
94 class EditableMaskImpl : public EditableMask {
95 public:
96 EditableMaskImpl(const ImageStack * s) : EditableMask(), stack(s) {}
97 private:
98 const ImageStack * stack;
99 virtual bool isLayerValidAt(int layer, int x, int y) const {
100 return stack->isLayerValidAt(layer, x, y);
101 }
102 };
103
104 std::vector<Image> images; ///< Images, from most to least exposed
105 EditableMaskImpl mask;
106 Array2D<uint8_t> origMask;
107 size_t width;
108 size_t height;
109 int flip;
110 uint16_t satThreshold;
111 };
112
113 } // namespace hdrmerge
114
115 #endif // _IMAGESTACK_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <iostream>
23 #include <iomanip>
24 #include <string>
25 #include <QApplication>
26 #include <QTranslator>
27 #include <QLibraryInfo>
28 #include <QLocale>
29 #include "Launcher.hpp"
30 #include "ImageIO.hpp"
31 #ifndef NO_GUI
32 #include "MainWindow.hpp"
33 #endif
34 #include "Log.hpp"
35 #include <libraw.h>
36
37 using namespace std;
38
39 namespace hdrmerge {
40
41 Launcher::Launcher(int argc, char * argv[]) : argc(argc), argv(argv), help(false) {
42 Log::setOutputStream(cout);
43 saveOptions.previewSize = 2;
44 }
45
46
47 int Launcher::startGUI() {
48 #ifndef NO_GUI
49 // Create main window
50 MainWindow mw;
51 mw.preload(generalOptions.fileNames);
52 mw.show();
53 QMetaObject::invokeMethod(&mw, "loadImages", Qt::QueuedConnection);
54
55 return QApplication::exec();
56 #else
57 return 0;
58 #endif
59 }
60
61
62 struct CoutProgressIndicator : public ProgressIndicator {
63 virtual void advance(int percent, const char * message, const char * arg) {
64 if (arg) {
65 Log::progress('[', setw(3), percent, "%] ", QCoreApplication::translate("LoadSave", message).arg(arg));
66 } else {
67 Log::progress('[', setw(3), percent, "%] ", QCoreApplication::translate("LoadSave", message));
68 }
69 }
70 };
71
72
73 list<LoadOptions> Launcher::getBracketedSets() {
74 list<LoadOptions> result;
75 list<pair<ImageIO::QDateInterval, QString>> dateNames;
76 for (QString & name : generalOptions.fileNames) {
77 ImageIO::QDateInterval interval = ImageIO::getImageCreationInterval(name);
78 if (interval.start.isValid()) {
79 dateNames.emplace_back(interval, name);
80 } else {
81 // We cannot get time information, process it alone
82 result.push_back(generalOptions);
83 result.back().fileNames.clear();
84 result.back().fileNames.push_back(name);
85 }
86 }
87 dateNames.sort();
88 ImageIO::QDateInterval lastInterval;
89 for (auto & dateName : dateNames) {
90 if (lastInterval.start.isNull() || lastInterval.difference(dateName.first) > generalOptions.batchGap) {
91 result.push_back(generalOptions);
92 result.back().fileNames.clear();
93 }
94 result.back().fileNames.push_back(dateName.second);
95 lastInterval = dateName.first;
96 }
97 int setNum = 0;
98 for (auto & i : result) {
99 Log::progressN("Set ", setNum++, ":");
100 for (auto & j : i.fileNames) {
101 Log::progressN(" ", j);
102 }
103 Log::progress();
104 }
105 return result;
106 }
107
108
109 int Launcher::automaticMerge() {
110 auto tr = [&] (const char * text) { return QCoreApplication::translate("LoadSave", text); };
111 list<LoadOptions> optionsSet;
112 if (generalOptions.batch) {
113 optionsSet = getBracketedSets();
114 } else {
115 optionsSet.push_back(generalOptions);
116 }
117 ImageIO io;
118 int result = 0;
119 for (LoadOptions & options : optionsSet) {
120 if (!options.withSingles && options.fileNames.size() == 1) {
121 Log::progress(tr("Skipping single image %1").arg(options.fileNames.front()));
122 continue;
123 }
124 CoutProgressIndicator progress;
125 int numImages = options.fileNames.size();
126 int result = io.load(options, progress);
127 if (result < numImages * 2) {
128 int format = result & 1;
129 int i = result >> 1;
130 if (format) {
131 cerr << tr("Error loading %1, it has a different format.").arg(options.fileNames[i]) << endl;
132 } else {
133 cerr << tr("Error loading %1, file not found.").arg(options.fileNames[i]) << endl;
134 }
135 result = 1;
136 continue;
137 }
138 SaveOptions setOptions = saveOptions;
139 if (!setOptions.fileName.isEmpty()) {
140 setOptions.fileName = io.replaceArguments(setOptions.fileName, "");
141 int extPos = setOptions.fileName.lastIndexOf('.');
142 if (extPos > setOptions.fileName.length() || setOptions.fileName.mid(extPos) != ".dng") {
143 setOptions.fileName += ".dng";
144 }
145 } else {
146 setOptions.fileName = io.buildOutputFileName();
147 }
148 Log::progress(tr("Writing result to %1").arg(setOptions.fileName));
149 io.save(setOptions, progress);
150 }
151 return result;
152 }
153
154
155 void Launcher::parseCommandLine() {
156 auto tr = [&] (const char * text) { return QCoreApplication::translate("Help", text); };
157 for (int i = 1; i < argc; ++i) {
158 if (string("-o") == argv[i]) {
159 if (++i < argc) {
160 saveOptions.fileName = argv[i];
161 }
162 } else if (string("-m") == argv[i]) {
163 if (++i < argc) {
164 saveOptions.maskFileName = argv[i];
165 saveOptions.saveMask = true;
166 }
167 } else if (string("-v") == argv[i]) {
168 Log::setMinimumPriority(1);
169 } else if (string("-vv") == argv[i]) {
170 Log::setMinimumPriority(0);
171 } else if (string("--no-align") == argv[i]) {
172 generalOptions.align = false;
173 } else if (string("--no-crop") == argv[i]) {
174 generalOptions.crop = false;
175 } else if (string("--batch") == argv[i] || string("-B") == argv[i]) {
176 generalOptions.batch = true;
177 } else if (string("--single") == argv[i]) {
178 generalOptions.withSingles = true;
179 } else if (string("--help") == argv[i]) {
180 help = true;
181 } else if (string("-b") == argv[i]) {
182 if (++i < argc) {
183 try {
184 int value = stoi(argv[i]);
185 if (value == 32 || value == 24 || value == 16) saveOptions.bps = value;
186 } catch (std::invalid_argument & e) {
187 cerr << tr("Invalid %1 parameter, using default.").arg(argv[i - 1]) << endl;
188 }
189 }
190 } else if (string("-w") == argv[i]) {
191 if (++i < argc) {
192 try {
193 generalOptions.customWl = stoi(argv[i]);
194 generalOptions.useCustomWl = true;
195 } catch (std::invalid_argument & e) {
196 cerr << tr("Invalid %1 parameter, using default.").arg(argv[i - 1]) << endl;
197 generalOptions.useCustomWl = false;
198 }
199 }
200 } else if (string("-g") == argv[i]) {
201 if (++i < argc) {
202 try {
203 generalOptions.batchGap = stod(argv[i]);
204 } catch (std::invalid_argument & e) {
205 cerr << tr("Invalid %1 parameter, using default.").arg(argv[i - 1]) << endl;
206 }
207 }
208 } else if (string("-r") == argv[i]) {
209 if (++i < argc) {
210 try {
211 saveOptions.featherRadius = stoi(argv[i]);
212 } catch (std::invalid_argument & e) {
213 cerr << tr("Invalid %1 parameter, using default.").arg(argv[i - 1]) << endl;
214 }
215 }
216 } else if (string("-p") == argv[i]) {
217 if (++i < argc) {
218 string previewWidth(argv[i]);
219 if (previewWidth == "full") {
220 saveOptions.previewSize = 2;
221 } else if (previewWidth == "half") {
222 saveOptions.previewSize = 1;
223 } else if (previewWidth == "none") {
224 saveOptions.previewSize = 0;
225 } else {
226 cerr << tr("Invalid %1 parameter, using default.").arg(argv[i - 1]) << endl;
227 }
228 }
229 } else if (argv[i][0] != '-') {
230 generalOptions.fileNames.push_back(QString::fromLocal8Bit(argv[i]));
231 }
232 }
233 }
234
235
236 void Launcher::showHelp() {
237 auto tr = [&] (const char * text) { return QCoreApplication::translate("Help", text); };
238 cout << tr("Usage") << ": HDRMerge [--help] [OPTIONS ...] [RAW_FILES ...]" << endl;
239 cout << tr("Merges RAW_FILES into an HDR DNG raw image.") << endl;
240 #ifndef NO_GUI
241 cout << tr("If neither -a nor -o, nor --batch options are given, the GUI will be presented.") << endl;
242 #endif
243 cout << tr("If similar options are specified, only the last one prevails.") << endl;
244 cout << endl;
245 cout << tr("Options:") << endl;
246 cout << " " << "--help " << tr("Shows this message.") << endl;
247 cout << " " << "-o OUT_FILE " << tr("Sets OUT_FILE as the output file name.") << endl;
248 cout << " " << " " << tr("The following parameters are accepted, most useful in batch mode:") << endl;
249 cout << " " << " - %if[n]: " << tr("Replaced by the base file name of image n. Image file names") << endl;
250 cout << " " << " " << tr("are first sorted in lexicographical order. Besides, n = -1 is the") << endl;
251 cout << " " << " " << tr("last image, n = -2 is the previous to the last image, and so on.") << endl;
252 cout << " " << " - %iF[n]: " << tr("Replaced by the base file name of image n without the extension.") << endl;
253 cout << " " << " - %id[n]: " << tr("Replaced by the directory name of image n.") << endl;
254 cout << " " << " - %in[n]: " << tr("Replaced by the numerical suffix of image n, if it exists.") << endl;
255 cout << " " << " " << tr("For instance, in IMG_1234.CR2, the numerical suffix would be 1234.") << endl;
256 cout << " " << " - %%: " << tr("Replaced by a single %.") << endl;
257 cout << " " << "-a " << tr("Calculates the output file name as") << " %id[-1]/%iF[0]-%in[-1].dng." << endl;
258 cout << " " << "-B|--batch " << tr("Batch mode: Input images are automatically grouped into bracketed sets,") << endl;
259 cout << " " << " " << tr("by comparing the creation time. Implies -a if no output file name is given.") << endl;
260 cout << " " << "-g gap " << tr("Batch gap, maximum difference in seconds between two images of the same set.") << endl;
261 cout << " " << "--single " << tr("Include single images in batch mode (the default is to skip them.)") << endl;
262 cout << " " << "-b BPS " << tr("Bits per sample, can be 16, 24 or 32.") << endl;
263 cout << " " << "--no-align " << tr("Do not auto-align source images.") << endl;
264 cout << " " << "--no-crop " << tr("Do not crop the output image to the optimum size.") << endl;
265 cout << " " << "-m MASK_FILE " << tr("Saves the mask to MASK_FILE as a PNG image.") << endl;
266 cout << " " << " " << tr("Besides the parameters accepted by -o, it also accepts:") << endl;
267 cout << " " << " - %of: " << tr("Replaced by the base file name of the output file.") << endl;
268 cout << " " << " - %od: " << tr("Replaced by the directory name of the output file.") << endl;
269 cout << " " << "-r radius " << tr("Mask blur radius, to soften transitions between images. Default is 3 pixels.") << endl;
270 cout << " " << "-p size " << tr("Preview size. Can be full, half or none.") << endl;
271 cout << " " << "-v " << tr("Verbose mode.") << endl;
272 cout << " " << "-vv " << tr("Debug mode.") << endl;
273 cout << " " << "-w whitelevel " << tr("Use custom white level.") << endl;
274 cout << " " << "RAW_FILES " << tr("The input raw files.") << endl;
275 }
276
277
278 bool Launcher::checkGUI() {
279 int numFiles = 0;
280 bool useGUI = true;
281 for (int i = 1; i < argc; ++i) {
282 if (string("-o") == argv[i]) {
283 if (++i < argc) {
284 useGUI = false;
285 }
286 } else if (string("-a") == argv[i]) {
287 useGUI = false;
288 } else if (string("--batch") == argv[i]) {
289 useGUI = false;
290 } else if (string("-B") == argv[i]) {
291 useGUI = false;
292 } else if (string("--help") == argv[i]) {
293 return false;
294 } else if (argv[i][0] != '-') {
295 numFiles++;
296 }
297 }
298 return useGUI || numFiles == 0;
299 }
300
301
302 int Launcher::run() {
303 #ifndef NO_GUI
304 bool useGUI = checkGUI();
305 #else
306 bool useGUI = false;
307 help = checkGUI();
308 #endif
309 QApplication app(argc, argv, useGUI);
310
311 // Settings
312 QCoreApplication::setOrganizationName("J.Celaya");
313 QCoreApplication::setApplicationName("HdrMerge");
314
315 // Translation
316 QTranslator qtTranslator;
317 qtTranslator.load("qt_" + QLocale::system().name(),
318 QLibraryInfo::location(QLibraryInfo::TranslationsPath));
319 app.installTranslator(&qtTranslator);
320
321 QTranslator appTranslator;
322 appTranslator.load("hdrmerge_" + QLocale::system().name(), ":/translators");
323 app.installTranslator(&appTranslator);
324
325 parseCommandLine();
326 Log::debug("Using LibRaw ", libraw_version());
327
328 if (help) {
329 showHelp();
330 return 0;
331 } else if (useGUI) {
332 return startGUI();
333 } else {
334 return automaticMerge();
335 }
336 }
337
338 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _LAUNCHER_HPP_
23 #define _LAUNCHER_HPP_
24
25 #include <list>
26 #include <string>
27 #include "ImageStack.hpp"
28
29 namespace hdrmerge {
30
31 class Launcher {
32 public:
33 Launcher(int argc, char * argv[]);
34
35 void parseCommandLine();
36
37 int run();
38
39 private:
40 bool checkGUI();
41 int startGUI();
42 int automaticMerge();
43 void showHelp();
44 std::list<LoadOptions> getBracketedSets();
45
46 int argc;
47 char ** argv;
48 LoadOptions generalOptions;
49 SaveOptions saveOptions;
50 bool help;
51 };
52
53 } // namespace hdrmerge
54
55 #endif // _LAUNCHER_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <QFormLayout>
23 #include <QVBoxLayout>
24 #include <QButtonGroup>
25 #include <QPushButton>
26 #include <QFileDialog>
27 #include <QSettings>
28 #include "LoadOptionsDialog.hpp"
29 #include "FileSystem.hpp"
30
31 namespace hdrmerge {
32
33
34 class FileItem : public QListWidgetItem {
35 public:
36 FileItem(const QString & filename, QListWidget * parent) : QListWidgetItem(parent, 1000) {
37 setText(QFileInfo(filename).fileName());
38 setData(Qt::UserRole, QVariant(filename));
39 setSizeHint(QSize(0, 24));
40 }
41 };
42
43
44 LoadOptionsDialog::LoadOptionsDialog(QWidget * parent, Qt::WindowFlags f)
45 : QDialog(parent, f), LoadOptions() {
46 QSettings settings;
47 QVBoxLayout * layout = new QVBoxLayout(this);
48
49 setMinimumSize(400, 400);
50
51 QWidget * fileSelector = new QWidget(this);
52 QHBoxLayout * fileSelectorLayout = new QHBoxLayout(fileSelector);
53 fileSelectorLayout->setMargin(0);
54 fileList = new QListWidget(fileSelector);
55 fileList->setSelectionMode(QAbstractItemView::ExtendedSelection);
56 fileSelectorLayout->addWidget(fileList, 1);
57
58 QWidget * addRemoveButtons = new QWidget(this);
59 QVBoxLayout * addRemoveButtonsLayout = new QVBoxLayout(addRemoveButtons);
60 addRemoveButtonsLayout->setMargin(0);
61 QPushButton * addButton = new QPushButton(tr("Add"), addRemoveButtons);
62 QPushButton * removeButton = new QPushButton(tr("Remove"), addRemoveButtons);
63 addRemoveButtonsLayout->addWidget(addButton, 0, Qt::AlignTop);
64 addRemoveButtonsLayout->addWidget(removeButton, 0, Qt::AlignTop);
65 addRemoveButtonsLayout->addStretch(1);
66 fileSelectorLayout->addWidget(addRemoveButtons, 0);
67 layout->addWidget(fileSelector, 1);
68 connect(addButton, SIGNAL(clicked(bool)), this, SLOT(addFiles()));
69 connect(removeButton, SIGNAL(clicked(bool)), this, SLOT(removeFiles()));
70
71 alignBox = new QCheckBox(tr("Align source images."), this);
72 alignBox->setChecked(settings.value("alignOnLoad", true).toBool());
73 layout->addWidget(alignBox, 0);
74
75 cropBox = new QCheckBox(tr("Crop result image to optimal size."), this);
76 cropBox->setChecked(settings.value("cropOnLoad", true).toBool());
77 layout->addWidget(cropBox, 0);
78
79 customWhiteLevelBox = new QCheckBox(tr("Use custom white level."), this);
80 customWhiteLevelBox->setChecked(settings.value("useCustomWlOnLoad", false).toBool());
81 layout->addWidget(customWhiteLevelBox, 0);
82
83 customWhiteLevelSpinBox = new QSpinBox();
84 customWhiteLevelSpinBox->setRange(0, 65535);
85 customWhiteLevelSpinBox->setValue(settings.value("customWlOnLoad", 16383).toInt());
86 customWhiteLevelSpinBox->setToolTip(tr("Custom white level."));
87 layout->addWidget(customWhiteLevelSpinBox, 0);
88
89 QWidget * buttons = new QWidget(this);
90 QHBoxLayout * buttonsLayout = new QHBoxLayout(buttons);
91 QPushButton * acceptButton = new QPushButton(tr("Accept"), this);
92 acceptButton->setDefault(true);
93 connect(acceptButton, SIGNAL(clicked(bool)), this, SLOT(accept()));
94 QPushButton * cancelButton = new QPushButton(tr("Cancel"), this);
95 connect(cancelButton, SIGNAL(clicked(bool)), this, SLOT(reject()));
96 buttonsLayout->addWidget(acceptButton);
97 buttonsLayout->addWidget(cancelButton);
98 layout->addWidget(buttons, 0, Qt::AlignHCenter);
99
100 setLayout(layout);
101 setWindowTitle(tr("Open raw images"));
102 }
103
104
105 void LoadOptionsDialog::showEvent(QShowEvent * event) {
106 if (!fileNames.empty()) {
107 for (auto & i : fileNames) {
108 new FileItem(i, fileList);
109 }
110 fileNames.clear();
111 }
112 }
113
114
115 void LoadOptionsDialog::addFiles() {
116 QSettings settings;
117 QVariant lastDirSetting = settings.value("lastOpenDirectory");
118 QString filter(tr("Raw images ("
119 "*.3fr "
120 "*.ari *.arw "
121 "*.bay "
122 "*.crw *.cr2 *.cap "
123 "*.dcs *.dcr *.dng *.drf "
124 "*.eip *.erf "
125 "*.fff "
126 "*.iiq "
127 "*.k25 *.kdc "
128 "*.mdc *.mef *.mos *.mrw "
129 "*.nef *.nrw "
130 "*.obm *.orf "
131 "*.pef *.ptx *.pxn "
132 "*.r3d *.raf *.raw *.rwl *.rw2 *.rwz "
133 "*.sr2 *.srf *.srw "
134 "*.x3f"
135 ")"));
136
137
138 QFileDialog loadDialog(this, tr("Select raw photos"), lastDirSetting.isNull() ? QDir::currentPath() : QDir(lastDirSetting.toString()).absolutePath(), filter);
139 loadDialog.setOptions(QFileDialog::DontUseNativeDialog);
140 loadDialog.setAcceptMode(QFileDialog::AcceptOpen);
141 loadDialog.setFileMode(QFileDialog::ExistingFiles);
142 QList<QUrl> urls = getStdUrls();
143 loadDialog.setSidebarUrls(urls);
144 if (loadDialog.exec()) {
145 QStringList files = loadDialog.selectedFiles();
146 if (!files.empty()) {
147 QString lastDir = QFileInfo(files.front()).absolutePath();
148 settings.setValue("lastOpenDirectory", lastDir);
149 for (auto & i : files) {
150 new FileItem(i, fileList);
151 }
152 }
153 }
154 }
155
156
157 void LoadOptionsDialog::removeFiles() {
158 QList<QListWidgetItem *> items = fileList->selectedItems();
159 for (auto i : items) {
160 delete i;
161 }
162 }
163
164
165 void LoadOptionsDialog::accept() {
166 QSettings settings;
167 align = alignBox->isChecked();
168 settings.setValue("alignOnLoad", align);
169 crop = cropBox->isChecked();
170 settings.setValue("cropOnLoad", crop);
171 useCustomWl = customWhiteLevelBox->isChecked();
172 settings.setValue("useCustomWlOnLoad", useCustomWl);
173 customWl = customWhiteLevelSpinBox->value();
174 settings.setValue("customWlOnLoad", customWl);
175 for (int i = 0; i < fileList->count(); ++i) {
176 fileNames.push_back(fileList->item(i)->data(Qt::UserRole).toString());
177 }
178 QDialog::accept();
179 }
180
181 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _LOADOPTIONSDIALOG_H_
23 #define _LOADOPTIONSDIALOG_H_
24
25 #include <QDialog>
26 #include <QListWidget>
27 #include <QCheckBox>
28 #include <QSpinBox>
29 #include "LoadSaveOptions.hpp"
30
31 namespace hdrmerge {
32
33 class LoadOptionsDialog : public QDialog, public LoadOptions {
34 public:
35 LoadOptionsDialog(QWidget * parent = 0, Qt::WindowFlags f = 0);
36
37 void closeEvent(QCloseEvent * event) { reject(); }
38
39 public slots:
40 virtual void accept();
41 void addFiles();
42 void removeFiles();
43
44 protected:
45 virtual void showEvent(QShowEvent * event);
46
47 private:
48 Q_OBJECT
49
50 QListWidget * fileList;
51 QCheckBox * alignBox;
52 QCheckBox * cropBox;
53 QCheckBox * customWhiteLevelBox;
54 QSpinBox * customWhiteLevelSpinBox;
55 };
56
57 } // namespace hdrmerge
58
59 #endif // _LOADOPTIONSDIALOG_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _LOADSAVEOPTIONS_H_
23 #define _LOADSAVEOPTIONS_H_
24
25 #include <vector>
26 #include <QString>
27
28 namespace hdrmerge {
29
30 struct LoadOptions {
31 std::vector<QString> fileNames;
32 bool align;
33 bool crop;
34 bool useCustomWl;
35 uint16_t customWl;
36 bool batch;
37 double batchGap;
38 bool withSingles;
39 LoadOptions() : align(true), crop(true), useCustomWl(false), customWl(16383), batch(false), batchGap(2.0),
40 withSingles(false) {}
41 };
42
43
44 struct SaveOptions {
45 int bps;
46 int previewSize;
47 QString fileName;
48 bool saveMask;
49 QString maskFileName;
50 int featherRadius;
51 SaveOptions() : bps(16), previewSize(0), saveMask(false), featherRadius(3) {}
52 };
53
54 } // namespace hdrmerge
55
56 #endif // _LOADSAVEOPTIONS_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _LOG_HPP_
23 #define _LOG_HPP_
24
25 #include <ostream>
26 #include <string>
27 #include <chrono>
28 #include <QString>
29
30 namespace hdrmerge {
31
32
33 inline std::ostream & operator<<(std::ostream & os, const QString & s) {
34 return os << std::string(s.toLocal8Bit().constData());
35 }
36
37
38 class Log {
39 public:
40 enum {
41 DEBUG = 0,
42 PROGRESS = 1,
43 } Priority;
44
45 template <typename... Args>
46 static void msg(int priority, const Args &... params) {
47 Log & l = getInstance();
48 if (l.out && priority >= l.minPriority) {
49 l.output(params...);
50 *l.out << std::endl;
51 }
52 }
53
54 template <typename... Args>
55 static void msgN(int priority, const Args &... params) {
56 Log & l = getInstance();
57 if (l.out && priority >= l.minPriority) {
58 l.output(params...);
59 }
60 }
61
62 template <typename... Args>
63 static void debug(const Args &... params) {
64 msg(DEBUG, params...);
65 }
66
67 template <typename... Args>
68 static void debugN(const Args &... params) {
69 msgN(DEBUG, params...);
70 }
71
72 template <typename... Args>
73 static void progress(const Args &... params) {
74 msg(PROGRESS, params...);
75 }
76
77 template <typename... Args>
78 static void progressN(const Args &... params) {
79 msgN(PROGRESS, params...);
80 }
81
82 static void setMinimumPriority(int p) {
83 getInstance().minPriority = p;
84 }
85
86 static void setOutputStream(std::ostream & o) {
87 getInstance().out = &o;
88 }
89
90 private:
91 int minPriority;
92 std::ostream * out;
93
94 Log() : minPriority(2), out(nullptr) {}
95
96 static Log & getInstance() {
97 static Log instance;
98 return instance;
99 }
100
101 void output() {}
102
103 template<typename T, typename... Args>
104 void output(const T & value, const Args &... args) {
105 *out << value;
106 output(args...);
107 }
108 };
109
110
111
112 class Timer {
113 public:
114 Timer(const char * n) : name(n) {
115 start = std::chrono::steady_clock::now();
116 }
117 ~Timer() {
118 std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now();
119 double t = std::chrono::duration_cast<std::chrono::duration<double>>(end - start).count();
120 Log::debug(name, ": ", t, " seconds");
121 }
122
123 private:
124 std::chrono::steady_clock::time_point start;
125 const char * name;
126 };
127
128
129 template <typename Func> auto measureTime(const char * name, Func f) -> decltype(f()) {
130 Timer t(name);
131 return f();
132 }
133
134 } // namespace hdrmerge
135
136 #endif // _LOG_HPP_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <list>
23 #include <cmath>
24 #include "MainWindow.hpp"
25 #include <QApplication>
26 #include <QFuture>
27 #include <QtConcurrent/QtConcurrent>
28 #include <QHBoxLayout>
29 #include <QMessageBox>
30 #include <QFileDialog>
31 #include <QFileInfo>
32 #include <QMenuBar>
33 #include <QProgressDialog>
34 #include <QSettings>
35 #include <QUrl>
36 #include "config.h"
37 #include "AboutDialog.hpp"
38 #include "DngFloatWriter.hpp"
39 #include "ImageStack.hpp"
40 #include "PreviewWidget.hpp"
41 #include "DraggableScrollArea.hpp"
42 #include "DngPropertiesDialog.hpp"
43 #include "LoadOptionsDialog.hpp"
44 #include "FileSystem.hpp"
45 using namespace std;
46 using namespace hdrmerge;
47
48
49 class ProgressDialog : public QProgressDialog , public ProgressIndicator {
50 public:
51 ProgressDialog(QWidget * parent = 0) : QProgressDialog(parent) {
52 setMaximum(100);
53 setMinimum(0);
54 setMinimumDuration(0);
55 setCancelButtonText(QString());
56 }
57
58 virtual void advance(int percent, const char * message, const char * arg) {
59 QString translatedMessage = QCoreApplication::translate("LoadSave", message);
60 if (arg) {
61 translatedMessage = translatedMessage.arg(arg);
62 }
63 QMetaObject::invokeMethod(this, "setValue", Qt::QueuedConnection, Q_ARG(int, percent));
64 QMetaObject::invokeMethod(this, "setLabelText", Qt::QueuedConnection, Q_ARG(QString, translatedMessage));
65 }
66 };
67
68
69 MainWindow::MainWindow() : QMainWindow() {
70 createWidgets();
71 createActions();
72 createToolbars();
73 createMenus();
74
75 setWindowTitle(tr("HDRMerge %1 - Raw image fusion").arg(HDRMERGE_VERSION_STRING));
76 setWindowIcon(QIcon(":/icon.png"));
77
78 QSettings settings;
79 restoreGeometry(settings.value("windowGeometry").toByteArray());
80 restoreState(settings.value("windowState").toByteArray());
81 }
82
83
84 void MainWindow::createWidgets() {
85 statusBar = new QStatusBar(this);
86 setStatusBar(statusBar);
87 statusLabel = new QLabel(statusBar);
88 statusBar->addWidget(statusLabel);
89
90 previewArea = new DraggableScrollArea(this);
91 setCentralWidget(previewArea);
92 preview = new PreviewWidget(io.getImageStack(), previewArea);
93 previewArea->setWidget(preview);
94 connect(preview, SIGNAL(pixelUnderMouse(int, int)), this, SLOT(setPixelStatus(int, int)));
95
96 radiusBox = new QSpinBox();
97 radiusBox->setRange(0, 200);
98 radiusBox->findChild<QLineEdit*>()->setReadOnly(true);
99 radiusBox->setToolTip(tr("Brush radius of the add/remove tool."));
100 radiusSlider = new QSlider(Qt::Horizontal);
101 radiusSlider->setRange(0, PreviewWidget::maxRadius);
102 radiusSlider->setMaximumWidth(200);
103 radiusSlider->setToolTip(tr("Brush radius of the add/remove tool."));
104 connect(radiusBox, SIGNAL(valueChanged(int)), preview, SLOT(setRadius(int)));
105 connect(radiusSlider, SIGNAL(valueChanged(int)), preview, SLOT(setRadius(int)));
106 connect(preview, SIGNAL(radiusChanged(int)), radiusSlider, SLOT(setValue(int)));
107 connect(radiusBox, SIGNAL(valueChanged(int)), radiusSlider, SLOT(setValue(int)));
108 connect(radiusSlider, SIGNAL(valueChanged(int)), radiusBox, SLOT(setValue(int)));
109 radiusBox->setValue(5);
110
111 exposureSlider = new QSlider(Qt::Horizontal);
112 exposureSlider->setRange(0, 1000);
113 exposureSlider->setMaximumWidth(200);
114 exposureSlider->setToolTip(tr("Preview brightness. It does NOT affect the HDR result."));
115 connect(exposureSlider, SIGNAL(valueChanged(int)), preview, SLOT(setExposureMultiplier(int)));
116 }
117
118
119 void MainWindow::createActions() {
120 loadImagesAction = new QAction(tr("&Open raw images..."), this);
121 loadImagesAction->setShortcut(tr("Ctrl+O"));
122 connect(loadImagesAction, SIGNAL(triggered()), this, SLOT(loadImages()));
123
124 quitAction = new QAction(tr("&Quit"), this);
125 quitAction->setShortcut(tr("Ctrl+Q"));
126 connect(quitAction, SIGNAL(triggered()), this, SLOT(close()));
127
128 undoAction = new QAction(tr("Undo"), this);
129 undoAction->setShortcut(QString("Ctrl+z"));
130 connect(undoAction, SIGNAL(triggered()), preview, SLOT(undo()));
131
132 redoAction = new QAction(tr("Redo"), this);
133 redoAction->setShortcut(QString("Ctrl+Shift+z"));
134 connect(redoAction, SIGNAL(triggered()), preview, SLOT(redo()));
135
136 aboutAction = new QAction(tr("&About..."), this);
137 connect(aboutAction, SIGNAL(triggered()), this, SLOT(about()));
138
139 mergeAction = new QAction(tr("&Save HDR..."), this);
140 mergeAction->setShortcut(tr("Ctrl+S"));
141 mergeAction->setEnabled(false);
142 connect(mergeAction, SIGNAL(triggered()), this, SLOT(saveResult()));
143
144 dragToolAction = new QAction(QIcon(":/images/transform-move.png"), tr("Pan"), nullptr);
145 dragToolAction->setCheckable(true);
146 connect(dragToolAction, SIGNAL(toggled(bool)), previewArea, SLOT(toggleMoveViewport(bool)));
147
148 addGhostAction = new QAction(QIcon(":/images/draw-brush.png"), tr("Add pixels to the current image"), nullptr);
149 addGhostAction->setCheckable(true);
150 addGhostAction->setDisabled(true);
151 connect(addGhostAction, SIGNAL(toggled(bool)), preview, SLOT(toggleAddPixelsTool(bool)));
152
153 rmGhostAction = new QAction(QIcon(":/images/draw-eraser.png"), tr("Remove pixels from the current image"), nullptr);
154 rmGhostAction->setCheckable(true);
155 rmGhostAction->setDisabled(true);
156 connect(rmGhostAction, SIGNAL(toggled(bool)), preview, SLOT(toggleRmPixelsTool(bool)));
157 }
158
159
160 void MainWindow::createMenus() {
161 fileMenu = new QMenu(tr("&File"));
162 fileMenu->addAction(loadImagesAction);
163 fileMenu->addAction(mergeAction);
164 fileMenu->addSeparator();
165 fileMenu->addAction(quitAction);
166
167 editMenu = new QMenu(tr("&Edit"));
168 editMenu->addAction(undoAction);
169 editMenu->addAction(redoAction);
170
171 helpMenu = new QMenu(tr("&Help"));
172 helpMenu->addAction(aboutAction);
173
174 menuBar()->addMenu(fileMenu);
175 menuBar()->addMenu(editMenu);
176 menuBar()->addMenu(helpMenu);
177 }
178
179
180 void MainWindow::createToolbars() {
181 QToolBar * toolBar = addToolBar("Tools");
182 toolBar->setObjectName("Tools");
183 toolBar->setOrientation(Qt::Horizontal);
184 toolBar->setFloatable(false);
185 toolBar->setMovable(true);
186 toolBar->setAllowedAreas(Qt::TopToolBarArea | Qt::BottomToolBarArea);
187
188 QActionGroup * toolActionGroup = new QActionGroup(toolBar);
189 toolBar->addAction(toolActionGroup->addAction(dragToolAction));
190 toolBar->addAction(toolActionGroup->addAction(addGhostAction));
191 toolBar->addAction(toolActionGroup->addAction(rmGhostAction));
192 dragToolAction->setChecked(true);
193 lastTool = dragToolAction;
194 toolBar->addSeparator();
195 toolBar->addWidget(new QLabel(" " + tr("Radius:"), toolBar));
196 toolBar->addWidget(radiusBox);
197 toolBar->addWidget(radiusSlider);
198 toolBar->addSeparator();
199 toolBar->addWidget(new QLabel(" " + tr("Brightness:"), toolBar));
200 toolBar->addWidget(exposureSlider);
201 connect(toolActionGroup, SIGNAL(triggered(QAction *)), this, SLOT(toolSelected(QAction *)));
202
203 layerSelector = addToolBar("Layers");
204 layerSelector->setObjectName("Layers");
205 layerSelector->setFloatable(false);
206 layerSelector->setMovable(true);
207 toolBar->setAllowedAreas(Qt::AllToolBarAreas);
208 layerSelector->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
209 layerSelectorGroup = new QActionGroup(layerSelector);
210 connect(layerSelectorGroup, SIGNAL(triggered(QAction *)), this, SLOT(layerSelected(QAction *)));
211 }
212
213
214 void MainWindow::setPixelStatus(int x, int y) {
215 int l = io.getImageStack().getImageAt(x, y);
216 Image & img = io.getImageStack().getImage(l);
217 setStatus(tr("Layer %1: displaced %2,%3,%4 cropped | src. value = %5 ; result = %6")
218 .arg(l + 1)
219 .arg(img.getDeltaX())
220 .arg(img.getDeltaY())
221 .arg(io.getImageStack().isCropped() ? "" : " not")
222 .arg(io.getImageStack().getImage(l)(x, y))
223 .arg(io.getImageStack().value(x, y)));
224 }
225
226
227 void MainWindow::closeEvent(QCloseEvent * event) {
228 QSettings settings;
229 settings.setValue("windowGeometry", saveGeometry());
230 settings.setValue("windowState", saveState());
231 QMainWindow::closeEvent(event);
232 }
233
234
235 void MainWindow::about() {
236 AboutDialog dialog(this);
237 dialog.exec();
238 }
239
240 void MainWindow::loadImages() {
241 LoadOptionsDialog lod(this);
242 if (!preloadFiles.empty()) {
243 lod.fileNames = preloadFiles;
244 preloadFiles.clear();
245 }
246 if (lod.exec() && !lod.fileNames.empty()) {
247 int numImages = lod.fileNames.size();
248 ProgressDialog progress(this);
249 progress.setWindowTitle(tr("Open raw images"));
250 QFuture<int> error = QtConcurrent::run(std::function<int()>([&] () { return io.load(lod, progress); }));
251 while (error.isRunning())
252 QApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents);
253 int result = error.result();
254 if (result < numImages * 2) {
255 int i = result >> 1;
256 QString message = result & 1 ?
257 tr("File %1 has not the same format as the previous ones.").arg(lod.fileNames[i]) :
258 tr("Unable to open file %1.").arg(lod.fileNames[i]);
259 QMessageBox::warning(this, tr("Error opening file"), message);
260 }
261
262 numImages = io.getImageStack().size();
263 // Create GUI
264 preview->reload();
265 mergeAction->setEnabled(numImages > 0);
266 addGhostAction->setEnabled(numImages > 1);
267 rmGhostAction->setEnabled(numImages > 1);
268 radiusSlider->setValue(50);
269 exposureSlider->setValue(1000);
270 createLayerSelector();
271 }
272 setToolFromKey();
273 }
274
275
276 static QPixmap getColorIcon(int i) {
277 QImage colorBlock(20, 20, QImage::Format_ARGB32);
278 QColor color(PreviewWidget::getColor(i - 1, 255));
279 colorBlock.fill(color);
280 color.setAlpha(0);
281 int x[] = { 0, 1, 18, 19, 0, 19, 0, 19, 0, 1, 18, 19 };
282 int y[] = { 0, 0, 0, 0, 1, 1, 18, 18, 19, 19, 19, 19 };
283 for (int i = 0; i < 12; ++i) {
284 colorBlock.setPixel(x[i], y[i], color.rgba());
285 }
286 return QPixmap::fromImage(colorBlock);
287 }
288
289
290 void MainWindow::createLayerSelector() {
291 ImageStack & images = io.getImageStack();
292 unsigned int numImages = images.size();
293 layerSelector->clear();
294 for (auto action : layerSelectorGroup->actions()) {
295 layerSelectorGroup->removeAction(action);
296 delete action;
297 }
298 if (numImages > 1) {
299 double logLeastExp = std::log2(images.getImage(numImages - 1).getRelativeExposure());
300 for (unsigned int i = 1; i < numImages; i++) {
301 QAction * action = new QAction(QIcon(getColorIcon(i)), QString::number(i), layerSelectorGroup);
302 action->setCheckable(true);
303 double logExp = logLeastExp - std::log2(images.getImage(i - 1).getRelativeExposure());
304 action->setToolTip(QString("%1: +%2 EV").arg(QFileInfo(images.getImage(i - 1).getFilename()).baseName()).arg(logExp, 0, 'f', 2));
305 if (i < 10)
306 action->setShortcut(Qt::Key_0 + i);
307 else if (i == 10)
308 action->setShortcut(Qt::Key_0);
309 layerSelector->addAction(action);
310 }
311 QAction * firstAction = layerSelectorGroup->actions().first();
312 firstAction->setChecked(true);
313 preview->selectLayer(0);
314 }
315 if (numImages > 0) {
316 QWidget * lastLayer = new QWidget();
317 lastLayer->setContentsMargins(0, 1, 0, 0);
318 lastLayer->setLayout(new QHBoxLayout());
319 QLabel * lastIcon = new QLabel(lastLayer);
320 lastIcon->setPixmap(getColorIcon(numImages));
321 lastIcon->setToolTip(QString("%1: +0 EV").arg(QFileInfo(images.getImage(numImages - 1).getFilename()).baseName()));
322 lastLayer->layout()->addWidget(lastIcon);
323 lastLayer->layout()->addWidget(new QLabel(QString::number(numImages)));
324 //lastLayer->setMinimumHeight(layerSelector->widgetForAction(firstAction)->height());
325 layerSelector->addWidget(lastLayer);
326 }
327 }
328
329
330 void MainWindow::saveResult() {
331 if (io.getImageStack().size() > 0) {
332 QSettings settings;
333 QVariant lastDirSetting = settings.value("lastSaveDirectory");
334 // Take the prefix and add the first and last suffix
335 QString name = io.buildOutputFileName();
336 if (!lastDirSetting.isNull()) {
337 name = QDir(lastDirSetting.toString()).absolutePath() + "/" + QFileInfo(name).fileName();
338 }
339
340 QFileDialog saveDialog(this, tr("Save DNG file"), name, tr("Digital Negatives (*.dng)"));
341 saveDialog.setOptions(QFileDialog::DontUseNativeDialog);
342 saveDialog.setAcceptMode(QFileDialog::AcceptSave);
343 saveDialog.setFileMode(QFileDialog::AnyFile);
344 saveDialog.setConfirmOverwrite(true);
345
346 QList<QUrl> urls = getStdUrls(io.getInputPath());
347 saveDialog.setSidebarUrls(urls);
348
349 if (saveDialog.exec()) {
350 QString file = saveDialog.selectedFiles().front();
351 int extPos = file.lastIndexOf('.');
352 if (extPos > file.length() || file.mid(extPos) != ".dng") {
353 file += ".dng";
354 }
355 DngPropertiesDialog dpd(this);
356 if (dpd.exec()) {
357 settings.setValue("lastSaveDirectory", QFileInfo(file).absolutePath());
358 dpd.fileName = file;
359 ProgressDialog pd(this);
360 pd.setWindowTitle(tr("Save DNG file"));
361 QFuture<void> result = QtConcurrent::run(std::function<void()>([&]() {
362 io.save(dpd, pd);
363 }));
364 while (result.isRunning())
365 QApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents);
366 }
367 }
368 }
369 setToolFromKey();
370 }
371
372
373 void MainWindow::layerSelected(QAction * action) {
374 int i = 0;
375 for (auto a : layerSelectorGroup->actions()) {
376 if (action == a) {
377 preview->selectLayer(i);
378 return;
379 }
380 ++i;
381 }
382 }
383
384
385 void MainWindow::setToolFromKey() {
386 Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
387 if ((mods & Qt::ShiftModifier) && addGhostAction->isEnabled() && !previewArea->isDragging()) addGhostAction->setChecked(true);
388 else if ((mods & Qt::ControlModifier) && rmGhostAction->isEnabled() && !previewArea->isDragging()) rmGhostAction->setChecked(true);
389 else lastTool->setChecked(true);
390 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _HDRMERGEMAINWINDOW_H
23 #define _HDRMERGEMAINWINDOW_H
24
25 #include <list>
26 #include <QString>
27 #include <QAction>
28 #include <QLabel>
29 #include <QMainWindow>
30 #include <QMenu>
31 #include <QEvent>
32 #include <QToolBar>
33 #include <QSpinBox>
34 #include <QSlider>
35 #include <QStatusBar>
36 #include "ImageIO.hpp"
37
38
39 namespace hdrmerge {
40
41 class PreviewWidget;
42 class DraggableScrollArea;
43
44 class MainWindow : public QMainWindow {
45 public:
46 MainWindow();
47
48 void closeEvent(QCloseEvent * event);
49 void preload(const std::vector<QString> & o) {
50 preloadFiles = o;
51 }
52
53 public slots:
54 void setStatus(const QString & status) {
55 statusLabel->setText(status);
56 }
57 void showTemporaryStatus(const QString & status) {
58 statusBar->showMessage(status, 3000);
59 }
60 void setPixelStatus(int x, int y);
61
62 protected:
63 void keyPressEvent(QKeyEvent * event) { setToolFromKey(); }
64 void keyReleaseEvent(QKeyEvent * event) { setToolFromKey(); }
65
66 private slots:
67 void about();
68 void loadImages();
69 void saveResult();
70 void layerSelected(QAction * action);
71 void toolSelected(QAction * action) {
72 lastTool = action;
73 }
74
75 private:
76 void createWidgets();
77 void createActions();
78 void createMenus();
79 void createToolbars();
80 void createLayerSelector();
81 void setToolFromKey();
82
83 Q_OBJECT
84
85 QAction * loadImagesAction;
86 QAction * quitAction;
87 QAction * undoAction;
88 QAction * redoAction;
89 QAction * aboutAction;
90 QAction * mergeAction;
91
92 QAction * dragToolAction;
93 QAction * addGhostAction;
94 QAction * rmGhostAction;
95 QAction * lastTool;
96
97 QMenu * fileMenu;
98 QMenu * editMenu;
99 QMenu * helpMenu;
100
101 DraggableScrollArea * previewArea;
102 PreviewWidget * preview;
103 QSpinBox * radiusBox;
104 QSlider * radiusSlider;
105 QActionGroup * layerSelectorGroup;
106 QToolBar * layerSelector;
107 QSlider * exposureSlider;
108 QStatusBar * statusBar;
109 QLabel * statusLabel;
110
111 ImageIO io;
112 std::vector<QString> preloadFiles;
113 };
114
115 } // namespace hdrmerge
116
117 #endif // UI_HDRMERGEMAINWINDOW_H
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "PreviewWidget.hpp"
23 #include <QImage>
24 #include <QPainter>
25 #include <QFuture>
26 #include <QtConcurrent/QtConcurrent>
27 #include <QApplication>
28 #include <QBitmap>
29 #include <QAction>
30 #include "Log.hpp"
31 using namespace hdrmerge;
32
33
34 PreviewWidget::PreviewWidget(ImageStack & s, QWidget * parent) : QWidget(parent), stack(s),
35 width(0), height(0), flip(0), addPixels(false), rmPixels(false), layer(0), radius(5),
36 mouseX(0), mouseY(0), expMult(1.0), cancelRender(false) {
37 float g = 1.0f / 2.2f;
38 for (int i = 0; i < 65536; i++) {
39 gamma[i] = (int)std::floor(65536.0f * std::pow(i / 65536.0f, g)) >> 8;
40 }
41 setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored);
42 setMouseTracking(true);
43 }
44
45
46 void PreviewWidget::reload() {
47 layer = 0;
48 expMult = 1.0;
49 flip = stack.size() ? stack.getFlip() : 0;
50 if (flip == 5 || flip == 6) {
51 width = stack.getHeight();
52 height = stack.getWidth();
53 } else {
54 width = stack.getWidth();
55 height = stack.getHeight();
56 }
57 pixmap.reset();
58 resize(QSize(0, 0));
59 repaintAsync();
60 }
61
62
63 void PreviewWidget::repaintAsync() {
64 cancelRender = true;
65 currentRender.waitForFinished();
66 currentRender = QtConcurrent::run(this, &PreviewWidget::render, QRect(0, 0, width, height));
67 }
68
69
70 void PreviewWidget::rotate(int & x, int & y) const {
71 int tmp;
72 switch (flip) {
73 case 3:
74 x = width - 1 - x;
75 y = height - 1 - y;
76 break;
77 case 5:
78 tmp = x;
79 x = height - 1 - y;
80 y = tmp;
81 break;
82 case 6:
83 tmp = x;
84 x = y;
85 y = width - 1 - tmp;
86 break;
87 }
88 }
89
90
91 QSize PreviewWidget::sizeHint() const {
92 return pixmap.get() ? pixmap->size() : QSize(0, 0);
93 }
94
95
96 void PreviewWidget::paintEvent(QPaintEvent * event) {
97 if (pixmap.get()) {
98 QPainter painter(this);
99 painter.drawPixmap(0, 0, *pixmap);
100 if ((addPixels || rmPixels) && underMouse()) {
101 painter.drawPixmap(mouseX - radius, mouseY - radius, brush);
102 }
103 }
104 }
105
106
107 QRgb PreviewWidget::getColor(int layer, int v) {
108 int v70 = v*7/10;
109 switch (layer % 7) {
110 case 0:
111 return qRgb(v70, v, v70); break;
112 case 1:
113 return qRgb(v70, v70, v); break;
114 case 2:
115 return qRgb(v, v70, v70); break;
116 case 3:
117 return qRgb(v, v, v70); break;
118 case 4:
119 return qRgb(v, v70, v); break;
120 case 5:
121 return qRgb(v70, v, v); break;
122 default:
123 return qRgb(v, v, v); break;
124 }
125 }
126
127
128 QRgb PreviewWidget::rgb(int col, int row) const {
129 rotate(col, row);
130 int v = (int)stack.value(col, row) * expMult;
131 if (v < 0) v = 0;
132 else if (v > 65535) v = 65535;
133 return getColor(stack.getImageAt(col, row), gamma[v]);
134 }
135
136
137 void PreviewWidget::render(QRect zone) {
138 if (!stack.size()) return;
139 zone = zone.intersected(QRect(0, 0, width, height));
140 if (zone.isNull()) return;
141 cancelRender = false;
142 QImage image(zone.width(), zone.height(), QImage::Format_RGB32);
143 #pragma omp parallel for schedule(dynamic)
144 for (int row = zone.top(); row <= zone.bottom(); row++) {
145 QRgb * scanLine = reinterpret_cast<QRgb *>(image.scanLine(row - zone.top()));
146 for (int col = zone.left(); !cancelRender && col <= zone.right(); col++) {
147 *scanLine++ = rgb(col, row);
148 }
149 }
150 if (!cancelRender) {
151 QMetaObject::invokeMethod(this, "paintImage", Qt::AutoConnection,
152 Q_ARG(QPoint, zone.topLeft()), Q_ARG(const QImage &, image));
153 }
154 }
155
156
157 void PreviewWidget::paintImage(QPoint where, const QImage & image) {
158 if (!pixmap.get()) {
159 pixmap.reset(new QPixmap);
160 *pixmap = QPixmap::fromImage(image);
161 resize(pixmap->size());
162 } else {
163 QPainter painter(pixmap.get());
164 painter.drawImage(where, image);
165 }
166 update(where.x(), where.y(), image.width(), image.height());
167 }
168
169
170 void PreviewWidget::createBrush(bool plus) {
171 brush = QPixmap(radius*2 + 1, radius*2 + 1);
172 brush.fill(Qt::white);
173 {
174 QPainter painter(&brush);
175 painter.setPen(QPen(QColor(64, 64, 64), 2, Qt::SolidLine));
176 painter.drawEllipse(0, 0, radius*2, radius*2);
177 QPen dashed(QColor(128, 128, 128), 2, Qt::CustomDashLine);
178 dashed.setDashPattern(QVector<qreal>({3, 4}));
179 painter.setPen(dashed);
180 painter.drawEllipse(0, 0, radius*2, radius*2);
181 painter.drawLine(radius - 2, radius, radius + 2, radius);
182 if (plus)
183 painter.drawLine(radius, radius - 2, radius, radius + 2);
184 }
185 brush.setMask(brush.createMaskFromColor(Qt::white));
186 }
187
188
189 void PreviewWidget::setShowBrush() {
190 if (addPixels || rmPixels) {
191 createBrush(addPixels);
192 setCursor(QCursor(Qt::BlankCursor));
193 }
194 update();
195 }
196
197
198 void PreviewWidget::mouseEvent(QMouseEvent * event, bool pressed) {
199 int rx = mouseX = event->x(), ry = mouseY = event->y();
200 rotate(rx, ry);
201 if (rx >= 0 && rx < (int)stack.getWidth() && ry >= 0 && ry < (int)stack.getHeight())
202 emit pixelUnderMouse(rx, ry);
203 if ((event->buttons() & Qt::LeftButton) && (addPixels || rmPixels)) {
204 event->accept();
205 if (pressed) {
206 stack.getMask().startAction(addPixels, layer);
207 }
208 stack.getMask().editPixels(rx, ry, radius);
209 render(QRect(mouseX - radius, mouseY - radius, 2*radius + 1, 2*radius + 1));
210 } else {
211 event->ignore();
212 }
213 update();
214 }
215
216
217 void PreviewWidget::wheelEvent(QWheelEvent * event) {
218 Qt::KeyboardModifiers mods = QApplication::queryKeyboardModifiers();
219 if (mods & Qt::AltModifier) {
220 event->accept();
221 int step = event->delta() / 24;
222 if (step == 0) step = event->delta() > 0 ? 1 : -1;
223 setRadius(radius - step);
224 emit radiusChanged(radius);
225 } else {
226 event->ignore();
227 }
228 }
229
230
231 void PreviewWidget::undo() {
232 if (stack.getMask().canUndo()) {
233 QRect undoRect = stack.getMask().undo();
234 render(QRect(unrotate(undoRect.topLeft()), unrotate(undoRect.bottomRight())));
235 }
236 }
237
238
239 void PreviewWidget::redo() {
240 if (stack.getMask().canRedo()) {
241 QRect redoRect = stack.getMask().redo();
242 render(QRect(unrotate(redoRect.topLeft()), unrotate(redoRect.bottomRight())));
243 }
244 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _PREVIEWWIDGET_H_
23 #define _PREVIEWWIDGET_H_
24
25 #include <memory>
26 #include <list>
27 #include <qwidget.h>
28 #include <QPaintEvent>
29 #include <QFuture>
30 #include "ImageStack.hpp"
31
32 namespace hdrmerge {
33
34 class PreviewWidget : public QWidget {
35 public:
36 static const int maxRadius = 200;
37
38 PreviewWidget(ImageStack & s, QWidget * parent);
39 QSize sizeHint() const;
40
41 static QRgb getColor(int layer, int v);
42
43 public slots:
44 void reload();
45 void toggleAddPixelsTool(bool toggled) {
46 addPixels = toggled;
47 setShowBrush();
48 }
49 void toggleRmPixelsTool(bool toggled) {
50 rmPixels = toggled;
51 setShowBrush();
52 }
53 void selectLayer(int i) { layer = i; }
54 void setRadius(int r) {
55 radius = r;
56 if (radius < 0) radius = 0;
57 if (radius > maxRadius) radius = maxRadius;
58 setShowBrush();
59 }
60 void setExposureMultiplier(int e) {
61 if (stack.size() > 0) {
62 expMult = 1.0 + e * stack.getMaxExposure() / (stack.size() * 1000.0);
63 repaintAsync();
64 }
65 }
66 void undo();
67 void redo();
68
69 signals:
70 void radiusChanged(int r);
71 void pixelUnderMouse(int x, int y);
72
73 protected:
74 void paintEvent(QPaintEvent * event);
75 void mousePressEvent(QMouseEvent * event) { mouseEvent(event, true); }
76 void mouseMoveEvent(QMouseEvent * event) { mouseEvent(event, false); }
77 void mouseEvent(QMouseEvent * event, bool pressed);
78 void wheelEvent(QWheelEvent * event);
79 void enterEvent(QEvent * event) { update(); }
80 void leaveEvent(QEvent * event) { update(); }
81
82 private slots:
83 void paintImage(QPoint where, const QImage & image);
84
85 private:
86 Q_OBJECT
87
88 std::unique_ptr<QPixmap> pixmap;
89 ImageStack & stack;
90 size_t width, height;
91 int flip;
92 bool addPixels, rmPixels;
93 int layer;
94 int radius;
95 int mouseX, mouseY;
96 QPixmap brush;
97 double expMult;
98 QFuture<void> currentRender;
99 bool cancelRender;
100 uint8_t gamma[65536];
101
102 void render(QRect zone);
103 QRgb rgb(int col, int row) const;
104 void rotate(int & x, int & y) const;
105 QPoint unrotate(QPoint p) const {
106 int x = p.x(), y = p.y();
107 rotate(x, y); rotate(x, y); rotate(x, y);
108 return QPoint(x, y);
109 }
110 void createBrush(bool plus);
111 void setShowBrush();
112 void repaintAsync();
113 };
114
115 } // namespace hdrmerge
116
117 #endif // _PREVIEWWIDGET_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _PROGRESSINDICATOR_HPP
23 #define _PROGRESSINDICATOR_HPP
24
25 namespace hdrmerge {
26
27 class ProgressIndicator {
28 public:
29 virtual void advance(int percent, const char * message, const char * arg = nullptr) = 0;
30 };
31
32 } // namespace hdrmerge
33
34 #endif // _PROGRESSINDICATOR_HPP
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <iostream>
23 #include <cmath>
24 #include <algorithm>
25 #include <functional>
26 #include <QDateTime>
27 #include <QFileInfo>
28 #include <libraw.h>
29 #include <exiv2/exiv2.hpp>
30 #include "Log.hpp"
31 #include "RawParameters.hpp"
32 using namespace hdrmerge;
33 using namespace std;
34 using namespace std::placeholders;
35
36
37 RawParameters::RawParameters() : width(0), height(0), rawWidth(0), rawHeight(0), topMargin(0), leftMargin(0), max(0),
38 black(0), maxBlack(0), cblack{}, preMul{}, camMul{}, camXyz{}, rgbCam{}, isoSpeed(0.0), shutter(0.0), aperture(0.0), colors(0) {}
39
40
41 void RawParameters::loadCamXyzFromDng() {
42 // Try to load it from the DNG metadata
43 try {
44 const float d65_white[3] = { 0.950456f, 1.0f, 1.088754f };
45 double cc[4][4], xyz[] = { 1,1,1 };
46 for (int i = 0; i < 4; ++i) {
47 for (int j = 0; j < 4; ++j) {
48 cc[j][i] = i == j ? 1.0 : 0.0;
49 }
50 }
51 Exiv2::Image::AutoPtr src = Exiv2::ImageFactory::open(fileName.toLocal8Bit().constData());
52 src->readMetadata();
53 const Exiv2::ExifData & srcExif = src->exifData();
54
55 auto ccData = srcExif.findKey(Exiv2::ExifKey("Exif.Image.CameraCalibration1"));
56 if (ccData == srcExif.end()) {
57 ccData = srcExif.findKey(Exiv2::ExifKey("Exif.Image.CameraCalibration2"));
58 }
59 if (ccData != srcExif.end()) {
60 for (int i = 0; i < colors; ++i) {
61 for (int j = 0; j < colors; ++j) {
62 cc[i][j] = ccData->toFloat(i*colors + j);
63 }
64 }
65 }
66
67 auto xyzData = srcExif.findKey(Exiv2::ExifKey("Exif.Image.AsShotWhiteXY"));
68 if (xyzData != srcExif.end()) {
69 xyz[0] = xyzData->toFloat(0);
70 xyz[1] = xyzData->toFloat(1);
71 xyz[2] = 1.0 - xyz[0] - xyz[1];
72 for (int i = 0; i < 3; ++i) {
73 xyz[i] /= d65_white[i];
74 }
75 }
76
77 auto cmData = srcExif.findKey(Exiv2::ExifKey("Exif.Image.ColorMatrix1"));
78 if (cmData == srcExif.end()) {
79 cmData = srcExif.findKey(Exiv2::ExifKey("Exif.Image.ColorMatrix2"));
80 }
81 if (cmData != srcExif.end() && cmData->count() == 3*colors) {
82 for (int c = 0; c < colors; ++c) {
83 for (int i = 0; i < 3; ++i) {
84 camXyz[c][i] = 0.0;
85 for (int j = 0; j < colors; ++j) {
86 camXyz[c][i] += cc[c][j] * cmData->toFloat(j*3 + i) * xyz[i];
87 }
88 }
89 }
90 }
91 } catch (Exiv2::Error & e) {
92 Log::debug("Could not load camXyz values from metadata: ", e.what());
93 }
94 }
95
96
97 static void pseudoinverse(double (*in)[3], double (*out)[3], int size) {
98 double work[3][6], num;
99 int i, j, k;
100
101 for (i=0; i < 3; i++) {
102 for (j=0; j < 6; j++)
103 work[i][j] = j == i+3;
104 for (j=0; j < 3; j++)
105 for (k=0; k < size; k++)
106 work[i][j] += in[k][i] * in[k][j];
107 }
108 for (i=0; i < 3; i++) {
109 num = work[i][i];
110 for (j=0; j < 6; j++)
111 work[i][j] /= num;
112 for (k=0; k < 3; k++) {
113 if (k==i) continue;
114 num = work[k][i];
115 for (j=0; j < 6; j++)
116 work[k][j] -= work[i][j] * num;
117 }
118 }
119 for (i=0; i < size; i++)
120 for (j=0; j < 3; j++)
121 for (out[i][j]=k=0; k < 3; k++)
122 out[i][j] += work[j][k+3] * in[i][k];
123 }
124
125
126 void RawParameters::camXyzFromRgbCam() {
127 if (rgbCam[0][0]) {
128 // Calculate it from rgbCam
129 // FIXME: This is experimental, not sure at all if it is correct
130 const double rgbXyz[3][3] = { // The inverse of xyz_rgb
131 { 3.240481, -1.537152, -0.498536 },
132 { -0.969255, 1.875990, 0.041556 },
133 { 0.055647, -0.204041, 1.057311 }
134 };
135 double camRgb[4][3], rgbCamT[4][3];
136 for (int i = 0; i < 3; ++i) {
137 for (int j = 0; j < colors; ++j) {
138 rgbCamT[j][i] = rgbCam[i][j];
139 }
140 }
141 pseudoinverse(rgbCamT, camRgb, colors);
142 for (int i = 0; i < colors; ++i) {
143 for (int j = 0; j < 3; ++j) {
144 camRgb[i][j] /= preMul[i];
145 }
146 }
147 for (int i = 0; i < colors; ++i) {
148 for (int j = 0; j < 3; ++j) {
149 camXyz[i][j] = 0;
150 for (int k = 0; k < 3; ++k) {
151 camXyz[i][j] += camRgb[i][k] * rgbXyz[k][j];
152 }
153 }
154 }
155 Log::debug("camXyz values computed from rgbCam.");
156 for (int i = 0; i < colors; ++i) {
157 for (int j = 0; j < 3; ++j) {
158 Log::debugN(" ", camXyz[i][j]);
159 }
160 Log::debug();
161 }
162 }
163 }
164
165
166 void RawParameters::calculateCamXyz() {
167 // LibRaw does not create this matrix for DNG files!!!
168 loadCamXyzFromDng();
169 if (!camXyz[0][0]) {
170 camXyzFromRgbCam();
171 if (!camXyz[0][0]) {
172 // Identity matrix if we know nothing
173 for (int i = 0; i < colors; ++i) {
174 for (int j = 0; j < 3; ++j) {
175 camXyz[i][j] = i == j ? 1.0 : 0.0;
176 }
177 }
178 }
179 }
180 }
181
182
183 void RawParameters::fromLibRaw(LibRaw & rawData) {
184 auto & r = rawData.imgdata;
185 width = r.sizes.width;
186 height = r.sizes.height;
187 rawWidth = r.sizes.raw_width;
188 rawHeight = r.sizes.raw_height;
189 topMargin = r.sizes.top_margin;
190 leftMargin = r.sizes.left_margin;
191 auto fcol = std::bind(&LibRaw::fcol, &rawData, _1, _2);
192 FC.setPattern(r.idata.filters, fcol);
193 colors = r.idata.colors;
194 cdesc = r.idata.cdesc;
195 max = r.color.maximum;
196 black = r.color.black;
197 if(r.color.cblack[4] * r.color.cblack[5] == 0) {
198 copy_n(r.color.cblack, 4, cblack);
199 } else if (r.color.cblack[4] * r.color.cblack[5] == 4) {
200 for (int c = 0; c < 4; c++) {
201 cblack[FC(c / 2, c % 2)] = r.color.cblack[6 + c / 2 % r.color.cblack[4] * r.color.cblack[5] + c % 2 % r.color.cblack[5]];
202 }
203 }
204 if(r.idata.filters == 9) { //xtrans
205 for (int c = 0; c < 4; c++) {
206 cblack[c] = r.color.cblack[6];
207 }
208 }
209 adjustBlack();
210 copy_n(r.color.pre_mul, 4, preMul);
211 copy_n(r.color.cam_mul, 4, camMul);
212 if (camMul[0] == 0 || camMul[0] == -1) {
213 Log::debug("Invalid camera white balance: ", camMul[0], ' ', camMul[1], ' ', camMul[2], ' ', camMul[3]);
214 camMul[0] = 0;
215 }
216 copy_n((float *)r.color.rgb_cam, 4*3, (float *)rgbCam);
217 isoSpeed = r.other.iso_speed;
218 shutter = r.other.shutter;
219 aperture = r.other.aperture;
220 if(aperture <= 0.f || isinf(aperture) || isnan(aperture)) {
221 Log::debug("Invalid aperture: ", aperture, " replaced by aperture: f8");
222 aperture = 8;
223 }
224 maker = r.idata.make;
225 model = r.idata.model;
226 description = r.other.desc;
227 QDateTime dateTimeTmp = QDateTime::fromTime_t(r.other.timestamp);
228 QString dateTimeTmpText = dateTimeTmp.toString("yyyy:MM:dd hh:mm:ss");
229 dateTime = dateTimeTmpText.toLatin1().constData();
230 flip = r.sizes.flip;
231 switch ((flip + 3600) % 360) {
232 case 270: flip = 5; break;
233 case 180: flip = 3; break;
234 case 90: flip = 6; break;
235 }
236 switch (flip) {
237 case 0: tiffOrientation = 1; break;
238 case 3: tiffOrientation = 3; break;
239 case 5: tiffOrientation = 8; break;
240 case 6: tiffOrientation = 6; break;
241 default: tiffOrientation = 9; break;
242 }
243 copy_n((float *)r.color.cam_xyz, 3*4, (float *)camXyz);
244 if (!camXyz[0][0]) {
245 calculateCamXyz();
246 }
247 dumpInfo();
248 }
249
250
251 double RawParameters::logExp() const {
252 return std::log2(isoSpeed * shutter / (100.0 * aperture * aperture));
253 }
254
255
256 void RawParameters::adjustBlack() {
257 uint16_t minb = cblack[0] + black;
258 maxBlack = minb;
259 for (int i = 0; i < 4; ++i) {
260 cblack[i] += black;
261 if (minb > cblack[i]) {
262 minb = cblack[i];
263 }
264 if (maxBlack < cblack[i])
265 maxBlack = cblack[i];
266 }
267 black = minb;
268 }
269
270
271 void RawParameters::adjustWhite(const Array2D<uint16_t> & image) {
272 if (camMul[0] == 0) {
273 autoWB(image);
274 } else if (camMul[1] == 0) {
275 camMul[1] = 1;
276 }
277 if (colors == 3) {
278 camMul[3] = camMul[1];
279 } else if (camMul[3] == 0) {
280 camMul[3] = 1;
281 }
282 float min = camMul[0];
283 for (int c = 1; c < 4; ++c) {
284 min = std::min(min, camMul[c]);
285 }
286 for (int c = 0; c < 4; ++c) {
287 camMul[c] /= min;
288 }
289 Log::debug("Adjusted white balance: ", camMul[0], ' ', camMul[1], ' ', camMul[2], ' ', camMul[3]);
290 }
291
292
293 void RawParameters::autoWB(const Array2D<uint16_t> & image) {
294 Timer t("AutoWB");
295 double dsum[4] = { 0.0, 0.0, 0.0, 0.0 };
296 size_t dcount[4] = { 0, 0, 0, 0 };
297 for (size_t row = 0; row < image.getHeight(); row += 8) {
298 for (size_t col = 0; col < image.getWidth() ; col += 8) {
299 double sum[4] = { 0.0, 0.0, 0.0, 0.0 };
300 size_t count[4] = { 0, 0, 0, 0 };
301 size_t ymax = std::min(row + 8, image.getHeight());
302 size_t xmax = std::min(col + 8, image.getWidth());
303 bool skipBlock = false;
304 for (size_t y = row; y < ymax && !skipBlock; y++) {
305 for (size_t x = col; x < xmax; x++) {
306 int c = FC(x, y);
307 uint16_t val = image(x, y);
308 if (val > max - 25) {
309 skipBlock = true;
310 break;
311 }
312 sum[c] += val;
313 count[c]++;
314 }
315 }
316 if (!skipBlock) {
317 for (int c = 0; c < 4; ++c) {
318 dsum[c] += sum[c];
319 dcount[c] += count[c];
320 }
321 }
322 }
323 }
324 for (int c = 0; c < 4; ++c) {
325 if (dsum[c] > 0.0) {
326 camMul[c] = dcount[c] / dsum[c];
327 } else {
328 copy_n(preMul, 4, camMul);
329 break;
330 }
331 }
332 }
333
334
335 void RawParameters::dumpInfo() const {
336 Log::debugN(QFileInfo(fileName).fileName(), ": ", width, 'x', height, " (", rawWidth, 'x', rawHeight, '+', leftMargin, '+', topMargin);
337 Log::debug(", by ", maker, ' ' , model, ", ", isoSpeed, "ISO 1/", (1.0/shutter), "sec f", aperture, " EV:", logExp());
338 Log::debugN(hex, FC.getFilters(), dec, ' ', cdesc, ", sat ", max, ", black ", black, ", flip ", flip);
339 Log::debugN(", wb: ", camMul[0], ' ', camMul[1], ' ', camMul[2], ' ', camMul[3]);
340 Log::debug(", cblack: ", cblack[0], ' ', cblack[1], ' ', cblack[2], ' ', cblack[3]);
341 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #ifndef _RAWPARAMETERS_H_
23 #define _RAWPARAMETERS_H_
24
25 #include <QString>
26 #include "Array2D.hpp"
27 #include "CFAPattern.hpp"
28
29 class LibRaw;
30
31 namespace hdrmerge {
32
33 class RawParameters {
34 public:
35 RawParameters();
36 RawParameters(const QString & f) : RawParameters() {
37 fileName = f;
38 }
39 virtual ~RawParameters() {}
40
41 void fromLibRaw(LibRaw & rawData);
42
43 bool isSameFormat(const RawParameters & r) const {
44 return width == r.width && height == r.height && FC == r.FC && cdesc == r.cdesc;
45 }
46 double logExp() const;
47 void dumpInfo() const;
48 uint16_t blackAt(int x, int y) const {
49 return cblack[FC(x, y)];
50 }
51 bool hasBlack() const {
52 return black || cblack[0] || cblack[1] || cblack[2] || cblack[3];
53 }
54 float whiteMultAt(int x, int y) const {
55 return camMul[FC(x, y)];
56 }
57 void adjustWhite(const Array2D<uint16_t> & image);
58 void autoWB(const Array2D<uint16_t> & image);
59 bool canAlign() const { return FC.canAlign(); }
60
61 QString fileName;
62 size_t width, height;
63 size_t rawWidth, rawHeight, topMargin, leftMargin;
64 std::string cdesc;
65 CFAPattern FC;
66 uint16_t max;
67 uint16_t black;
68 uint16_t maxBlack;
69 uint16_t cblack[4];
70 float preMul[4];
71 float camMul[4];
72 float camXyz[4][3];
73 float rgbCam[3][4];
74 float isoSpeed;
75 float shutter;
76 float aperture;
77 std::string maker, model, description, dateTime;
78 int colors;
79 int flip;
80 int tiffOrientation;
81
82 private:
83 void adjustBlack();
84 void calculateCamXyz();
85 void loadCamXyzFromDng();
86 void camXyzFromRgbCam();
87 };
88
89 } // namespace hdrmerge
90
91 #endif // _RAWPARAMETERS_H_
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <algorithm>
23 #include "TiffDirectory.hpp"
24 using namespace hdrmerge;
25
26 void IFD::addEntry(uint16_t tag, uint16_t type, uint32_t count, const void * data) {
27 uint32_t newSize = entryData.size();
28 DirEntry * entry = &(*entries.insert(entries.end(), DirEntry({tag, type, count, newSize})));
29 uint32_t dataSize = entry->dataSize();
30 if (dataSize > 4) {
31 newSize += dataSize;
32 if (newSize & 1)
33 ++newSize;
34 entryData.resize(newSize);
35 }
36 setValue(entry, data);
37 }
38
39
40 void IFD::setValue(DirEntry * entry, const void * data) {
41 const uint8_t * castedData = reinterpret_cast<const uint8_t *>(data);
42 size_t dataSize = entry->dataSize();
43 if (dataSize > 4) {
44 std::copy_n(castedData, dataSize, &entryData[entry->offset]);
45 } else {
46 std::copy_n(castedData, dataSize, (uint8_t *)&entry->offset);
47 }
48 }
49
50
51 void TiffHeader::write(uint8_t * buffer, size_t & pos) {
52 pos = std::copy_n((uint8_t *)this, 8, &buffer[pos]) - buffer;
53 }
54
55
56 void IFD::write(uint8_t * buffer, size_t & pos, bool hasNext) {
57 uint16_t numEntries = entries.size();
58 uint32_t offsetData = (uint32_t)pos + 12*numEntries + 6;
59 sort(entries.begin(), entries.end());
60 uint32_t offsetNext = hasNext ? offsetData + entryData.size() : 0;
61 for (auto & entry : entries) {
62 if (entry.dataSize() > 4) {
63 entry.offset += offsetData;
64 }
65 }
66 uint8_t * p = &buffer[pos];
67 p = std::copy_n((uint8_t *)&numEntries, 2, p);
68 p = std::copy_n((uint8_t *)&entries[0], 12*numEntries, p);
69 p = std::copy_n((uint8_t *)&offsetNext, 4, p);
70 p = std::copy_n((uint8_t *)&entryData[0], entryData.size(), p);
71 pos = p - buffer;
72 }
73
74
75 size_t IFD::length() const {
76 return 6 + 12*entries.size() + entryData.size();
77 }
78
79
80 IFD::DirEntry * IFD::getEntry(uint16_t tag) {
81 auto it = entries.begin();
82 while (it != entries.end() && it->tag != tag) it++;
83 return it == entries.end() ? nullptr : &(*it);
84 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <vector>
23 #include <string>
24
25 #ifndef _TIFFDIRECTORY_HPP_
26 #define _TIFFDIRECTORY_HPP_
27
28 namespace hdrmerge {
29
30 struct TiffHeader {
31 union {
32 uint32_t endian;
33 struct {
34 uint16_t endian;
35 uint16_t magic;
36 } sep;
37 };
38 uint32_t offset;
39 // It sets the first two bytes to their correct value, given the architecture
40 TiffHeader() : endian(0x4D4D4949), offset(8) { sep.magic = 42; }
41 void write(uint8_t * buffer, size_t & pos);
42 };
43
44 class IFD {
45 public:
46 enum {
47 BYTE = 1,
48 ASCII,
49 SHORT,
50 LONG,
51 RATIONAL,
52 SBYTE,
53 UNDEFINED,
54 SSHORT,
55 SLONG,
56 SRATIONAL,
57 FLOAT,
58 DOUBLE
59 } Type;
60
61 template <typename T> void addEntry(uint16_t tag, uint16_t type, const T & value) {
62 DirEntry * entry = &(*entries.insert(entries.end(), DirEntry({tag, type, 1, 0})));
63 setValue(entry, value);
64 }
65 template <typename T> void setValue(uint16_t tag, const T * value) {
66 DirEntry * entry = getEntry(tag);
67 if (entry) {
68 setValue(entry, (const void *)value);
69 }
70 }
71 template <typename T> void setValue(uint16_t tag, const T & value) {
72 DirEntry * entry = getEntry(tag);
73 if (entry) {
74 setValue(entry, value);
75 }
76 }
77 void addEntry(uint16_t tag, uint16_t type, uint32_t count, const void * data);
78 void addEntry(uint16_t tag, const std::string &str) {
79 addEntry(tag, ASCII, str.length() + 1, str.c_str());
80 }
81 void write(uint8_t * buffer, size_t & pos, bool hasNext);
82 size_t length() const;
83
84 private:
85 struct DirEntry {
86 uint16_t tag;
87 uint16_t type;
88 uint32_t count;
89 uint32_t offset;
90 int bytesPerValue() const {
91 static int bytesPerValue[12] =
92 { 1, 1, 2, 4, 8, 1, 1, 2, 4, 8, 4, 8 };
93 return bytesPerValue[type - 1];
94 }
95 uint32_t dataSize() const {
96 return count * bytesPerValue();
97 }
98 bool operator<(const DirEntry & r) const {
99 return tag < r.tag;
100 }
101 };
102
103 std::vector<DirEntry> entries;
104 std::vector<uint8_t> entryData;
105
106 DirEntry * getEntry(uint16_t tag);
107 void setValue(DirEntry * entry, const void * data);
108 template <typename T> void setValue(DirEntry * entry, const T & value) {
109 if (entry->dataSize() > 4) {
110 setValue(entry, (const void *)&value);
111 } else {
112 union {
113 float floatValue;
114 uint32_t longValue;
115 uint16_t shortValue;
116 uint8_t byteValue;
117 } u;
118 switch (entry->type) {
119 case BYTE: case ASCII: case SBYTE: case UNDEFINED:
120 u.byteValue = (uint8_t) value; break;
121 case SHORT: case SSHORT:
122 u.shortValue = (uint16_t) value; break;
123 case LONG: case SLONG:
124 u.longValue = (uint32_t)value; break;
125 case FLOAT:
126 u.floatValue = (float)value; break;
127 }
128 entry->offset = u.longValue;
129 }
130 }
131 };
132
133 } // namespace hdrmerge
134
135 #endif // _TIFFDIRECTORY_HPP_
0 // The configured options and settings for STaRS
1 #define HDRMERGE_VERSION_MAJOR @HDRMERGE_VERSION_MAJOR@
2 #define HDRMERGE_VERSION_MINOR @HDRMERGE_VERSION_MINOR@
3 #define HDRMERGE_VERSION_REV @HDRMERGE_VERSION_REV@
4 #define HDRMERGE_VERSION_STRING "v@HDRMERGE_VERSION@"
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <exiv2/error.hpp>
23 #include "Launcher.hpp"
24
25 int main(int argc, char * argv[]) {
26 Exiv2::LogMsg::setLevel(Exiv2::LogMsg::mute);
27 hdrmerge::Launcher launcher(argc, argv);
28 return launcher.run();
29 }
0 include_directories(${Boost_INCLUDE_DIRS})
1
2 set(
3 test_sources
4 testMain.cpp
5 testImageStack.cpp
6 testBitmap.cpp
7 testHistogram.cpp
8 testBoxBlur.cpp
9 testArray2D.cpp
10 testDngFloatWriter.cpp
11 )
12
13 #add_executable(hdrmerge-test ${test_sources} $<TARGET_OBJECTS:hdrmerge-objects> $<TARGET_OBJECTS:hdrmerge-gui-objects>)
14 add_executable(hdrmerge-test
15 ${test_sources}
16 )
17
18 if(APPLE)
19 target_link_libraries(hdrmerge-test ${hdrmerge_libs} alglib-objects ${Boost_LIBRARIES} Qt5::Widgets)
20 else()
21 target_link_libraries(hdrmerge-test ${hdrmerge_libs} ${Boost_LIBRARIES} Qt5::Widgets)
22 endif()
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <cstdlib>
23 #include <QImage>
24 #include "../src/RawParameters.hpp"
25 #include "../src/Array2D.hpp"
26 #include "../src/Log.hpp"
27
28 namespace hdrmerge {
29
30 class SampleImage : public Array2D<uint16_t> {
31 public:
32 RawParameters params;
33 SampleImage() : Array2D<uint16_t>() {}
34 SampleImage(const std::string & f) : Array2D<uint16_t>() {
35 QImage image;
36 if (!image.load(f.c_str())) {
37 Log::msg(Log::DEBUG, "Imposible to load sample image ", f);
38 return;
39 }
40 Log::msg(Log::DEBUG, "Loaded sample image ", f, " with format ", image.format());
41 resize(image.width(), image.height());
42 const uchar * data = image.constBits();
43 int min = 255, max = 0;
44 for (size_t i = 0; i < width * height; ++i) {
45 (*this)[i] = data[i];
46 if (min > data[i]) min = data[i];
47 if (max < data[i]) max = data[i];
48 }
49 Log::msg(Log::DEBUG, "Data in range ", min, " - ", max);
50 params.width = params.rawWidth = width;
51 params.height = height;
52 params.max = 255;
53 params.FC.setPattern(0x4b4b4b4b, (uint8_t (*)(int, int))0);
54 }
55
56 void save(const std::string & f) {
57 QImage image(width, height, QImage::Format_RGB32);
58 QRgb * data = reinterpret_cast<QRgb *>(image.bits());
59 for (size_t i = 0; i < width * height; ++i) {
60 int v = (*this)[i];
61 data[i] = qRgb(v, v, v);
62 }
63 image.save(f.c_str());
64 }
65 };
66
67 } // namespace hdrmerge
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
Binary diff not shown
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "../src/Array2D.hpp"
23 #include <boost/test/unit_test.hpp>
24 using namespace hdrmerge;
25 using namespace std;
26
27
28 struct Array2DFixtureEmpty {
29 Array2D<float> a;
30 };
31
32
33 BOOST_FIXTURE_TEST_CASE(array2d_create, Array2DFixtureEmpty) {
34 BOOST_CHECK_EQUAL(a.getWidth(), 0);
35 BOOST_CHECK_EQUAL(a.getHeight(), 0);
36 BOOST_CHECK_EQUAL(a.getDeltaX(), 0);
37 BOOST_CHECK_EQUAL(a.getDeltaY(), 0);
38 }
39
40
41 BOOST_FIXTURE_TEST_CASE(array2d_resize, Array2DFixtureEmpty) {
42 a.resize(100, 10);
43 BOOST_CHECK_EQUAL(a.getWidth(), 100);
44 BOOST_CHECK_EQUAL(a.getHeight(), 10);
45 BOOST_CHECK_EQUAL(a.getDeltaX(), 0);
46 BOOST_CHECK_EQUAL(a.getDeltaY(), 0);
47 }
48
49
50 struct Array2DFixture100x10 {
51 Array2D<float> a;
52 Array2DFixture100x10() : a(100, 10) {}
53 };
54
55
56 BOOST_FIXTURE_TEST_CASE(array2d_create_with_size, Array2DFixture100x10) {
57 BOOST_CHECK_EQUAL(a.getWidth(), 100);
58 BOOST_CHECK_EQUAL(a.getHeight(), 10);
59 BOOST_CHECK_EQUAL(a.getDeltaX(), 0);
60 BOOST_CHECK_EQUAL(a.getDeltaY(), 0);
61 }
62
63
64 BOOST_FIXTURE_TEST_CASE(array2d_displace, Array2DFixture100x10) {
65 a.displace(5, -8);
66 BOOST_CHECK_EQUAL(a.getWidth(), 100);
67 BOOST_CHECK_EQUAL(a.getHeight(), 10);
68 BOOST_CHECK_EQUAL(a.getDeltaX(), 5);
69 BOOST_CHECK_EQUAL(a.getDeltaY(), -8);
70 BOOST_CHECK(!a.contains(4, -4));
71 BOOST_CHECK(!a.contains(4, 1));
72 BOOST_CHECK(!a.contains(20, 5));
73 BOOST_CHECK(!a.contains(103, 5));
74 BOOST_CHECK(a.contains(103, 1));
75 BOOST_CHECK(a.contains(20, -4));
76 }
77
78
79 BOOST_FIXTURE_TEST_CASE(array2d_access, Array2DFixture100x10) {
80 a[0] = -1.0;
81 BOOST_CHECK_EQUAL(a[0], -1.0);
82 a(2, 3) = 3.5;
83 BOOST_CHECK_EQUAL(a(2, 3), 3.5);
84 a.displace(-2, -3);
85 BOOST_CHECK_EQUAL(a[0], -1.0);
86 BOOST_CHECK_NE(a(2, 3), 3.5);
87 BOOST_CHECK_EQUAL(a(0, 0), 3.5);
88 }
89
90
91 BOOST_FIXTURE_TEST_CASE(array2d_copy, Array2DFixture100x10) {
92 a[0] = -1.0;
93 a(2, 3) = 3.5;
94 a.displace(-2, -3);
95 Array2D<double> b(a);
96 BOOST_CHECK_EQUAL(b[0], -1.0);
97 BOOST_CHECK_NE(b(2, 3), 3.5);
98 BOOST_CHECK_EQUAL(b(0, 0), 3.5);
99 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "../src/Bitmap.hpp"
23 #include <boost/test/unit_test.hpp>
24 using namespace hdrmerge;
25 using namespace std;
26
27 BOOST_AUTO_TEST_CASE(bitmap_create) {
28 Bitmap b(100, 200);
29 b.reset();
30 b.position(20, 30).set();
31 b.position(45, 81).set();
32 b.position(99, 199).set();
33 BOOST_CHECK(b.position(45, 81).get());
34 BOOST_CHECK_EQUAL(b.count(), 3);
35 b.position(20, 30).reset();
36 BOOST_CHECK_EQUAL(b.count(), 2);
37 }
38
39 static bool checkZeroArea(const Bitmap & b, int i0, int i1, int j0, int j1) {
40 for (int i = i0; i < i1; ++i) {
41 for (int j = j0; j < j1; ++j) {
42 if (b.position(i, j).get()) return false;
43 }
44 }
45 return true;
46 }
47
48 BOOST_AUTO_TEST_CASE(bitmap_shift) {
49 Bitmap b(100, 100);
50 for (Bitmap::iterator p = b.position(0, 0); p != b.end(); ++p) {
51 p.set();
52 }
53 b.position(49, 49).reset(); b.position(49, 50).reset(); b.position(50, 49).reset(); b.position(50, 50).reset();
54 Bitmap s(100, 100);
55 s.shift(b, 35, 35);
56 BOOST_CHECK_MESSAGE(checkZeroArea(s, 0, 35, 0, 100), s.dumpInfo());
57 BOOST_CHECK_MESSAGE(checkZeroArea(s, 0, 100, 0, 35), s.dumpInfo());
58 BOOST_CHECK_MESSAGE(checkZeroArea(s, 84, 86, 84, 86), s.dumpInfo());
59 BOOST_CHECK_MESSAGE(s.count() == 4221, s.dumpInfo());
60 s.shift(b, -35, 35);
61 BOOST_CHECK_MESSAGE(checkZeroArea(s, 65, 100, 0, 100), s.dumpInfo());
62 BOOST_CHECK_MESSAGE(checkZeroArea(s, 0, 100, 0, 35), s.dumpInfo());
63 BOOST_CHECK_MESSAGE(checkZeroArea(s, 14, 16, 84, 86), s.dumpInfo());
64 BOOST_CHECK_MESSAGE(s.count() == 4221, s.dumpInfo());
65 s.shift(b, 35, -35);
66 BOOST_CHECK_MESSAGE(checkZeroArea(s, 0, 35, 0, 100), s.dumpInfo());
67 BOOST_CHECK_MESSAGE(checkZeroArea(s, 0, 100, 65, 100), s.dumpInfo());
68 BOOST_CHECK_MESSAGE(checkZeroArea(s, 84, 86, 14, 16), s.dumpInfo());
69 BOOST_CHECK_MESSAGE(s.count() == 4221, s.dumpInfo());
70 s.shift(b, -35, -35);
71 BOOST_CHECK_MESSAGE(checkZeroArea(s, 65, 100, 0, 100), s.dumpInfo());
72 BOOST_CHECK_MESSAGE(checkZeroArea(s, 0, 100, 65, 100), s.dumpInfo());
73 BOOST_CHECK_MESSAGE(checkZeroArea(s, 14, 16, 14, 16), s.dumpInfo());
74 BOOST_CHECK_MESSAGE(s.count() == 4221, s.dumpInfo());
75 }
76
77 BOOST_AUTO_TEST_CASE(bitmap_xor) {
78 Bitmap b1(10, 10), b2(10, 10);
79 b1.reset();
80 b2.reset();
81 for (int i : { 1, 3, 4, 7, 8 }) {
82 b1.position(i, 3).set();
83 b2.position(i, 3).set();
84 }
85 b1.position(1, 5).set();
86 b1.position(3, 7).set();
87 b2.position(6, 6).set();
88 b2.bitwiseXor(b1);
89 BOOST_CHECK_EQUAL(b2.count(), 3);
90 BOOST_CHECK(b2.position(1, 5).get());
91 BOOST_CHECK(b2.position(3, 7).get());
92 BOOST_CHECK(b2.position(6, 6).get());
93 }
94
95 BOOST_AUTO_TEST_CASE(bitmap_and) {
96 Bitmap b1(10, 10), b2(10, 10);
97 b1.reset();
98 b2.reset();
99 for (int i : { 1, 3, 4, 7, 8 }) {
100 b1.position(i, 3).set();
101 b2.position(i, 3).set();
102 }
103 b1.position(1, 5).set();
104 b1.position(3, 7).set();
105 b2.position(6, 6).set();
106 b2.bitwiseAnd(b1);
107 BOOST_CHECK_EQUAL(b2.count(), 5);
108 for (int i : { 1, 3, 4, 7, 8 }) {
109 BOOST_CHECK(b2.position(i, 3).get());
110 }
111 }
112
113 BOOST_AUTO_TEST_CASE(bitmap_mtb) {
114 uint16_t data[] = { 1, 2, 3, 4, 3, 4, 5, 2, 3, 1, 4, 2, 5, 2, 3, 1, 2, 2, 2, 4, 5 };
115 Bitmap b(3, 7);
116 b.mtb(data, 3);
117 BOOST_CHECK_EQUAL(b.count(), 7);
118 BOOST_CHECK(b.position(0, 1).get());
119 BOOST_CHECK(b.position(2, 1).get());
120 BOOST_CHECK(b.position(0, 2).get());
121 BOOST_CHECK(b.position(1, 3).get());
122 BOOST_CHECK(b.position(0, 4).get());
123 BOOST_CHECK(b.position(1, 6).get());
124 BOOST_CHECK(b.position(2, 6).get());
125 }
126
127 BOOST_AUTO_TEST_CASE(bitmap_exclusion) {
128 uint16_t data[] = { 1, 2, 3, 4, 3, 4, 5, 2, 3, 1, 4, 3, 5, 2, 3, 1, 3, 4, 2, 4, 5 };
129 Bitmap b(3, 7);
130 b.exclusion(data, 3, 1);
131 BOOST_CHECK_EQUAL(b.count(), 10);
132 BOOST_CHECK(b.position(0, 0).get());
133 BOOST_CHECK(b.position(1, 0).get());
134 BOOST_CHECK(b.position(0, 2).get());
135 BOOST_CHECK(b.position(1, 2).get());
136 BOOST_CHECK(b.position(0, 3).get());
137 BOOST_CHECK(b.position(0, 4).get());
138 BOOST_CHECK(b.position(1, 4).get());
139 BOOST_CHECK(b.position(0, 5).get());
140 BOOST_CHECK(b.position(0, 6).get());
141 BOOST_CHECK(b.position(2, 6).get());
142 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <string>
23 #include <cmath>
24 #include <QDir>
25 #include "../src/BoxBlur.hpp"
26 #include "SampleImage.hpp"
27 #include "../src/Log.hpp"
28 #include <boost/test/unit_test.hpp>
29 using namespace hdrmerge;
30 using namespace std;
31
32 BOOST_AUTO_TEST_CASE(testBoxBlur) {
33 SampleImage image("test/testMap.png"), result;
34 for (int radius = 1; radius < 25; radius += 3) {
35 BoxBlur map = image;
36 string title = string("Blur with radius ") + to_string(radius);
37 measureTime(title.c_str(), [&] () {map.blur(radius);});
38 (Array2D<uint16_t> &)result = (Array2D<float> &)map;
39 string fileName = QDir::tempPath().toStdString() + "/testMapblur_" + to_string(radius) + ".png";
40 result.save(fileName);
41 }
42 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <string>
23 #include <cmath>
24 #include <QDir>
25 #include "../src/ImageIO.hpp"
26 #include "../src/Log.hpp"
27 #include "../src/DngFloatWriter.hpp"
28 #include <boost/test/unit_test.hpp>
29 using namespace hdrmerge;
30 using namespace std;
31
32
33 BOOST_AUTO_TEST_CASE(testDngFloatWriter) {
34 RawParameters params("test/sample1.dng");
35 Image image = ImageIO::loadRawImage(params);
36 int imageWidth = image.getWidth();
37 float max = 0;
38 for (auto i : image) {
39 if (max < i) max = i;
40 }
41 int bps = 16, width = imageWidth;
42 // for (int bps : {16, 24, 32}) {
43 // for (int width : {0, imageWidth / 2, imageWidth}) {
44 Array2D<float> result(image.getWidth(), image.getHeight());
45 for (size_t i = 0; i < image.getWidth()*image.getHeight(); ++i)
46 result[i] = image[i] / max;
47 QImage preview = ImageIO::renderPreview(result, params, 1.0);
48 DngFloatWriter writer;
49 writer.setBitsPerSample(bps);
50 writer.setPreviewWidth(width);
51 writer.setPreview(preview);
52 QString fileName = QDir::tempPath() + QString("/testDngFloat_%1_%2.dng").arg(bps).arg(width);
53 string title = string("Save Dng Float with ") + to_string(bps) + " bps and preview width " + to_string(width);
54 measureTime(title.c_str(), [&] () {
55 writer.write(std::move(result), params, fileName);
56 });
57 // }
58 // }
59 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include "../src/Histogram.hpp"
23 #include <boost/test/unit_test.hpp>
24 using namespace hdrmerge;
25 using namespace std;
26
27 namespace hdrmerge {
28
29 struct HistogramFixture {
30 Histogram h;
31 uint16_t values[14];
32
33 HistogramFixture() : values{8, 3, 6, 4, 5, 2, 1, 7, 9, 3, 2, 7, 9, 9} {}
34 ~HistogramFixture() {
35 BOOST_CHECK_EQUAL(h.getNumSamples(), 14);
36 BOOST_CHECK_EQUAL(h.getPercentile(0.5), 5);
37 BOOST_CHECK_EQUAL(h.getPercentile(0.75), 7);
38 BOOST_CHECK_EQUAL(h.getPercentile(0.2), 2);
39 BOOST_CHECK_EQUAL(h.getFraction(2), 3.0/14.0);
40 }
41 };
42
43
44 BOOST_FIXTURE_TEST_CASE(Histogram_addValue, HistogramFixture) {
45 for (uint16_t i : values) {
46 h.addValue(i);
47 }
48 }
49
50
51 BOOST_FIXTURE_TEST_CASE(Histogram_ctr, HistogramFixture) {
52 h = Histogram(values, values + 14);
53 }
54
55 } // namespace hdrmerge
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #include <iostream>
23 #include <QDir>
24 #include "../src/ImageIO.hpp"
25 #include "SampleImage.hpp"
26 #include "../src/Log.hpp"
27 #include <boost/test/unit_test.hpp>
28 #include <boost/config/no_tr1/complex.hpp>
29 using namespace hdrmerge;
30 using namespace std;
31
32
33 static const char * image1 = "test/sample1.dng";
34 static const char * image2 = "test/sample2.dng";
35 static const char * image3 = "test/sample3.dng";
36 // Sample images with deviation
37 static const char * sample1 = "test/sample1.png"; // (0, 0)
38 static const char * sample2 = "test/sample2.png"; // (20, 32)
39 static const char * sample3 = "test/sample3.png"; // (38, 26)
40 static const char * sample4 = "test/sample4.png"; // (34, -4)
41
42 struct ImageIOFixture {
43 ImageIO io;
44 Image e1, e2, e3, e4;
45 };
46
47
48 BOOST_FIXTURE_TEST_CASE(image_load, ImageIOFixture) {
49 RawParameters m1(image1), m2(image2), m3(sample1);
50 e1 = io.loadRawImage(m1);
51 BOOST_REQUIRE(e1.good());
52 e2 = io.loadRawImage(m2);
53 BOOST_REQUIRE(e2.good());
54 BOOST_REQUIRE(m2.isSameFormat(m1));
55 e3 = io.loadRawImage(m3);
56 BOOST_CHECK(!e3.good());
57 }
58
59
60 BOOST_FIXTURE_TEST_CASE(image_align, ImageIOFixture) {
61 SampleImage si1(sample1);
62 SampleImage si2(sample2);
63 SampleImage si3(sample3);
64 SampleImage si4(sample4);
65 e1 = Image(si1.begin(), si1.params);
66 e2 = Image(si2.begin(), si2.params);
67 e3 = Image(si3.begin(), si3.params);
68 e4 = Image(si4.begin(), si4.params);
69 BOOST_REQUIRE(e1.good());
70 BOOST_REQUIRE(e2.good());
71 BOOST_REQUIRE(e3.good());
72 BOOST_REQUIRE(e4.good());
73 e1.preScale(); e1.setSaturationThreshold(254);
74 e2.preScale(); e2.setSaturationThreshold(254);
75 e3.preScale(); e3.setSaturationThreshold(254);
76 e4.preScale(); e4.setSaturationThreshold(254);
77 e2.alignWith(e1);
78 e3.alignWith(e1);
79 e4.alignWith(e1);
80 BOOST_CHECK_EQUAL(e1.getDeltaX(), 0);
81 BOOST_CHECK_EQUAL(e1.getDeltaY(), 0);
82 BOOST_CHECK_EQUAL(e2.getDeltaX(), 20);
83 BOOST_CHECK_EQUAL(e2.getDeltaY(), 32);
84 BOOST_CHECK_EQUAL(e3.getDeltaX(), 38);
85 BOOST_CHECK_EQUAL(e3.getDeltaY(), 26);
86 BOOST_CHECK_EQUAL(e4.getDeltaX(), 32);
87 BOOST_CHECK_EQUAL(e4.getDeltaY(), -4);
88 // Align in chain, should remain aligned with e1
89 e3.alignWith(e2);
90 e4.alignWith(e3);
91 e3.displace(e2.getDeltaX(), e2.getDeltaY());
92 BOOST_CHECK_EQUAL(e3.getDeltaX(), 38);
93 BOOST_CHECK_EQUAL(e3.getDeltaY(), 26);
94 e4.displace(e3.getDeltaX(), e3.getDeltaY());
95 BOOST_CHECK_EQUAL(e4.getDeltaX(), 32);
96 BOOST_CHECK_EQUAL(e4.getDeltaY(), -4);
97 }
98
99 BOOST_AUTO_TEST_CASE(stack_load) {
100 ImageStack images;
101 BOOST_CHECK_EQUAL(images.size(), 0);
102 RawParameters m1(image2), m2(image1);
103 Image e1(ImageIO::loadRawImage(m1)), e2(ImageIO::loadRawImage(m2));
104 BOOST_REQUIRE(e1.good());
105 BOOST_REQUIRE(e2.good());
106 images.addImage(std::move(e1));
107 BOOST_CHECK_EQUAL(images.addImage(std::move(e2)), 0);
108 BOOST_CHECK_EQUAL(images.size(), 2);
109 BOOST_CHECK_EQUAL(images.getImage(0).getWidth(), images.getWidth());
110 BOOST_CHECK_EQUAL(images.getImage(0).getHeight(), images.getHeight());
111 }
112
113 BOOST_AUTO_TEST_CASE(stack_align) {
114 ImageStack images;
115 SampleImage si1(sample1);
116 SampleImage si2(sample2);
117 SampleImage si3(sample3);
118 SampleImage si4(sample4);
119 Image e1(si1.begin(), si1.params),
120 e2(si2.begin(), si2.params),
121 e3(si3.begin(), si3.params),
122 e4(si4.begin(), si4.params);
123 BOOST_REQUIRE(e1.good());
124 BOOST_REQUIRE(e2.good());
125 BOOST_REQUIRE(e3.good());
126 BOOST_REQUIRE(e4.good());
127 e1.setSaturationThreshold(254);
128 e2.setSaturationThreshold(254);
129 e3.setSaturationThreshold(254);
130 e4.setSaturationThreshold(254);
131 images.addImage(std::move(e1));
132 images.addImage(std::move(e2));
133 images.addImage(std::move(e3));
134 images.addImage(std::move(e4));
135 images.setFlip(0);
136 measureTime("Align images total", [&] () {images.align();});
137 images.crop();
138 Image & e1ref = images.getImage(1), & e2ref = images.getImage(3),
139 & e3ref = images.getImage(2), & e4ref = images.getImage(0);
140 BOOST_CHECK_EQUAL(images.getWidth(), 962);
141 BOOST_CHECK_EQUAL(images.getHeight(), 564);
142 BOOST_CHECK_EQUAL(e1ref.getDeltaX(), -38);
143 BOOST_CHECK_EQUAL(e1ref.getDeltaY(), -32);
144 BOOST_CHECK_EQUAL(e2ref.getDeltaX(), -18);
145 BOOST_CHECK_EQUAL(e2ref.getDeltaY(), 0);
146 BOOST_CHECK_EQUAL(e3ref.getDeltaX(), 0);
147 BOOST_CHECK_EQUAL(e3ref.getDeltaY(), -6);
148 BOOST_CHECK_EQUAL(e4ref.getDeltaX(), -6);
149 BOOST_CHECK_EQUAL(e4ref.getDeltaY(), -36);
150 }
151
152
153 BOOST_AUTO_TEST_CASE(auto_exposure) {
154 ImageStack images;
155 Image e1, e2, e3;
156 RawParameters m1(image1), m2(image2), m3(image3);
157 measureTime("Load images", [&] () {
158 e1 = ImageIO::loadRawImage(m1);
159 e2 = ImageIO::loadRawImage(m2);
160 e3 = ImageIO::loadRawImage(m3);
161 });
162 BOOST_REQUIRE(e1.good());
163 BOOST_REQUIRE(e2.good());
164 BOOST_REQUIRE(e3.good());
165
166 images.addImage(std::move(e1));
167 images.addImage(std::move(e2));
168 images.addImage(std::move(e3));
169 Image & e1ref = images.getImage(0), & e2ref = images.getImage(1), & e3ref = images.getImage(2);
170 images.setFlip(m1.flip);
171 images.calculateSaturationLevel(m1);
172 measureTime("Align images", [&] () {
173 images.align();
174 });
175
176 measureTime("Compute response functions", [&] () {
177 images.computeResponseFunctions();
178 });
179 double metaImmExp = 1.0 / (1 << (int)(m1.logExp() - m2.logExp()));
180 double dataImmExp = e1ref.getRelativeExposure() / e2ref.getRelativeExposure();
181 cerr << "Relative exposure from data: " << dataImmExp << endl;
182 cerr << "Relative exposure from metadata: " << metaImmExp << endl;
183 }
184
185
186 struct NullProgressIndicator : public ProgressIndicator {
187 virtual void advance(int percent, const char * message, const char * arg) {}
188 };
189
190
191 BOOST_AUTO_TEST_CASE(output_filename) {
192 ImageIO io;
193 LoadOptions lo;
194 lo.align = lo.crop = false;
195 NullProgressIndicator npi;
196 lo.fileNames.push_back(image1);
197 BOOST_REQUIRE_EQUAL(io.load(lo, npi), 2);
198 string pwd = QDir::currentPath().toLocal8Bit().constData();
199 string oneFile = io.buildOutputFileName().toLocal8Bit().constData();
200 BOOST_CHECK_EQUAL(oneFile, pwd + "/test/sample1.dng");
201 lo.fileNames.push_back(image2);
202 lo.fileNames.push_back(image3);
203 BOOST_REQUIRE_EQUAL(io.load(lo, npi), 6);
204 string threeFile = io.buildOutputFileName().toLocal8Bit().constData();
205 BOOST_CHECK_EQUAL(threeFile, pwd + "/test/sample1-3.dng");
206 }
0 /*
1 * HDRMerge - HDR exposure merging software.
2 * Copyright 2012 Javier Celaya
3 * jcelaya@gmail.com
4 *
5 * This file is part of HDRMerge.
6 *
7 * HDRMerge is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * HDRMerge is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with HDRMerge. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #define BOOST_TEST_MODULE hdrmerge
23 #define BOOST_TEST_DYN_LINK
24 #include <boost/test/unit_test.hpp>
25 #include "../src/Log.hpp"
26 #include <iostream>
27
28 static int setLog() {
29 hdrmerge::Log::setOutputStream(std::cout);
30 hdrmerge::Log::setMinimumPriority(0);
31 return 0;
32 }
33 static int foo = setLog();
Binary diff not shown