├── include ├── python │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-37.pyc │ │ ├── sbpreader.cpython-37.pyc │ │ └── sbpwriter.cpython-37.pyc │ ├── sbpwriter.py │ └── sbpreader.py └── spb │ ├── spbreader.hpp │ └── spbwriter.hpp ├── tests ├── imagespec.spb ├── test_spb_c.c └── test_spb.py ├── .gitignore ├── setup.py ├── LICENSE └── README.md /include/python/__init__.py: -------------------------------------------------------------------------------- 1 | # 2 | -------------------------------------------------------------------------------- /tests/imagespec.spb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-K-E/spbparser/master/tests/imagespec.spb -------------------------------------------------------------------------------- /include/python/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-K-E/spbparser/master/include/python/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /include/python/__pycache__/sbpreader.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-K-E/spbparser/master/include/python/__pycache__/sbpreader.cpython-37.pyc -------------------------------------------------------------------------------- /include/python/__pycache__/sbpwriter.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D-K-E/spbparser/master/include/python/__pycache__/sbpwriter.cpython-37.pyc -------------------------------------------------------------------------------- /tests/test_spb_c.c: -------------------------------------------------------------------------------- 1 | // test file for quaternion 2 | #include "../include/c/spbreader.c" 3 | #include "../include/c/spbwriter.c" 4 | 5 | #include 6 | 7 | /*! @{ 8 | */ 9 | 10 | /*! @{ testing constructors for the vector 11 | */ 12 | 13 | CTEST(suite, test_empty_constructor) { 14 | ASSERT_EQUAL(false, false); 15 | } 16 | 17 | /*! @} */ 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # 2 | import setuptools 3 | 4 | setuptools.setup( 5 | name="spbparser", 6 | version="0.0.1", 7 | packages=setuptools.find_packages( 8 | exclude=[ 9 | "tests", 10 | "*.tests", 11 | "*.tests.*", 12 | "tests.*", 13 | "docs", 14 | ".gitignore", 15 | "README.md", 16 | ] 17 | ), 18 | test_suite="tests", 19 | classifiers=[ 20 | "Programming Language :: Python :: 3", 21 | "License :: OSI Approved :: MIT License", 22 | "Operating System :: OS Independent", 23 | ], 24 | ) 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 DKE 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/test_spb.py: -------------------------------------------------------------------------------- 1 | """ 2 | python tests of the spb library 3 | """ 4 | 5 | import unittest 6 | import csv 7 | import array 8 | from include.python.sbpreader import spb as sbreader 9 | from include.python.sbpwriter import spb as sbwriter 10 | 11 | 12 | class SpbTest(unittest.TestCase): 13 | "" 14 | 15 | @classmethod 16 | def setUpClass(cls): 17 | "" 18 | rows = [] 19 | with open("imagespec.csv", "r", encoding="utf-8", newline="\n") as f: 20 | imspec = csv.reader(f, delimiter=",") 21 | for row in imspec: 22 | rs = [] 23 | for cell in row: 24 | rs.append(float(cell)) 25 | # 26 | rows.append(rs) 27 | # 28 | 29 | cls.spb_csv = rows 30 | cls.sbp_path = "./imagespec.spb" 31 | 32 | def test_get_header(self): 33 | "test getting header" 34 | docs = sbreader.get_header(self.sbp_path) 35 | magic, dims, winfo = docs[1] 36 | self.assertEqual(magic, "SPB") 37 | self.assertEqual(dims, (100, 56, 30)) 38 | self.assertEqual(winfo, (380.0, 11.72412109375, 720.0)) 39 | 40 | def test_parseFileHeader(self): 41 | "test getting header" 42 | hexample = ("SPB", (100, 56, 30), (380.0, 11.72412109375, 720.0)) 43 | docs = sbreader.parseFileHeader(hexample) 44 | self.assertEqual( 45 | docs, 46 | { 47 | "width": 100, 48 | "height": 56, 49 | "nb_channels": 30, 50 | "first_wavelength": 380.0, 51 | "last_wavelength": 720.0, 52 | "wavelength_resolution": 11.72412109375, 53 | }, 54 | ) 55 | 56 | def test_read_header(self): 57 | "" 58 | docs = sbreader.read_header(self.sbp_path) 59 | self.assertEqual( 60 | docs, 61 | ( 62 | True, 63 | { 64 | "width": 100, 65 | "height": 56, 66 | "nb_channels": 30, 67 | "first_wavelength": 380.0, 68 | "last_wavelength": 720.0, 69 | "wavelength_resolution": 11.72412109375, 70 | }, 71 | ), 72 | ) 73 | 74 | def test_getImageData(self): 75 | "" 76 | docs = sbreader.getImageData(self.sbp_path) 77 | cs = [] 78 | for row in self.spb_csv[1:]: # skip the first row 79 | for r in row: 80 | cs.append(r) 81 | arr = array.array('f', cs) 82 | self.assertEqual(arr, docs) 83 | 84 | 85 | if __name__ == "__main__": 86 | unittest.main() 87 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # spbparser 2 | Header only Spectral Binary Format utils. 3 | 4 | Just include header of the required file from `ìnclude` folder. All files are 5 | provided with `spb` namespace. 6 | 7 | ## WARNING 8 | 9 | The parser is tested in a little endian machine. Though we check if the case 10 | is otherwise for a given machine before proceeding further, it is not heavily 11 | tested. 12 | 13 | The specification of spb format does not allow heterogeneous spds to be 14 | serialized. Resampling of your original spds might be necessary before 15 | proceeding with serialization. 16 | 17 | ## Dependency 18 | 19 | - None. We are not even using STL or vectors. Probably can be ported to C with 20 | a little work. 21 | 22 | - Should be mostly C++98 compliant as well. 23 | 24 | ## Features 25 | 26 | - Reader 27 | - Writer 28 | 29 | ## SPectral Binary Specification Summary 30 | 31 | File section | Bytes 32 | ----------------------------- | ---------- 33 | File identifier ’SPB’ | 3 34 | Header Part | 35 | Image width x | 4 36 | Image height y | 4 37 | Number of spectral channels n | 4 38 | First wavelength | 4 39 | Wavelength Resolution | 4 40 | Last Wavelength | 4 41 | Image Data | x\*y\*n\*4 42 | 43 | 44 | 45 | Image data is written to the file in column order and values 46 | are stored in little endian form. 47 | Dimensions (x,y and n) are stored in uint32-format 48 | and wavelength values in float32-format. Spectral image 49 | values are reflectance values stored as float32. 50 | 51 | 52 | ## Usage 53 | 54 | To read an spb file: 55 | 56 | ``` 57 | c++ 58 | 59 | #include 60 | 61 | const char *path = "path/to/my/file.spb"; 62 | 63 | uint32_t width, height, nb_channels; 64 | float first_wavelength, wavelength_resolution; 65 | float last_wavelength; 66 | 67 | spb::read_header(path, width, height, nb_channels, 68 | first_wavelength, wavelength_resolution, 69 | last_wavelength); 70 | float *data = new float[width * height * nb_channels]; 71 | spb::read_file(path, width, height, nb_channels, data); 72 | 73 | ``` 74 | 75 | 76 | To write an spb file: 77 | 78 | ``` 79 | c++ 80 | 81 | #include 82 | 83 | const char *path = "path/to/my/outfile.spb"; 84 | 85 | MyCustomSpd spectrum(); 86 | /** 87 | some operations ... 88 | . 89 | . 90 | . 91 | */ 92 | 93 | uint32_t width = spectrum.image_width(); 94 | uint32_t height = spectrum.image_height(); 95 | uint32_t nb_spectral_channels = spectrum.nb_channels(); 96 | float first_wavelength = spectrum.first_wavelength(); 97 | float wavelength_resolution = spectrum.wavelength_resolution(); 98 | float last_wavelength = spectrum.last_wavelength(); 99 | float *data = new float[width * height * nb_spectral_channels]; 100 | spectrum.put_data(data); 101 | 102 | spb::write_file(path, width, height, nb_channels, 103 | first_wavelength, wavelength_resolution, 104 | last_wavelength, data); 105 | 106 | ``` 107 | -------------------------------------------------------------------------------- /include/python/sbpwriter.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 D.Kaan Eraslan & Qm Auber 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy 8 | of this software and associated documentation files (the 9 | "Software"), to deal 10 | in the Software without restriction, including without 11 | limitation the rights 12 | to use, copy, modify, merge, publish, distribute, 13 | sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the 15 | Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall 19 | be included in all 20 | copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 23 | KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 27 | EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 29 | DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 31 | OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 33 | OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | """ 36 | """\brief Spectral Binary File Format (.spb) Specification 37 | 38 | Table 1: Structure of Spectral Binary File. 39 | 40 | File section | Bytes 41 | ----------------------------- | ---------- 42 | File identifier ’SPB’ | 3 43 | Header Part | 24 44 | Image width | 4 45 | Image height | 4 46 | Number of spectral channels n | 4 47 | First wavelength | 4 48 | Wavelength Resolution | 4 49 | Last Wavelength | 4 50 | Image Data | x*y*n*4 51 | 52 | Image data is written to the file in column order and values 53 | are stored in little endian form. 54 | Dimensions (x,y and n) are stored in uint32-format 55 | and wavelength values in float32-format. Spectral image 56 | values are reflectance values stored as float32. 57 | """ 58 | 59 | from typing import List, Tuple, Dict, Union, Optional 60 | import os 61 | import array 62 | 63 | HEADER_SIZE = 27 64 | 65 | NB_HEADER_BYTE = 4 66 | 67 | 68 | class spb: 69 | "" 70 | 71 | @staticmethod 72 | def to_array( 73 | width: int, 74 | height: int, 75 | nb_channels: int, 76 | first_wavelength: int, 77 | wavelength_resolution: int, 78 | last_wavelength: int, 79 | data: List[float], 80 | ): 81 | "" 82 | arr = array.array("B") 83 | arr.append("S") 84 | arr.append("P") 85 | arr.append("B") 86 | 87 | # 88 | width_c = ctypes.c_uint32(width) 89 | arr.append(width_c) 90 | 91 | # 92 | height_c = ctypes.c_uint32(height) 93 | arr.append(height_c) 94 | 95 | # 96 | n_c = ctypes.c_uint32(nb_channels) 97 | arr.append(n_c) 98 | 99 | # 100 | first_w = ctypes.c_float(first_wavelength) 101 | arr.append(first_w) 102 | 103 | # 104 | wave_res = ctypes.c_float(wavelength_resolution) 105 | arr.append(wave_res) 106 | 107 | # 108 | last_w = ctypes.c_float(last_wavelength) 109 | arr.append(last_w) 110 | for d in data: 111 | arr.append(d) 112 | return arr 113 | 114 | @staticmethod 115 | def save( 116 | fpath: str, 117 | width: int, 118 | height: int, 119 | nb_channels: int, 120 | first_wavelength: int, 121 | wavelength_resolution: int, 122 | last_wavelength: int, 123 | data: List[float], 124 | ): 125 | "" 126 | arr = Spb.to_array( 127 | width=width, 128 | height=height, 129 | nb_channels=nb_channels, 130 | first_wavelength=first_wavelength, 131 | last_wavelength=last_wavelength, 132 | data=data, 133 | wavelength_resolution=wavelength_resolution, 134 | ) 135 | with open(fpath, "wb") as f: 136 | for a in arr: 137 | f.write(a) 138 | -------------------------------------------------------------------------------- /include/python/sbpreader.py: -------------------------------------------------------------------------------- 1 | """ 2 | MIT License 3 | 4 | Copyright (c) 2020 D.Kaan Eraslan & Qm Auber 5 | 6 | Permission is hereby granted, free of charge, to any person 7 | obtaining a copy 8 | of this software and associated documentation files (the 9 | "Software"), to deal 10 | in the Software without restriction, including without 11 | limitation the rights 12 | to use, copy, modify, merge, publish, distribute, 13 | sublicense, and/or sell 14 | copies of the Software, and to permit persons to whom the 15 | Software is 16 | furnished to do so, subject to the following conditions: 17 | 18 | The above copyright notice and this permission notice shall 19 | be included in all 20 | copies or substantial portions of the Software. 21 | 22 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 23 | KIND, EXPRESS OR 24 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 25 | MERCHANTABILITY, 26 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 27 | EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 29 | DAMAGES OR OTHER 30 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 31 | OTHERWISE, ARISING FROM, 32 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 33 | OTHER DEALINGS IN THE 34 | SOFTWARE. 35 | """ 36 | """\brief Spectral Binary File Format (.spb) Specification 37 | 38 | Table 1: Structure of Spectral Binary File. 39 | 40 | File section | Bytes 41 | ----------------------------- | ---------- 42 | File identifier ’SPB’ | 3 43 | Header Part | 24 44 | Image width | 4 45 | Image height | 4 46 | Number of spectral channels n | 4 47 | First wavelength | 4 48 | Wavelength Resolution | 4 49 | Last Wavelength | 4 50 | Image Data | x*y*n*4 51 | 52 | Image data is written to the file in column order and values 53 | are stored in little endian form. 54 | Dimensions (x,y and n) are stored in uint32-format 55 | and wavelength values in float32-format. Spectral image 56 | values are reflectance values stored as float32. 57 | """ 58 | 59 | from typing import List, Tuple, Dict, Union, Optional 60 | import os 61 | import array 62 | 63 | HEADER_SIZE = 27 64 | 65 | 66 | class spb: 67 | @staticmethod 68 | def check_file_identifier(headerInfo: List[str]) -> bool: 69 | "" 70 | return headerInfo[0] == "S" and headerInfo[1] == "P" and headerInfo[2] == "B" 71 | 72 | @staticmethod 73 | def get_header(fpath: str) -> Tuple[bool, Optional[str]]: 74 | "" 75 | 76 | with open(fpath, "rb") as f: 77 | hinfo = f.read() 78 | 79 | headerInfo = hinfo[: HEADER_SIZE + 1] 80 | magic_header = hinfo[:3].decode() 81 | diminfo = hinfo[3 : 3 + 12] 82 | dimarr = array.array("I", diminfo) 83 | waveinfo = hinfo[3 + 12 : 3 + 12 + 12] 84 | wavearr = array.array("f", waveinfo) 85 | # headerInfo = headerInfo.decode("ascii") 86 | if spb.check_file_identifier(magic_header) is False: 87 | return False, None 88 | return True, (magic_header, tuple(dimarr), tuple(wavearr)) 89 | 90 | @staticmethod 91 | def parseFileHeader( 92 | headerInfo: Tuple[str, Tuple[int, int, int], Tuple[float, float, float]] 93 | ) -> Dict[str, Union[float, int]]: 94 | "" 95 | width = int(headerInfo[1][0]) 96 | height = int(headerInfo[1][1]) 97 | nb_channels = int(headerInfo[1][2]) 98 | first_wavelength = float(headerInfo[2][0]) 99 | wavelength_resolution = float(headerInfo[2][1]) 100 | last_wavelength = float(headerInfo[2][2]) 101 | return { 102 | "width": width, 103 | "height": height, 104 | "nb_channels": nb_channels, 105 | "first_wavelength": first_wavelength, 106 | "last_wavelength": last_wavelength, 107 | "wavelength_resolution": wavelength_resolution, 108 | } 109 | 110 | @staticmethod 111 | def getImageData(fpath: str,) -> List[float]: 112 | "get image data" 113 | with open(fpath, "rb") as f: 114 | fimage = f.read() 115 | 116 | image_data = array.array("f", fimage[HEADER_SIZE:]) 117 | 118 | return image_data 119 | 120 | @staticmethod 121 | def read_header(fpath: str) -> Tuple[bool, Union[Dict[str, Union[int, float]]]]: 122 | "" 123 | check, header = spb.get_header(fpath) 124 | if check is False: 125 | return check, None 126 | header = spb.parseFileHeader(header) 127 | return True, header 128 | 129 | @staticmethod 130 | def read_file(fpath: str) -> Tuple[bool, Optional[Dict[str, Union[int, float]]]]: 131 | "" 132 | check, header = Spb.read_header(fpath) 133 | if check is False: 134 | return check, None 135 | imdata = getImageData(fpath) 136 | fdata = {} 137 | fdata.update(header) 138 | fdata["data"] = imdata 139 | return fdata 140 | -------------------------------------------------------------------------------- /include/spb/spbreader.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPBREADER_HPP 2 | #define SPBREADER_HPP 3 | /** 4 | MIT License 5 | 6 | Copyright (c) 2020 D.Kaan Eraslan & Qm Auber 7 | 8 | Permission is hereby granted, free of charge, to any person 9 | obtaining a copy 10 | of this software and associated documentation files (the 11 | "Software"), to deal 12 | in the Software without restriction, including without 13 | limitation the rights 14 | to use, copy, modify, merge, publish, distribute, 15 | sublicense, and/or sell 16 | copies of the Software, and to permit persons to whom the 17 | Software is 18 | furnished to do so, subject to the following conditions: 19 | 20 | The above copyright notice and this permission notice shall 21 | be included in all 22 | copies or substantial portions of the Software. 23 | 24 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 25 | KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 27 | MERCHANTABILITY, 28 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 29 | EVENT SHALL THE 30 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 31 | DAMAGES OR OTHER 32 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 33 | OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 35 | OTHER DEALINGS IN THE 36 | SOFTWARE. 37 | */ 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | // 46 | namespace spb { 47 | /** 48 | Spectral Binary File Format (.spb) Specification 49 | 50 | Table 1: Structure of Spectral Binary File. 51 | 52 | File section | Bytes 53 | ----------------------------- | ---------- 54 | File identifier ’SPB’ | 3 55 | Header Part | 24 56 | Image width | 4 57 | Image height | 4 58 | Number of spectral channels n | 4 59 | First wavelength | 4 60 | Wavelength Resolution | 4 61 | Last Wavelength | 4 62 | Image Data | x*y*n*4 63 | 64 | Image data is written to the file in column order and values 65 | are stored in little endian form. 66 | Dimensions (x,y and n) are stored in uint32-format 67 | and wavelength values in float32-format. Spectral image 68 | values are reflectance values stored as float32. 69 | 70 | */ 71 | 72 | #define HEADER_SIZE 27 73 | 74 | /** 75 | Check if file header is "SPB" 76 | */ 77 | bool check_file_identifier(char headerInfo[HEADER_SIZE]) { 78 | return headerInfo[0] == 'S' && headerInfo[1] == 'P' && 79 | headerInfo[2] == 'B'; 80 | } 81 | 82 | void get_header(std::ifstream &file, 83 | char headerInfo[HEADER_SIZE]) { 84 | // 85 | // set file to back 86 | if (file.is_open()) { 87 | file.seekg(0); 88 | file.read(headerInfo, HEADER_SIZE); 89 | } else { 90 | throw std::runtime_error("spb file is not opened"); 91 | } 92 | if (!check_file_identifier(headerInfo)) { 93 | std::stringstream ss; 94 | ss << "File identifier for spb file is not correct." 95 | << std::endl 96 | << "It should be 'SPB'" 97 | << "see given header: " << headerInfo << std::endl; 98 | std::string err = ss.str(); 99 | throw std::runtime_error(err); 100 | } 101 | } 102 | void get_sub(unsigned int &start, char arr[4], 103 | char headerInfo[HEADER_SIZE]) { 104 | for (unsigned int k = 0; k < 4; k++) { 105 | arr[k] = headerInfo[start]; 106 | start++; 107 | } 108 | } 109 | void get_sub_ui(unsigned int &start, uint32_t &val, 110 | char headerInfo[HEADER_SIZE]) { 111 | char arr[4]; 112 | get_sub(start, arr, headerInfo); 113 | val = *(uint32_t *)(arr); 114 | } 115 | void get_sub_f(unsigned int &start, float &val, 116 | char headerInfo[HEADER_SIZE]) { 117 | char arr[4]; 118 | get_sub(start, arr, headerInfo); 119 | val = *(float *)(arr); 120 | } 121 | void parseFileHeader(char headerInfo[HEADER_SIZE], 122 | uint32_t &width, uint32_t &height, 123 | uint32_t &nb_channels, 124 | float &first_wavelength, 125 | float &wavelength_resolution, 126 | float &last_wavelength) { 127 | unsigned int start = 3; 128 | get_sub_ui(start, width, headerInfo); 129 | get_sub_ui(start, height, headerInfo); 130 | get_sub_ui(start, nb_channels, headerInfo); 131 | get_sub_f(start, first_wavelength, headerInfo); 132 | get_sub_f(start, wavelength_resolution, headerInfo); 133 | get_sub_f(start, last_wavelength, headerInfo); 134 | } 135 | void getImageData(std::ifstream &file, float *data, 136 | const uint32_t &width, 137 | const uint32_t &height, 138 | const uint32_t &nb_channels) { 139 | file.seekg(0); 140 | file.seekg(HEADER_SIZE, file.end); 141 | int data_length = file.tellg(); 142 | char *buffer = new char[data_length]; 143 | file.seekg(HEADER_SIZE); 144 | file.read(buffer, data_length); 145 | file.close(); 146 | // 147 | uint32_t stride = 4; 148 | uint32_t total_size = width * height * nb_channels; 149 | int char_counter = 0; 150 | // 151 | for (uint32_t pos = 0; pos < total_size; pos++) { 152 | char arr[stride]; 153 | for (int i = 0; i < stride; i++) { 154 | arr[i] = buffer[char_counter + i]; 155 | } 156 | char_counter += stride; 157 | data[pos] = *(float *)arr; 158 | } 159 | delete[] buffer; 160 | } 161 | 162 | void read_header(const char *fpath, uint32_t &width, 163 | uint32_t &height, uint32_t &nb_channels, 164 | float &first_wavelength, 165 | float &wavelength_resolution, 166 | float &last_wavelength) { 167 | std::ifstream file; 168 | file.open(fpath, std::ios::in | std::ios::binary); 169 | char headerInfo[HEADER_SIZE]; 170 | get_header(file, headerInfo); 171 | // 172 | parseFileHeader(headerInfo, width, height, nb_channels, 173 | first_wavelength, wavelength_resolution, 174 | last_wavelength); 175 | file.close(); 176 | } 177 | 178 | void read_file(const char *fpath, const uint32_t &width, 179 | const uint32_t &height, 180 | const uint32_t &nb_channels, float *data) { 181 | // 182 | std::ifstream file; 183 | file.open(fpath, std::ios::in | std::ios::binary); 184 | getImageData(file, data, width, height, nb_channels); 185 | file.close(); 186 | } 187 | } 188 | #endif 189 | -------------------------------------------------------------------------------- /include/spb/spbwriter.hpp: -------------------------------------------------------------------------------- 1 | #ifndef SPBWRITER_HPP 2 | #define SPBWRITER_HPP 3 | 4 | /** 5 | MIT License 6 | 7 | Copyright (c) 2020 D.Kaan Eraslan & Qm Auber 8 | 9 | Permission is hereby granted, free of charge, to any person 10 | obtaining a copy 11 | of this software and associated documentation files (the 12 | "Software"), to deal 13 | in the Software without restriction, including without 14 | limitation the rights 15 | to use, copy, modify, merge, publish, distribute, 16 | sublicense, and/or sell 17 | copies of the Software, and to permit persons to whom the 18 | Software is 19 | furnished to do so, subject to the following conditions: 20 | 21 | The above copyright notice and this permission notice shall 22 | be included in all 23 | copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 26 | KIND, EXPRESS OR 27 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 28 | MERCHANTABILITY, 29 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO 30 | EVENT SHALL THE 31 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 32 | DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 34 | OTHERWISE, ARISING FROM, 35 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 36 | OTHER DEALINGS IN THE 37 | SOFTWARE. 38 | */ 39 | 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | 48 | namespace spb { 49 | /** 50 | Spectral Binary File Format (.spb) Specification 51 | 52 | Table 1: Structure of Spectral Binary File. 53 | 54 | File section | Bytes 55 | ----------------------------- | ---------- 56 | File identifier ’SPB’ | 3 57 | Header Part | 24 58 | Image width | 4 59 | Image height | 4 60 | Number of spectral channels n | 4 61 | First wavelength | 4 62 | Wavelength Resolution | 4 63 | Last Wavelength | 4 64 | Image Data | x*y*n*4 65 | 66 | Image data is written to the file in column order and values 67 | are stored in little endian form. 68 | Dimensions (x,y and n) are stored in uint32-format 69 | and wavelength values in float32-format. Spectral image 70 | values are reflectance values stored as float32. 71 | 72 | */ 73 | #define HEADER_SIZE 27 74 | #define NB_HEADER_BYTE 4 75 | 76 | void put_header(char hinfo[HEADER_SIZE], 77 | uint32_t &start_pos, const char *put_arr, 78 | uint32_t size) { 79 | for (uint32_t i = 0; i < size; i++) { 80 | hinfo[start_pos] = put_arr[i]; 81 | start_pos++; 82 | } 83 | } 84 | bool isLittleEndian() { 85 | unsigned int i = 1; 86 | char *c = (char *)&i; 87 | if (*c) 88 | return true; 89 | return false; 90 | } 91 | void convert_uint_to_bytes_little(uint32_t t, 92 | char bytes[4]) { 93 | // https://stackoverflow.com/a/3784478/7330813 94 | char *tchar = (char *)&t; 95 | for (int i = 0; i < 4; i++) { 96 | bytes[i] = tchar[i]; 97 | } 98 | } 99 | void convert_uint_to_bytes_big(uint32_t t, char bytes[4]) { 100 | convert_uint_to_bytes_little(t, bytes); 101 | char last = bytes[3]; 102 | bytes[3] = bytes[0]; 103 | bytes[0] = last; 104 | } 105 | void convert_float_to_bytes_little(float t, char bytes[4]) { 106 | // https://stackoverflow.com/a/3784478/7330813 107 | char *tchar = (char *)&t; 108 | for (int i = 0; i < 4; i++) { 109 | bytes[i] = tchar[i]; 110 | } 111 | } 112 | void convert_float_to_bytes_big(float t, char bytes[4]) { 113 | convert_uint_to_bytes_little(t, bytes); 114 | char last = bytes[3]; 115 | bytes[3] = bytes[0]; 116 | bytes[0] = last; 117 | } 118 | 119 | void write_header(char headerInfo[HEADER_SIZE], 120 | uint32_t width, uint32_t height, 121 | uint32_t nb_channels, 122 | float first_wavelength, 123 | float wavelength_resolution, 124 | float last_wavelength) { 125 | // 126 | const char file_h[] = "SPB"; 127 | uint32_t start = 0; 128 | put_header(headerInfo, start, file_h, 3); 129 | 130 | char ws[NB_HEADER_BYTE]; 131 | char hs[NB_HEADER_BYTE]; 132 | char nb_chs[NB_HEADER_BYTE]; 133 | char first_ws[NB_HEADER_BYTE]; 134 | char waveres[NB_HEADER_BYTE]; 135 | char last_ws[NB_HEADER_BYTE]; 136 | 137 | if (isLittleEndian()) { 138 | convert_uint_to_bytes_little(width, ws); 139 | convert_uint_to_bytes_little(height, hs); 140 | convert_uint_to_bytes_little(nb_channels, nb_chs); 141 | convert_float_to_bytes_little(first_wavelength, 142 | first_ws); 143 | convert_float_to_bytes_little(wavelength_resolution, 144 | waveres); 145 | convert_float_to_bytes_little(last_wavelength, last_ws); 146 | } else { 147 | convert_uint_to_bytes_big(width, ws); 148 | convert_uint_to_bytes_big(height, hs); 149 | convert_uint_to_bytes_big(nb_channels, nb_chs); 150 | convert_float_to_bytes_big(first_wavelength, first_ws); 151 | convert_float_to_bytes_big(wavelength_resolution, 152 | waveres); 153 | convert_float_to_bytes_big(last_wavelength, last_ws); 154 | } 155 | put_header(headerInfo, start, ws, NB_HEADER_BYTE); 156 | put_header(headerInfo, start, hs, NB_HEADER_BYTE); 157 | put_header(headerInfo, start, nb_chs, NB_HEADER_BYTE); 158 | put_header(headerInfo, start, first_ws, NB_HEADER_BYTE); 159 | put_header(headerInfo, start, waveres, NB_HEADER_BYTE); 160 | put_header(headerInfo, start, last_ws, NB_HEADER_BYTE); 161 | } 162 | void write_data(char *spb_data, uint32_t imsize, 163 | float *data) { 164 | auto convfn = isLittleEndian() 165 | ? convert_float_to_bytes_little 166 | : convert_float_to_bytes_big; 167 | for (unsigned int i = 0; i < imsize; i++) { 168 | float d = data[i]; 169 | char darr[NB_HEADER_BYTE]; 170 | convfn(d, darr); 171 | unsigned int start = i * 4; 172 | for (unsigned int k = 0; k < 4; k++) { 173 | spb_data[start + k] = darr[k]; 174 | } 175 | } 176 | } 177 | 178 | void write_file(const char *path, uint32_t width, 179 | uint32_t height, uint32_t nb_channels, 180 | float first_wavelength, 181 | float wavelength_resolution, 182 | float last_wavelength, float *data) { 183 | std::ofstream file; 184 | file.open(path, std::ios::binary | std::ios::out); 185 | char hinfo[HEADER_SIZE]; 186 | write_header(hinfo, width, height, nb_channels, 187 | first_wavelength, wavelength_resolution, 188 | last_wavelength); 189 | int spb_size = width * height * nb_channels * 4; 190 | char *spb_data = new char[spb_size]; 191 | uint32_t imsize = width * height * nb_channels; 192 | write_data(spb_data, imsize, data); 193 | int spb_file_size = spb_size + HEADER_SIZE; 194 | char *spb_file = new char[spb_file_size]; 195 | for (int i = 0; i < HEADER_SIZE; i++) { 196 | spb_file[i] = hinfo[i]; 197 | } 198 | for (int i = 0; i < spb_size; i++) { 199 | spb_file[HEADER_SIZE + i] = spb_data[i]; 200 | } 201 | file.write(spb_file, spb_file_size); 202 | delete[] spb_data; 203 | delete[] spb_file; 204 | } 205 | } 206 | 207 | #endif 208 | --------------------------------------------------------------------------------