diff --git a/.github/workflows/build-linux-clazy.yml b/.github/workflows/build-linux-clazy.yml
index cac8dd19..e4bed7ed 100644
--- a/.github/workflows/build-linux-clazy.yml
+++ b/.github/workflows/build-linux-clazy.yml
@@ -37,6 +37,6 @@ jobs:
# all level 1 checks but ignore clazy-no-connect-by-name
# no parallel make with clazy to not mess log
- run: |
- export CLAZY_IGNORE_DIRS=".*usr.*|.*bezier.*|.*boost.*|.*SingleApplication.*|.*spdlog.*|.*zernike.*" \
+ export CLAZY_IGNORE_DIRS=".*usr.*|.*bezier.*|.*boost.*|.*SingleApplication.*|.*spdlog.*|.*zernike.*|.*cnpy.*" \
&& export CLAZY_CHECKS="level1,no-connect-by-name,function-args-by-value,function-args-by-ref,incorrect-emit,old-style-connect" \
&& make
diff --git a/.github/workflows/build-linux.yml b/.github/workflows/build-linux.yml
index 75d55b65..b483b24f 100644
--- a/.github/workflows/build-linux.yml
+++ b/.github/workflows/build-linux.yml
@@ -49,4 +49,4 @@ jobs:
tidy-checks: '' # rely solely on .clang-tidy file
tidy-review: true
passive-reviews: true
- ignore: 'bezier|boost|SingleApplication|spdlog|zernike|moc_*|ui_*|qwt*'
+ ignore: 'bezier|boost|SingleApplication|spdlog|zernike|cnpy|moc_*|ui_*|qwt*'
diff --git a/DFTFringe.pro b/DFTFringe.pro
index 4adcc4e2..e94e9cab 100644
--- a/DFTFringe.pro
+++ b/DFTFringe.pro
@@ -56,6 +56,7 @@ win32 {
LIBS += -L$$PWD\..\build_openCV\install\x64\mingw\bin -llibopencv_imgcodecs4120
LIBS += -L$$PWD\..\build_openCV\install\x64\mingw\bin -llibopencv_imgproc4120
LIBS += -ldbghelp # for SetUnhandledExceptionFilter
+ LIBS += -lz # zip compression library needed for cnpy.cpp
# This is for armadillo to not use wrapper. See https://gitlab.com/conradsnicta/armadillo-code#6-linux-and-macos-compiling-and-linking
@@ -81,6 +82,7 @@ unix: !mac {
LIBS += -lopencv_imgcodecs
LIBS += -lopencv_imgproc
LIBS += -L/usr/local/qwt-6.3.0/lib -lqwt
+ LIBS += -lz # zip compression library needed for cnpy.cpp
}
# MAC ##############
@@ -143,7 +145,7 @@ RESOURCES += DFTResources.qrc
TRANSLATIONS += dftfringe_fr.ts
-INCLUDEPATH += ./bezier ./SingleApplication ./zernike
+INCLUDEPATH += ./bezier ./SingleApplication ./zernike ./cnpy
SOURCES += SingleApplication/singleapplication.cpp \
SingleApplication/singleapplication_p.cpp \
@@ -164,6 +166,7 @@ SOURCES += SingleApplication/singleapplication.cpp \
ccswappeddlg.cpp \
circlefit.cpp \
circleoutline.cpp \
+ cnpy/cnpy.cpp \
colorchannel.cpp \
colorchanneldisplay.cpp \
colormapviewerdlg.cpp \
@@ -285,6 +288,7 @@ HEADERS += bezier/bezier.h \
circle.h \
circleoutline.h \
circleutils.h \
+ cnpy/cnpy.h \
colorchannel.h \
colorchanneldisplay.h \
colormapviewerdlg.h \
diff --git a/DFTFringe_Dale.pro b/DFTFringe_Dale.pro
index 24b5d0e7..76fd813f 100644
--- a/DFTFringe_Dale.pro
+++ b/DFTFringe_Dale.pro
@@ -39,6 +39,7 @@ SOURCES += main.cpp \
mainwindow.cpp \
igramarea.cpp \
circleoutline.cpp \
+ cnpy/cnpy.cpp \
graphicsutilities.cpp \
dfttools.cpp \
dftarea.cpp \
@@ -156,6 +157,7 @@ HEADERS += mainwindow.h \
edgeplot.h \
IgramArea.h \
circleoutline.h \
+ cnpy/cnpy.h \
graphicsutilities.h \
dfttools.h \
dftarea.h \
@@ -266,7 +268,7 @@ HEADERS += mainwindow.h \
SingleApplication/singleapplication.h \
SingleApplication/singleapplication_p.h
-INCLUDEPATH += ./bezier ./SingleApplication ./zernike
+INCLUDEPATH += ./bezier ./SingleApplication ./zernike ./cnpy
FORMS += mainwindow.ui \
annulushelpdlg.ui \
@@ -378,6 +380,7 @@ LIBS += D:\lapack\build64\bin\liblapack.dll
LIBS += D:\lapack\build64\bin\libblas.dll
LIBS += -ldbghelp # for SetUnhandledExceptionFilter
+LIBS += -lz # zip compression library needed for cnpy.cpp
}
diff --git a/DFTFringe_QT5.pro b/DFTFringe_QT5.pro
index 79b3336e..65573323 100644
--- a/DFTFringe_QT5.pro
+++ b/DFTFringe_QT5.pro
@@ -56,7 +56,7 @@ win32 {
LIBS += -L..\build_openCV\install\x64\mingw\bin -llibopencv_imgcodecs460
LIBS += -L..\build_openCV\install\x64\mingw\bin -llibopencv_imgproc460
LIBS += -ldbghelp # for SetUnhandledExceptionFilter
-
+ LIBS += -lz # zip compression library needed for cnpy.cpp
# This is for armadillo to not use wrapper. See https://gitlab.com/conradsnicta/armadillo-code#6-linux-and-macos-compiling-and-linking
DEFINES += ARMA_DONT_USE_WRAPPER
@@ -80,6 +80,8 @@ unix: !mac {
LIBS += -lopencv_imgproc
LIBS += -lopencv_imgproc
LIBS += -lqwt-qt5
+ LIBS += -lz # zip compression library needed for cnpy.cpp
+
}
# MAC ##############
@@ -142,7 +144,7 @@ RESOURCES += DFTResources.qrc
TRANSLATIONS += dftfringe_fr.ts
-INCLUDEPATH += ./bezier ./SingleApplication ./zernike
+INCLUDEPATH += ./bezier ./SingleApplication ./zernike ./cnpy
SOURCES += SingleApplication/singleapplication.cpp \
SingleApplication/singleapplication_p.cpp \
@@ -163,6 +165,7 @@ SOURCES += SingleApplication/singleapplication.cpp \
ccswappeddlg.cpp \
circlefit.cpp \
circleoutline.cpp \
+ cnpy/cnpy.cpp \
colorchannel.cpp \
colorchanneldisplay.cpp \
colormapviewerdlg.cpp \
@@ -284,6 +287,7 @@ HEADERS += bezier/bezier.h \
circle.h \
circleoutline.h \
circleutils.h \
+ cnpy/cnpy.h \
colorchannel.h \
colorchanneldisplay.h \
colormapviewerdlg.h \
diff --git a/LICENSES/MIT.txt b/LICENSES/MIT.txt
index 0474e915..9003e035 100644
--- a/LICENSES/MIT.txt
+++ b/LICENSES/MIT.txt
@@ -15,6 +15,9 @@ Copyright (C) 2016 Gabi Melman.
zernike (zapm.cpp)
Copyright (C) 2022 Michael Peck
+cnpy (cnpy.h cnpy.cpp)
+Copyright (c) Carl Rogers, 2011
+
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
diff --git a/RevisionHistory.html b/RevisionHistory.html
index a17bf02b..83944cbc 100644
--- a/RevisionHistory.html
+++ b/RevisionHistory.html
@@ -10,6 +10,14 @@
DFTFringe Version History
+ - Version 8.4.0
+
+ - Ronchi and Foucault displays have new circular grid feature to measure zones better (right click on ronchi)
+ - Ronchi compare now has blink mode that switches between 2 ronchis (first select at least 2 ronchis, then right click on the ronchi view, then select "compare" button, then "blink" button)
+ - Can read Wavefront Pro format wavefront files (.npz files)
+ - Fixed bug in subtract wavefront feature introduced in version 7.4.0 where sometimes it subtracts the wrong wavefront (but correctly showed you what it did on the right side). Bug happened on A-B if B is farther down in the wavefront list than A.
+
+
- Version 8.3.2
- Fixed bug introduced in 8.3.1 in Ronchi Foucault where it was always doing autocollimate mode
diff --git a/cnpy/LICENSE.txt b/cnpy/LICENSE.txt
new file mode 100644
index 00000000..e60eadbc
--- /dev/null
+++ b/cnpy/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) Carl Rogers, 2011
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/cnpy/cnpy.cpp b/cnpy/cnpy.cpp
new file mode 100644
index 00000000..f42275dd
--- /dev/null
+++ b/cnpy/cnpy.cpp
@@ -0,0 +1,339 @@
+//Copyright (C) 2011 Carl Rogers
+//Released under MIT License
+//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
+
+#include"cnpy.h"
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+char cnpy::BigEndianTest() {
+ int x = 1;
+ return (((char *)&x)[0]) ? '<' : '>';
+}
+
+char cnpy::map_type(const std::type_info& t)
+{
+ if(t == typeid(float) ) return 'f';
+ if(t == typeid(double) ) return 'f';
+ if(t == typeid(long double) ) return 'f';
+
+ if(t == typeid(int) ) return 'i';
+ if(t == typeid(char) ) return 'i';
+ if(t == typeid(short) ) return 'i';
+ if(t == typeid(long) ) return 'i';
+ if(t == typeid(long long) ) return 'i';
+
+ if(t == typeid(unsigned char) ) return 'u';
+ if(t == typeid(unsigned short) ) return 'u';
+ if(t == typeid(unsigned long) ) return 'u';
+ if(t == typeid(unsigned long long) ) return 'u';
+ if(t == typeid(unsigned int) ) return 'u';
+
+ if(t == typeid(bool) ) return 'b';
+
+ if(t == typeid(std::complex) ) return 'c';
+ if(t == typeid(std::complex) ) return 'c';
+ if(t == typeid(std::complex) ) return 'c';
+
+ else return '?';
+}
+
+template<> std::vector& cnpy::operator+=(std::vector& lhs, const std::string rhs) {
+ lhs.insert(lhs.end(),rhs.begin(),rhs.end());
+ return lhs;
+}
+
+template<> std::vector& cnpy::operator+=(std::vector& lhs, const char* rhs) {
+ //write in little endian
+ size_t len = strlen(rhs);
+ lhs.reserve(len);
+ for(size_t byte = 0; byte < len; byte++) {
+ lhs.push_back(rhs[byte]);
+ }
+ return lhs;
+}
+
+void cnpy::parse_npy_header(unsigned char* buffer,size_t& word_size, std::vector& shape, bool& fortran_order) {
+ //std::string magic_string(buffer,6);
+ uint8_t major_version = *reinterpret_cast(buffer+6);
+ uint8_t minor_version = *reinterpret_cast(buffer+7);
+ uint16_t header_len = *reinterpret_cast(buffer+8);
+ std::string header(reinterpret_cast(buffer+9),header_len);
+
+ size_t loc1, loc2;
+
+ //fortran order
+ loc1 = header.find("fortran_order")+16;
+ fortran_order = (header.substr(loc1,4) == "True" ? true : false);
+
+ //shape
+ loc1 = header.find("(");
+ loc2 = header.find(")");
+
+ std::regex num_regex("[0-9][0-9]*");
+ std::smatch sm;
+ shape.clear();
+
+ std::string str_shape = header.substr(loc1+1,loc2-loc1-1);
+ while(std::regex_search(str_shape, sm, num_regex)) {
+ shape.push_back(std::stoi(sm[0].str()));
+ str_shape = sm.suffix().str();
+ }
+
+ //endian, word size, data type
+ //byte order code | stands for not applicable.
+ //not sure when this applies except for byte array
+ loc1 = header.find("descr")+9;
+ bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
+ assert(littleEndian);
+
+ //char type = header[loc1+1];
+ //assert(type == map_type(T));
+
+ std::string str_ws = header.substr(loc1+2);
+ loc2 = str_ws.find("'");
+ word_size = atoi(str_ws.substr(0,loc2).c_str());
+}
+
+void cnpy::parse_npy_header(FILE* fp, size_t& word_size, std::vector& shape, bool& fortran_order) {
+ char buffer[256];
+ size_t res = fread(buffer,sizeof(char),11,fp);
+ if(res != 11)
+ throw std::runtime_error("parse_npy_header: failed fread");
+ std::string header = fgets(buffer,256,fp);
+ assert(header[header.size()-1] == '\n');
+
+ size_t loc1, loc2;
+
+ //fortran order
+ loc1 = header.find("fortran_order");
+ if (loc1 == std::string::npos)
+ throw std::runtime_error("parse_npy_header: failed to find header keyword: 'fortran_order'");
+ loc1 += 16;
+ fortran_order = (header.substr(loc1,4) == "True" ? true : false);
+
+ //shape
+ loc1 = header.find("(");
+ loc2 = header.find(")");
+ if (loc1 == std::string::npos || loc2 == std::string::npos)
+ throw std::runtime_error("parse_npy_header: failed to find header keyword: '(' or ')'");
+
+ std::regex num_regex("[0-9][0-9]*");
+ std::smatch sm;
+ shape.clear();
+
+ std::string str_shape = header.substr(loc1+1,loc2-loc1-1);
+ while(std::regex_search(str_shape, sm, num_regex)) {
+ shape.push_back(std::stoi(sm[0].str()));
+ str_shape = sm.suffix().str();
+ }
+
+ //endian, word size, data type
+ //byte order code | stands for not applicable.
+ //not sure when this applies except for byte array
+ loc1 = header.find("descr");
+ if (loc1 == std::string::npos)
+ throw std::runtime_error("parse_npy_header: failed to find header keyword: 'descr'");
+ loc1 += 9;
+ bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
+ assert(littleEndian);
+
+ //char type = header[loc1+1];
+ //assert(type == map_type(T));
+
+ std::string str_ws = header.substr(loc1+2);
+ loc2 = str_ws.find("'");
+ word_size = atoi(str_ws.substr(0,loc2).c_str());
+}
+
+void cnpy::parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size, size_t& global_header_offset)
+{
+ std::vector footer(22);
+ fseek(fp,-22,SEEK_END);
+ size_t res = fread(&footer[0],sizeof(char),22,fp);
+ if(res != 22)
+ throw std::runtime_error("parse_zip_footer: failed fread");
+
+ uint16_t disk_no, disk_start, nrecs_on_disk, comment_len;
+ disk_no = *(uint16_t*) &footer[4];
+ disk_start = *(uint16_t*) &footer[6];
+ nrecs_on_disk = *(uint16_t*) &footer[8];
+ nrecs = *(uint16_t*) &footer[10];
+ global_header_size = *(uint32_t*) &footer[12];
+ global_header_offset = *(uint32_t*) &footer[16];
+ comment_len = *(uint16_t*) &footer[20];
+
+ assert(disk_no == 0);
+ assert(disk_start == 0);
+ assert(nrecs_on_disk == nrecs);
+ assert(comment_len == 0);
+}
+
+cnpy::NpyArray load_the_npy_file(FILE* fp) {
+ std::vector shape;
+ size_t word_size;
+ bool fortran_order;
+ cnpy::parse_npy_header(fp,word_size,shape,fortran_order);
+
+ cnpy::NpyArray arr(shape, word_size, fortran_order);
+ size_t nread = fread(arr.data(),1,arr.num_bytes(),fp);
+ if(nread != arr.num_bytes())
+ throw std::runtime_error("load_the_npy_file: failed fread");
+ return arr;
+}
+
+cnpy::NpyArray load_the_npz_array(FILE* fp, uint32_t compr_bytes, uint32_t uncompr_bytes) {
+
+ std::vector buffer_compr(compr_bytes);
+ std::vector buffer_uncompr(uncompr_bytes);
+ size_t nread = fread(&buffer_compr[0],1,compr_bytes,fp);
+ if(nread != compr_bytes)
+ throw std::runtime_error("load_the_npy_file: failed fread");
+
+ int err;
+ z_stream d_stream;
+
+ d_stream.zalloc = Z_NULL;
+ d_stream.zfree = Z_NULL;
+ d_stream.opaque = Z_NULL;
+ d_stream.avail_in = 0;
+ d_stream.next_in = Z_NULL;
+ err = inflateInit2(&d_stream, -MAX_WBITS);
+
+ d_stream.avail_in = compr_bytes;
+ d_stream.next_in = &buffer_compr[0];
+ d_stream.avail_out = uncompr_bytes;
+ d_stream.next_out = &buffer_uncompr[0];
+
+ err = inflate(&d_stream, Z_FINISH);
+ err = inflateEnd(&d_stream);
+
+ std::vector shape;
+ size_t word_size;
+ bool fortran_order;
+ cnpy::parse_npy_header(&buffer_uncompr[0],word_size,shape,fortran_order);
+
+ cnpy::NpyArray array(shape, word_size, fortran_order);
+
+ size_t offset = uncompr_bytes - array.num_bytes();
+ memcpy(array.data(),&buffer_uncompr[0]+offset,array.num_bytes());
+
+ return array;
+}
+
+cnpy::npz_t cnpy::npz_load(std::string fname) {
+ FILE* fp = fopen(fname.c_str(),"rb");
+
+ if(!fp) {
+ throw std::runtime_error("npz_load: Error! Unable to open file "+fname+"!");
+ }
+
+ cnpy::npz_t arrays;
+
+ while(1) {
+ std::vector local_header(30);
+ size_t headerres = fread(&local_header[0],sizeof(char),30,fp);
+ if(headerres != 30)
+ throw std::runtime_error("npz_load: failed fread");
+
+ //if we've reached the global header, stop reading
+ if(local_header[2] != 0x03 || local_header[3] != 0x04) break;
+
+ //read in the variable name
+ uint16_t name_len = *(uint16_t*) &local_header[26];
+ std::string varname(name_len,' ');
+ size_t vname_res = fread(&varname[0],sizeof(char),name_len,fp);
+ if(vname_res != name_len)
+ throw std::runtime_error("npz_load: failed fread");
+
+ //erase the lagging .npy
+ varname.erase(varname.end()-4,varname.end());
+
+ //read in the extra field
+ uint16_t extra_field_len = *(uint16_t*) &local_header[28];
+ if(extra_field_len > 0) {
+ std::vector buff(extra_field_len);
+ size_t efield_res = fread(&buff[0],sizeof(char),extra_field_len,fp);
+ if(efield_res != extra_field_len)
+ throw std::runtime_error("npz_load: failed fread");
+ }
+
+ uint16_t compr_method = *reinterpret_cast(&local_header[0]+8);
+ uint32_t compr_bytes = *reinterpret_cast(&local_header[0]+18);
+ uint32_t uncompr_bytes = *reinterpret_cast(&local_header[0]+22);
+
+ if(compr_method == 0) {arrays[varname] = load_the_npy_file(fp);}
+ else {arrays[varname] = load_the_npz_array(fp,compr_bytes,uncompr_bytes);}
+ }
+
+ fclose(fp);
+ return arrays;
+}
+
+cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) {
+ FILE* fp = fopen(fname.c_str(),"rb");
+
+ if(!fp) throw std::runtime_error("npz_load: Unable to open file "+fname);
+
+ while(1) {
+ std::vector local_header(30);
+ size_t header_res = fread(&local_header[0],sizeof(char),30,fp);
+ if(header_res != 30)
+ throw std::runtime_error("npz_load: failed fread");
+
+ //if we've reached the global header, stop reading
+ if(local_header[2] != 0x03 || local_header[3] != 0x04) break;
+
+ //read in the variable name
+ uint16_t name_len = *(uint16_t*) &local_header[26];
+ std::string vname(name_len,' ');
+ size_t vname_res = fread(&vname[0],sizeof(char),name_len,fp);
+ if(vname_res != name_len)
+ throw std::runtime_error("npz_load: failed fread");
+ vname.erase(vname.end()-4,vname.end()); //erase the lagging .npy
+
+ //read in the extra field
+ uint16_t extra_field_len = *(uint16_t*) &local_header[28];
+ fseek(fp,extra_field_len,SEEK_CUR); //skip past the extra field
+
+ uint16_t compr_method = *reinterpret_cast(&local_header[0]+8);
+ uint32_t compr_bytes = *reinterpret_cast(&local_header[0]+18);
+ uint32_t uncompr_bytes = *reinterpret_cast(&local_header[0]+22);
+
+ if(vname == varname) {
+ NpyArray array = (compr_method == 0) ? load_the_npy_file(fp) : load_the_npz_array(fp,compr_bytes,uncompr_bytes);
+ fclose(fp);
+ return array;
+ }
+ else {
+ //skip past the data
+ uint32_t size = *(uint32_t*) &local_header[22];
+ fseek(fp,size,SEEK_CUR);
+ }
+ }
+
+ fclose(fp);
+
+ //if we get here, we haven't found the variable in the file
+ throw std::runtime_error("npz_load: Variable name "+varname+" not found in "+fname);
+}
+
+cnpy::NpyArray cnpy::npy_load(std::string fname) {
+
+ FILE* fp = fopen(fname.c_str(), "rb");
+
+ if(!fp) throw std::runtime_error("npy_load: Unable to open file "+fname);
+
+ NpyArray arr = load_the_npy_file(fp);
+
+ fclose(fp);
+ return arr;
+}
+
+
diff --git a/cnpy/cnpy.h b/cnpy/cnpy.h
new file mode 100644
index 00000000..4ea87a9c
--- /dev/null
+++ b/cnpy/cnpy.h
@@ -0,0 +1,269 @@
+//Copyright (C) 2011 Carl Rogers
+//Released under MIT License
+//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
+
+#ifndef LIBCNPY_H_
+#define LIBCNPY_H_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include