diff --git a/README.md b/README.md index 61fd22ca..f2b1cb03 100644 --- a/README.md +++ b/README.md @@ -27,3 +27,20 @@ A set of tutorial videos introducing aspects of ISETCam and the companion tool f * May 10, 2024 We work more smoothly with EXR files, including sceneFromFile now reading in EXR files, and writing out sensor2EXR) This work was implemented for the extensions to HDR imaging and application of the Restormer PyTorch network for demosaicing sensor data. * April 15, 2024 Implemented a remote copy function ieSCP, to help with the distributed nature of our assets and datafiles +## Octave Support +- July 14, 2025: Ayush Jamdar. +- New: A function `isOctave` tests if code is being run in Octave and uses modified code accordingly. +- GNU Octave is an free open-source alternative to MATLAB. While MATLAB (> R2022b) has its own tools to read `exr` files, Octave doesn't have native support for this and relies on other tools like `openexr`. +- We have added/modified `exrinfo.cpp`, `exrread.cpp`, `exrreadchannels.cpp`, `exrwrite.cpp`, and `exrwritechannels.cpp` to work with Octave-compatible headers and datatypes. +- These readers and writers are completely based on OpenEXR tools. One doesn't need MATLAB to run these scripts. +- `make.m` calls `mkoctfile` with the `--mex` flag so that the executable functions hence generated can be called by Octave. +- We've added `octave-functions/` that replicate the behaviour of certain MATLAB functions. +- We only used the Octave CLI; the `ipWindow` GUIs don't yet work. +- We have tested `isethdrsensor/scripts/fullSimulation.m` on examples from the ISET HDR dataset. +- Known warnings that Octave throws: + - Octave can't read `filterNames` from the `.mat` files. As a temporary fix, we've hardcoded r, g, b filter names. + - After the recent updates to `isetcam/main`, Octave throws a new but benign warning "Invalid UTF-8 byte sequences have been replaced." + +## Instructions for Octave Packages +- Get kernel-level packages for a conda environment from `environment.yml` +- Get packages for Octave as listed in `octave-requirements.txt` diff --git a/environment.yml b/environment.yml new file mode 100644 index 00000000..6ae7e0fb --- /dev/null +++ b/environment.yml @@ -0,0 +1,218 @@ +name: octave-env +channels: + - conda-forge + - defaults +dependencies: + - _libgcc_mutex=0.1 + - _openmp_mutex=4.5 + - _x86_64-microarch-level=4 + - alsa-lib=1.2.14 + - arpack=3.9.1 + - attr=2.5.1 + - binutils=2.44 + - binutils_impl_linux-64=2.44 + - binutils_linux-64=2.44 + - bzip2=1.0.8 + - c-ares=1.34.5 + - c-compiler=1.10.0 + - ca-certificates=2025.7.9 + - cairo=1.18.4 + - curl=8.14.1 + - cxx-compiler=1.10.0 + - cyrus-sasl=2.1.28 + - dbus=1.16.2 + - fftw=3.3.10 + - fltk=1.3.10 + - font-ttf-dejavu-sans-mono=2.37 + - font-ttf-inconsolata=3.000 + - font-ttf-source-code-pro=2.038 + - font-ttf-ubuntu=0.83 + - fontconfig=2.15.0 + - fonts-conda-ecosystem=1 + - fonts-conda-forge=1 + - freetype=2.13.3 + - fribidi=1.0.10 + - gcc=13.3.0 + - gcc_impl_linux-64=13.3.0 + - gcc_linux-64=13.3.0 + - gettext=0.25.1 + - gettext-tools=0.25.1 + - gfortran=13.3.0 + - gfortran_impl_linux-64=13.3.0 + - gfortran_linux-64=13.3.0 + - ghostscript=10.04.0 + - giflib=5.2.2 + - gl2ps=1.4.2 + - glib=2.84.2 + - glib-tools=2.84.2 + - glpk=5.0 + - gmp=6.3.0 + - gnuplot=5.4.10 + - graphicsmagick=1.3.45 + - graphite2=1.3.14 + - gst-plugins-base=1.24.11 + - gstreamer=1.24.11 + - gxx=13.3.0 + - gxx_impl_linux-64=13.3.0 + - gxx_linux-64=13.3.0 + - harfbuzz=11.2.1 + - hdf5=1.14.6 + - icu=75.1 + - imath=3.1.12 + - intel-openmp=2022.0.1 + - kernel-headers_linux-64=3.10.0 + - keyutils=1.6.1 + - krb5=1.21.3 + - lame=3.100 + - ld_impl_linux-64=2.44 + - lerc=4.0.0 + - libaec=1.1.4 + - libamd=3.3.3 + - libasprintf=0.25.1 + - libasprintf-devel=0.25.1 + - libblas=3.9.0 + - libbtf=2.3.2 + - libcamd=3.3.3 + - libcap=2.75 + - libcblas=3.9.0 + - libccolamd=3.3.4 + - libcholmod=5.3.1 + - libclang-cpp20.1=20.1.7 + - libclang13=20.1.7 + - libcolamd=3.3.4 + - libcups=2.3.3 + - libcurl=8.14.1 + - libcxsparse=4.4.1 + - libdeflate=1.24 + - libdrm=2.4.125 + - libedit=3.1.20250104 + - libegl=1.7.0 + - libev=4.33 + - libevent=2.1.12 + - libexpat=2.7.0 + - libffi=3.4.6 + - libflac=1.4.3 + - libfreetype=2.13.3 + - libfreetype6=2.13.3 + - libgcc=15.1.0 + - libgcc-devel_linux-64=13.3.0 + - libgcc-ng=15.1.0 + - libgcrypt-lib=1.11.1 + - libgd=2.3.3 + - libgettextpo=0.25.1 + - libgettextpo-devel=0.25.1 + - libgfortran=15.1.0 + - libgfortran-ng=15.1.0 + - libgfortran5=15.1.0 + - libgl=1.7.0 + - libglib=2.84.2 + - libglu=9.0.3 + - libglvnd=1.7.0 + - libglx=1.7.0 + - libgomp=15.1.0 + - libgpg-error=1.55 + - libiconv=1.18 + - libjpeg-turbo=3.1.0 + - libklu=2.3.5 + - liblapack=3.9.0 + - liblapacke=3.9.0 + - libldl=3.3.2 + - libllvm20=20.1.8 + - liblzma=5.8.1 + - liblzma-devel=5.8.1 + - libmpdec=4.0.0 + - libnghttp2=1.64.0 + - libntlm=1.8 + - libogg=1.3.5 + - libopenblas=0.3.30 + - libopengl=1.7.0 + - libopus=1.5.2 + - libparu=1.0.0 + - libpciaccess=0.18 + - libpng=1.6.50 + - libpq=17.5 + - librbio=4.3.4 + - libsanitizer=13.3.0 + - libsndfile=1.2.2 + - libspex=3.2.3 + - libspqr=4.3.4 + - libsqlite=3.50.2 + - libssh2=1.11.1 + - libstdcxx=15.1.0 + - libstdcxx-devel_linux-64=13.3.0 + - libstdcxx-ng=15.1.0 + - libsuitesparseconfig=7.10.1 + - libsystemd0=257.7 + - libtiff=4.7.0 + - libumfpack=6.3.5 + - libuuid=2.38.1 + - libvorbis=1.3.7 + - libwebp=1.6.0 + - libwebp-base=1.6.0 + - libxcb=1.17.0 + - libxcrypt=4.4.36 + - libxkbcommon=1.10.0 + - libxml2=2.13.8 + - libzlib=1.3.1 + - lz4-c=1.10.0 + - metis=5.1.0 + - mkl=2022.1.0 + - mpfr=4.2.1 + - mpg123=1.32.9 + - ncurses=6.5 + - nspr=4.36 + - nss=3.113 + - octave=9.4.0 + - openexr=3.3.4 + - openldap=2.6.10 + - openssl=3.5.1 + - packaging=25.0 + - pango=1.56.4 + - pcre=8.45 + - pcre2=10.45 + - perl=5.32.1 + - pip=25.1.1 + - pixman=0.46.2 + - ply=3.11 + - portaudio=19.7.0 + - pthread-stubs=0.4 + - pulseaudio-client=17.0 + - pyqt=5.15.11 + - pyqt5-sip=12.17.0 + - python=3.13.5 + - python_abi=3.13 + - qhull=2020.2 + - qscintilla2=2.14.1 + - qt-main=5.15.15 + - readline=8.2 + - setuptools=80.9.0 + - sip=6.10.0 + - suitesparse=7.10.1 + - sundials=7.3.0 + - sysroot_linux-64=2.17 + - texinfo=7.2 + - tk=8.6.13 + - toml=0.10.2 + - tomli=2.2.1 + - tzdata=2025b + - xcb-util=0.4.1 + - xcb-util-image=0.4.0 + - xcb-util-keysyms=0.4.1 + - xcb-util-renderutil=0.3.10 + - xcb-util-wm=0.4.2 + - xkeyboard-config=2.45 + - xorg-libice=1.1.2 + - xorg-libsm=1.2.6 + - xorg-libx11=1.8.12 + - xorg-libxau=1.0.12 + - xorg-libxcomposite=0.4.6 + - xorg-libxdamage=1.1.6 + - xorg-libxdmcp=1.1.5 + - xorg-libxext=1.3.6 + - xorg-libxfixes=6.0.1 + - xorg-libxrender=0.9.12 + - xorg-libxshmfence=1.3.3 + - xorg-libxxf86vm=1.1.6 + - zlib=1.3.1 + - zstd=1.5.7 +prefix: /home/OVT/ayush.jamdar/miniconda3/envs/iset-lfm diff --git a/imgproc/openexr/ImfToMatlab.cpp b/imgproc/openexr/ImfToMatlab.cpp index cfaa9538..cb810a87 100644 --- a/imgproc/openexr/ImfToMatlab.cpp +++ b/imgproc/openexr/ImfToMatlab.cpp @@ -24,7 +24,12 @@ #include #include -#include + +#ifdef OCTAVE + // #include +#else + #include +#endif #ifdef __clang__ #pragma clang diagnostic push diff --git a/imgproc/openexr/ImfToMatlab.h b/imgproc/openexr/ImfToMatlab.h index 7eaab13f..bc92b6a0 100644 --- a/imgproc/openexr/ImfToMatlab.h +++ b/imgproc/openexr/ImfToMatlab.h @@ -24,10 +24,14 @@ #pragma once #include - -#include #include +#ifdef OCTAVE + // #include +#else + #include +#endif + // Utilities to convert from OpenEXR types to Matlab types diff --git a/imgproc/openexr/MatlabToImf.h b/imgproc/openexr/MatlabToImf.h index 4c7dc503..558b2371 100644 --- a/imgproc/openexr/MatlabToImf.h +++ b/imgproc/openexr/MatlabToImf.h @@ -306,10 +306,17 @@ inline void convertData(TargetType * dest, switch(srcType) { case mxDOUBLE_CLASS: - convertData(dest, pa, len); + #ifdef OCTAVE + convertData(dest, pa, len); + #else + convertData(dest, pa, len); break; case mxSINGLE_CLASS: - convertData(dest, pa, len); + #ifdef OCTAVE + convertData(dest, pa, len); + #else + convertData(dest, pa, len); + #endif break; case mxINT8_CLASS: convertData(dest, pa, len); diff --git a/imgproc/openexr/README.md b/imgproc/openexr/README.md index 0cfe7dc1..61619576 100644 --- a/imgproc/openexr/README.md +++ b/imgproc/openexr/README.md @@ -56,3 +56,11 @@ You may also need to update `~/.matlab/YOUR_MATLAB_VERSION/mexopts.sh` (e.g. in 300 300 3 >> exrwrite(a, 'a.exr'); + + +# Octave Support +- July 24, 2025: Ayush Jamdar +- New: Added `#ifdef OCTAVE` tests in cpp files. During compile, Octave's `mkoctfile` defines a macro `OCTAVE` automatically. MATLAB does not. +- Modified `cpp` files to generate `mex` executables compatible with Octave. +- Run `make.m` to generage the executable functions. +- The `dev-octave` branch has been tested to work with Octave 6.4.0 diff --git a/imgproc/openexr/exrinfo.cpp b/imgproc/openexr/exrinfo.cpp new file mode 100644 index 00000000..a0741954 --- /dev/null +++ b/imgproc/openexr/exrinfo.cpp @@ -0,0 +1,190 @@ +#if _MSC_VER >= 1600 +# define CHAR16_T wchar_t +#endif + +#include "ImfToMatlab.h" + +#include "mex.h" // ONLY include this, not oct.h + +#ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wlong-long" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #pragma clang diagnostic ignored "-Wextra" + #pragma clang diagnostic pop +#endif + +// OpenEXR headers +#include +#include +#include +#include +#include + +#ifdef OCTAVE + // #include +#else + #include +#endif + +// Math + exception handling +#include +#include + +// Local utilities +#include "utilities.h" + +#include +#include +#include + +// Namespaces +using namespace OPENEXR_IMF_NAMESPACE; // Use this for OpenEXR 2.x +using Imath::Box2i; + +// Optionally: +using OpenEXRforMatlab::toMatlab; +using OpenEXRforMatlab::fromArray; + + +namespace +{ + +// Temporaty struct to hold a name and a matlab value +struct Pair +{ + const char * name; + mxArray * value; + + Pair() : name(NULL), value(NULL) {} + + Pair(const char * pName, mxArray * pValue = NULL) : + name(pName), value(pValue) + {} + + bool isValid() const { + return name != NULL && value != NULL; + } +}; + + + +// Create a cell array with only the name of the channels +mxArray * getChannelNames(const ChannelList & channels) +{ + typedef ChannelList::ConstIterator ChannelIterator; + + std::vector channelNames; + for (ChannelIterator it = channels.begin(); it != channels.end(); ++it) + { + mxArray * mStr = mxCreateString(it.name()); + channelNames.push_back(mStr); + } + assert(!channelNames.empty()); + + mxArray * cells = mxCreateCellMatrix(1, channelNames.size()); + for (size_t i = 0; i != channelNames.size(); ++i) { + mxSetCell(cells, i, channelNames[i]); + } + return cells; +} + + + +// Create and populate a containters.Map object with the attributes +mxArray * getAttributesMap(const Header & header) +{ + std::vector attributes; + + // Get the full set of attributes + for (Header::ConstIterator it = header.begin(); it != header.end(); ++it) { + const Attribute & attr = it.attribute(); + if (!Attribute::knownType(attr.typeName())) { + continue; + } + + Pair pair(it.name()); + pair.value = toMatlab(attr); + if (pair.isValid()) { + attributes.push_back(pair); + } + } + assert(!attributes.empty()); + + // Create cell arrays with the attributes' names and values + mxArray * nameCell = mxCreateCellMatrix(1, attributes.size()); + mxArray * valueCell = mxCreateCellMatrix(1, attributes.size()); + for (size_t i = 0; i != attributes.size(); ++i) { + mxSetCell(nameCell, i, mxCreateString(attributes[i].name)); + mxSetCell(valueCell, i, attributes[i].value); + } + + // Create the attributes map + mxArray * mapHandle = NULL; + mxArray * mapArgs[2] = {nameCell, valueCell}; + if (mexCallMATLAB(1, &mapHandle, 2, &mapArgs[0], "containers.Map") != 0) { + mexErrMsgIdAndTxt("OpenEXR:exception", + "Could not create the attribute map."); + return NULL; + } + + return mapHandle; +} + + +} // namespace + + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + /* Check for proper number of arguments */ + if (nrhs != 1) { + mexErrMsgIdAndTxt("OpenEXR:argument", "The filename is required."); + } else if (nlhs > 1) { + mexErrMsgIdAndTxt("OpenEXR:argument", "Too many output arguments."); + } + + char *inputfilePtr = mxArrayToString(prhs[0]); + if (inputfilePtr == NULL) { + mexErrMsgIdAndTxt("OpenEXR:argument", "Invalid filename argument."); + } + // Copy to a string so that the matlab memory may be freed asap + const std::string inputfile(inputfilePtr); + mxFree(inputfilePtr); inputfilePtr = static_cast(0); + + try { + // Open a file, but only read the header + InputFile image(inputfile.c_str()); + const Header & header = image.header(); + + const Box2i & dw = header.dataWindow(); + const int width = dw.max.x - dw.min.x + 1; + const int height = dw.max.y - dw.min.y + 1; + + // List of channel names + mxArray * channelNames = getChannelNames(header.channels()); + + // Attributes map + mxArray * attributesMap = getAttributesMap(header); + + // Size in matlab style (rows by columns) + const int dataSize[] = {height, width}; + mxArray * size = fromArray(dataSize); + + // Build the structure + const char* fields[] = {"channels", "size", "attributes"}; + mxArray *bStruct = mxCreateStructMatrix(1, 1, + sizeof(fields)/sizeof(const char *), &fields[0]); + mxSetField(bStruct, 0, "channels", channelNames); + mxSetField(bStruct, 0, "size", size); + mxSetField(bStruct, 0, "attributes", attributesMap); + + // Assign the result + plhs[0] = bStruct; + } + catch( std::exception &e ) { + mexErrMsgIdAndTxt("OpenEXR:exception", e.what()); + } +} + diff --git a/imgproc/openexr/exrinfo.m b/imgproc/openexr/exrinfo.m new file mode 100644 index 00000000..5b4bfe4d --- /dev/null +++ b/imgproc/openexr/exrinfo.m @@ -0,0 +1,20 @@ +function exrinfo( filename ) +%EXRINFO Read metadata from the header of an OpenEXR image. +% INFO = EXRINFO(FILENAME) reads the header of the given OpenEXR +% file and returns a struct with the following fields: +% channels - a cell array with the names of the image channels. +% size - Matlab-style size of the image: it is an 1x2 matrix +% equivalent to [height, width]. +% attributes - a containters.Map object with the full, raw +% attributes from the file. Note that it will +% only contain those attributes for which a Matlab +% conversion is avaiable. +% +% Note: this implementation uses the ILM IlmImf library version 1.7. +% +% See also CONTAINERS.MAP,EXRREAD,EXRREADCHANNELS + +% Edgar Velazquez-Armendariz (eva5@cs.cornell.edu) +% +% (The help system uses this file, but actually doing something with it +% will employ the mex file). diff --git a/imgproc/openexr/exrinfo.mex b/imgproc/openexr/exrinfo.mex new file mode 100755 index 00000000..1ea05671 Binary files /dev/null and b/imgproc/openexr/exrinfo.mex differ diff --git a/imgproc/openexr/exrread.cpp b/imgproc/openexr/exrread.cpp new file mode 100644 index 00000000..51e3eca5 --- /dev/null +++ b/imgproc/openexr/exrread.cpp @@ -0,0 +1,205 @@ +/*============================================================================ + HDRITools - High Dynamic Range Image Tools + Copyright 2008-2011 Program of Computer Graphics, Cornell University + + Distributed under the OSI-approved MIT License (the "License"); + see accompanying file LICENSE for details. + + This software is distributed WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + See the License for more information. + ----------------------------------------------------------------------------- + Primary author: + Edgar Velazquez-Armendariz + Originally based on code by: + Jinwei Gu (2006/02/10) +============================================================================*/ + +#if _MSC_VER >= 1600 +# define CHAR16_T wchar_t +#endif + +#include "ImfToMatlab.h" + +#include + +#ifdef OCTAVE + // #include +#else + #include +#endif + +#ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wlong-long" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #pragma clang diagnostic ignored "-Wextra" + #pragma clang diagnostic pop +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "utilities.h" + +#include +#include +#include + +using namespace OPENEXR_IMF_INTERNAL_NAMESPACE; +using Imath::Box2i; + + +namespace +{ + +// Read the RGB pixels using the simplfied interface +void readPixels(float * buffer, RgbaInputFile & file) +{ + const Box2i & dw = file.header().dataWindow(); + const int width = dw.max.x - dw.min.x + 1; + const int height = dw.max.y - dw.min.y + 1; + + Array2D halfPixels(height, width); + const off_t offset = - dw.min.x - dw.min.y * width; + + file.setFrameBuffer(&halfPixels[0][0] + offset, 1, width); + file.readPixels(dw.min.y, dw.max.y); + + // Write into the target buffer + const off_t planeOffset = width * height; + float * r = buffer; + float * g = r + planeOffset; + float * b = g + planeOffset; + const off_t xStride = height; + const off_t yStride = 1; + + // Traverse the image in column-major order. Perhaps it is more + // efficient to copy the data in the same order and then call + // the native Matlab transpose, but this should be decent enough. + for (int h = 0; h < height; ++h) { + const Rgba* scanline = halfPixels[h]; + off_t xOffset = 0; + for (int w = 0; w < width; ++w, xOffset += xStride) { + const Rgba & px = scanline[w]; + const off_t idx = xOffset + h*yStride; + r[idx] = px.r; + g[idx] = px.g; + b[idx] = px.b; + } + } +} + + +// Read the RGB channels using the general interface. This load +// only the R,G,B channels of the image +void readPixels(float * buffer, InputFile & file) +{ + const Box2i & dw = file.header().dataWindow(); + const int width = dw.max.x - dw.min.x + 1; + const int height = dw.max.y - dw.min.y + 1; + + const off_t planeOffset = width * height; + float * r = buffer; + float * g = r + planeOffset; + float * b = g + planeOffset; + + FrameBuffer framebuffer; + + // The "weird" strides are because Matlab uses column-major order + const int xStride = height; + const int yStride = 1; + const off_t offset = - dw.min.x * xStride - dw.min.y * yStride; + + const ChannelList & channelList = file.header().channels(); + const char* channels[] = {"R", "G", "B"}; + float* data[] = {r, g, b}; + for (int i = 0; i < 3; ++i) { + + // Get the appropriate sampling factors + int xSampling = 1, ySampling = 1; + ChannelList::ConstIterator cIt = channelList.find(channels[i]); + if (cIt != channelList.end()) { + xSampling = cIt.channel().xSampling; + ySampling = cIt.channel().ySampling; + } + + // Insert the slice in the framebuffer + framebuffer.insert(channels[i], Slice(FLOAT, (char*)(data[i] + offset), + sizeof(float) * xStride, + sizeof(float) * yStride, + xSampling, ySampling)); + } + + // Finally read the pixels + file.setFrameBuffer(framebuffer); + file.readPixels(dw.min.y, dw.max.y); +} + + +// Main entry point. It returns an mxArray* with the appropriate RGB data +// loaded in the matlab way (column major, planar) +mxArray * readPixels(const char * filename) +{ + // Check if it is a YC file, so that we use the RGBA interface to decode it + InputFile file(filename); + const ChannelList & channels = file.header().channels(); + const bool isYC = channels.findChannel("Y") != NULL || + channels.findChannel("RY") != NULL || + channels.findChannel("BY") != NULL; + + // Allocate the requied space + const Box2i & dw = file.header().dataWindow(); + const mwSize width = dw.max.x - dw.min.x + 1; + const mwSize height = dw.max.y - dw.min.y + 1; + const mwSize dims[] = {height, width, 3}; + mxArray * data = mxCreateNumericArray(3, &dims[0], mxSINGLE_CLASS, mxREAL); + float * buffer = static_cast (mxGetData(data)); + + if (!isYC) { + readPixels(buffer, file); + } else { + OPENEXR_IMF_INTERNAL_NAMESPACE::RgbaInputFile ycFile(filename); + readPixels(buffer, ycFile); + } + + return data; +} + +} // namespace + + +void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) +{ + OpenEXRforMatlab::mexEXRInit(); + + /* Check for proper number of arguments */ + if (nrhs != 1) { + mexErrMsgIdAndTxt("OpenEXR:argument", "The filename is required."); + } else if (nlhs > 1) { + mexErrMsgIdAndTxt("OpenEXR:argument", "Too many output arguments."); + } + + char *inputfilePtr = mxArrayToString(prhs[0]); + if (inputfilePtr == NULL) { + mexErrMsgIdAndTxt("OpenEXR:argument", "Invalid filename argument."); + } + // Copy to a string so that the matlab memory may be freed asap + const std::string inputfile(inputfilePtr); + mxFree(inputfilePtr); inputfilePtr = static_cast(0); + + try { + plhs[0] = readPixels(inputfile.c_str()); + } + catch(Iex::BaseExc &e) { + mexErrMsgIdAndTxt("OpenEXR:exception", e.what()); + } +} \ No newline at end of file diff --git a/imgproc/openexr/exrread.m b/imgproc/openexr/exrread.m new file mode 100644 index 00000000..ee6b729f --- /dev/null +++ b/imgproc/openexr/exrread.m @@ -0,0 +1,34 @@ +function hdr = exrread( filename ) +%EXRREAD Read OpenEXR high dynamic range (HDR) image. +% HDR = EXRREAD(FILENAME) reads the high dynamic range image HDR from +% FILENAME, which points to an OpenEXR .exr file. HDR is an m-by-n-by-3 RGB +% array in the range (-65504,65504), plus Inf and NaN, and has type single. +% For scene-referred datasets, these values usually are scene illumination +% in radians units. To display these images, use an appropriate +% tone-mapping operator. +% +% Class Support +% ------------- +% The output image HDR is an m-by-n-by-3 image with type single. +% +% Example +% ------- +% hdr = exrread('office.hdr'); +% rgb = tonemap(hdr); +% imshow(rgb); +% +% Note: this implementation uses the ILM IlmImf library version 1.6.1. +% +% Reference: +% Florian Kainz et. al. "The OpenEXR Image File Format". GPU Gems, 2004. +% (http://developer.download.nvidia.com/books/HTML/gpugems/gpugems_ch26.html) +% Industrial Light & Magic OpenEXR website and documentation +% (http://www.openexr.com) +% +% See also EXRWRITE,TONEMAP + +% Edgar Velazquez-Armendariz (eva5@cs.cornell.edu) +% Based on the originals by Jinwei Gu (jwgu@cs.columbia.edu) +% +% (The help system uses this file, but actually doing something with it +% will employ the mex file). diff --git a/imgproc/openexr/exrread.mex b/imgproc/openexr/exrread.mex new file mode 100755 index 00000000..ff209a55 Binary files /dev/null and b/imgproc/openexr/exrread.mex differ diff --git a/imgproc/openexr/exrreadchannels.mex b/imgproc/openexr/exrreadchannels.mex new file mode 100755 index 00000000..aeae8b63 Binary files /dev/null and b/imgproc/openexr/exrreadchannels.mex differ diff --git a/imgproc/openexr/exrwrite.cpp b/imgproc/openexr/exrwrite.cpp new file mode 100644 index 00000000..e7ac45ab --- /dev/null +++ b/imgproc/openexr/exrwrite.cpp @@ -0,0 +1,204 @@ +////////////////////////////////////////////////////////////////////////// +// +// exrwrite.cpp +// +// Matlab interface for writting a float image to exr file +// +// exrwrite(img, filename) +// +// img can be 2d (gray) or 3d (color) hdr image +// +// see also exrread.cpp +// +// Jinwei Gu. 2006/02/10 +// jwgu@cs.columbia.edu +// +// Modified by Edgar Velazquez-Armendariz +// +// +// When using mex to compiling it in matlab, make sure to use VC7.1 or above +// instead of VC6. +////////////////////////////////////////////////////////////////////////// + +#if _MSC_VER >= 1600 +# define CHAR16_T wchar_t +#endif + + +#include + +#ifdef OCTAVE + // #include + // #include // Matlab types + // #include +#else + #include + #include // Matlab types + #include +#endif + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +#ifdef __clang__ + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wlong-long" + #pragma clang diagnostic ignored "-Wdeprecated-register" + #pragma clang diagnostic ignored "-Wextra" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __clang__ + #pragma clang diagnostic pop +#endif + +#include "utilities.h" +#include "ImfToMatlab.h" +#include "MatlabToImf.h" + + +using namespace OPENEXR_IMF_INTERNAL_NAMESPACE; +using Imath::Box2i; + + + +// Templated function to copy data from either a float or a double array +template +void copyPixels(Rgba *pixels, const T *img, int width, int height, + bool isMonochrome = false) +{ + // Stride to write the same value in all channels when the image + // is monochromatic + const int A = isMonochrome ? 0 : width*height; + + // Copy the pixels + for(int i=0; i(0); + + // Uses matlab's alloc, so that the memory if released when + // the control returns to matlab + Rgba *pixels = (Rgba*)mxCalloc(width*height, sizeof(Rgba)); + + const bool isMonochrome = nd==3 ? false : true; + + // We only know how to write real data, so we cast img to the + // right type, and the template will do the magic, or just + // raise an error + if (mxIsComplex(prhs[0])) { + mexErrMsgIdAndTxt( "OpenEXR:unsupported", + "Matrices of complex data are unsupported." ); + } + void *img = mxGetPr(prhs[0]); + mxClassID category = mxGetClassID(prhs[0]); + switch (category) { + case mxDOUBLE_CLASS: + copyPixels(pixels, (double*)img, width, height, isMonochrome); + break; + case mxSINGLE_CLASS: + copyPixels(pixels, (float*)img, width, height, isMonochrome); + break; + case mxINT8_CLASS: + copyPixels(pixels, (int8_T*)img, width, height, isMonochrome); + break; + case mxINT16_CLASS: + copyPixels(pixels, (int16_T*)img, width, height, isMonochrome); + break; + case mxINT32_CLASS: + copyPixels(pixels, (int32_T*)img, width, height, isMonochrome); + break; + case mxINT64_CLASS: + copyPixels(pixels, (int64_T*)img, width, height, isMonochrome); + break; + case mxUINT8_CLASS: + copyPixels(pixels, (uint8_T*)img, width, height, isMonochrome); + break; + case mxUINT16_CLASS: + copyPixels(pixels, (uint16_T*)img, width, height, isMonochrome); + break; + case mxUINT32_CLASS: + copyPixels(pixels, (uint32_T*)img, width, height, isMonochrome); + break; + case mxUINT64_CLASS: + copyPixels(pixels, (uint64_T*)img, width, height, isMonochrome); + break; + + default: + mexErrMsgIdAndTxt( "OpenEXR:unsupported", + "Matrices of type %s are unsupported.", + mxGetClassName(prhs[0]) ); + } + + try { + RgbaOutputFile file(outputfile.c_str(), width, height, WRITE_RGB); + file.setFrameBuffer(pixels, 1, width); + file.writePixels(height); + } + catch( std::exception &e ) { + mexErrMsgIdAndTxt("OpenEXR:exception", e.what()); + } + + // We don't need to explicitly delete the data because we + // allocated it with mxCalloc + /*delete[] pixels; pixels = NULL;*/ +} \ No newline at end of file diff --git a/imgproc/openexr/exrwrite.m b/imgproc/openexr/exrwrite.m new file mode 100644 index 00000000..f0116b83 --- /dev/null +++ b/imgproc/openexr/exrwrite.m @@ -0,0 +1,17 @@ +function exrwrite( hdr, filename ) +%EXRWRITE Write OpenEXR high dynamic range (HDR) image. +% EXRWRITE(HDR, FILENAME) creates an OpenEXR high dynamic range (HDR) image +% file from HDR, a real numeric high dynamic range image with either one or +% three channels per pixel (i.e. hdr is a m-by-n or m-by-n-by-3 matrix). +% The HDR file with the name filename uses the PIZ wavelet based +% compression to minimize the file size. +% +% Note: this implementation uses the ILM IlmImf library version 1.6.1. +% +% See also EXRREAD,TONEMAP + +% Edgar Velazquez-Armendariz (eva5@cs.cornell.edu) +% Based on the originals by Jinwei Gu (jwgu@cs.columbia.edu) +% +% (The help system uses this file, but actually doing something with it +% will employ the mex file). diff --git a/imgproc/openexr/exrwrite.mex b/imgproc/openexr/exrwrite.mex new file mode 100755 index 00000000..9574bfb5 Binary files /dev/null and b/imgproc/openexr/exrwrite.mex differ diff --git a/imgproc/openexr/exrwritechannels.cpp b/imgproc/openexr/exrwritechannels.cpp index 1c919ae7..2a1923a7 100644 --- a/imgproc/openexr/exrwritechannels.cpp +++ b/imgproc/openexr/exrwritechannels.cpp @@ -633,7 +633,7 @@ void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) } try { - std::auto_ptr data(prepareArguments(nrhs, prhs)); + std::unique_ptr data(prepareArguments(nrhs, prhs)); data->writeEXR(); } catch (Iex::BaseExc & e) { diff --git a/imgproc/openexr/exrwritechannels.mex b/imgproc/openexr/exrwritechannels.mex new file mode 100755 index 00000000..16aa830b Binary files /dev/null and b/imgproc/openexr/exrwritechannels.mex differ diff --git a/imgproc/openexr/make.m b/imgproc/openexr/make.m deleted file mode 100644 index 88774776..00000000 --- a/imgproc/openexr/make.m +++ /dev/null @@ -1,43 +0,0 @@ -clc; -verbose = false; - - -% ----------------------------------------------- -build_files = { 'exrinfo.cpp', ... - 'exrread.cpp', ... - 'exrreadchannels.cpp', ... - 'exrwrite.cpp', ... - 'exrwritechannels.cpp'}; - -companion_files = { 'utilities.cpp', ... - 'ImfToMatlab.cpp', ... - 'MatlabToImf.cpp'}; - -additionals = {}; -if(verbose == true) - additionals = [additionals, {'-v'}]; -end - -for n = 1:size(build_files, 2) - if(verbose == true) - clc; - end - - file = cell2mat(build_files(n)); - - disp(['Building ', file]); - - mex(file, companion_files{:}, ... - '-I/usr/local/include/OpenEXR', ... - '-I/usr/local/include/Imath',... - '-L/usr/local/lib', ... - '-lOpenEXR',... - '-lIex', ... - '-lImath', ... - '-lIlmThread', ... - '-largeArrayDims', ... - additionals{:}); -end - -clear; -disp('Finished building OpenEXR for Matlab'); \ No newline at end of file diff --git a/imgproc/openexr/make_matlab.m b/imgproc/openexr/make_matlab.m new file mode 100644 index 00000000..66bda586 --- /dev/null +++ b/imgproc/openexr/make_matlab.m @@ -0,0 +1,96 @@ +% make.m - Compile OpenEXR MEX functions for MATLAB +% -------------------------------------------------- +% This script builds the C++ MEX wrappers for OpenEXR support in MATLAB. +% It assumes dependencies (OpenEXR, Imath, etc.) are installed in a Conda environment. +% Use the companion `make_octave.m` script to build for Octave. +% +% Modified by Ayush Jamdar, based on ISETCam project code. + +clc; + +% Set verbosity +verbose = false; + +% List of primary source files to compile +build_files = { + 'exrinfo.cpp', ... + 'exrread.cpp', ... + 'exrreadchannels.cpp', ... + 'exrwrite.cpp', ... + 'exrwritechannels.cpp' +}; + +% Companion/shared utility source files +companion_files = { + 'utilities.cpp', ... + 'ImfToMatlab.cpp', ... + 'MatlabToImf.cpp' +}; + +% Extra compiler flags (e.g., verbosity) +additionals = {}; +if verbose + additionals = [additionals, {'-v'}]; +end + +% ------------------------------------------------------------------------ +% ENVIRONMENT CONFIGURATION +% ------------------------------------------------------------------------ + +% Set your Conda environment path here if needed +% Only required if OpenEXR is installed in a specific Conda env +% Change this to match your system/environment +setenv('CONDA_PREFIX', '/home/OVT/ayush.jamdar/miniconda3/envs/openexr_env'); + +% Make sure to run this script *outside* the Conda environment. +% MATLAB's MEX compiler should use system g++ (not conda’s g++) +% You may need to manually select g++: +mex -setup C++ % Choose system compiler, e.g., /usr/bin/g++ + +% ------------------------------------------------------------------------ +% COMPILATION FLAGS +% ------------------------------------------------------------------------ + +% Paths to OpenEXR headers and libraries +conda_prefix = getenv('CONDA_PREFIX'); + +include_paths = { + ['-I', fullfile(conda_prefix, 'include', 'OpenEXR')], ... + ['-I', fullfile(conda_prefix, 'include', 'Imath')] +}; + +lib_path = ['-L', fullfile(conda_prefix, 'lib')]; + +libs = { + '-lOpenEXR', ... + '-lIex', ... + '-lImath', ... + '-lIlmThread', ... + '-lz' +}; + +% ------------------------------------------------------------------------ +% BUILD LOOP +% ------------------------------------------------------------------------ + +[~, which_gpp] = system('which g++'); +disp(['Using g++ at: ', strtrim(which_gpp)]); + +for n = 1:numel(build_files) + if verbose + clc; + end + + file = build_files{n}; + disp(['Building ', file]); + + mex(file, companion_files{:}, ... + include_paths{:}, ... + lib_path, ... + libs{:}, ... + '-largeArrayDims', ... + additionals{:}); +end + +clear; +disp('Finished building OpenEXR for MATLAB'); diff --git a/imgproc/openexr/make_octave.m b/imgproc/openexr/make_octave.m new file mode 100644 index 00000000..04b601f7 --- /dev/null +++ b/imgproc/openexr/make_octave.m @@ -0,0 +1,60 @@ +% make_octave.m - Compile OpenEXR .mex/.oct files for Octave +% ------------------------------------------------------------------- +% This script builds OpenEXR-compatible C++ wrappers using Octave's `mkoctfile`. +% Ensure that your OpenEXR dependencies are installed in a Conda environment. +% +% NOTE: Some C++ header files (e.g., ImfNamespace.h) may be excluded in Octave +% for compatibility. Octave-specific macros (e.g., #ifdef OCTAVE) should be used +% in the C++ source files to handle divergence from MATLAB. +% +% Modified by Ayush Jamdar, based on ISETCam project code. + +clc; +verbose = false; + +% ------------------------------------------------- +build_files = { 'exrinfo.cpp', ... + 'exrread.cpp', ... + 'exrreadchannels.cpp', ... + 'exrwrite.cpp', ... + 'exrwritechannels.cpp'}; + +companion_files = { 'utilities.cpp', ... + 'ImfToMatlab.cpp', ... + 'MatlabToImf.cpp' }; + +% Header and library paths — adjust if needed +conda_prefix = getenv('CONDA_PREFIX'); +if isempty(conda_prefix) + error('CONDA_PREFIX environment variable is not set.'); +end + +include_paths = sprintf('-I%s/include/OpenEXR -I%s/include/Imath', ... + conda_prefix, conda_prefix); +lib_paths = ['-L', fullfile(conda_prefix, 'lib')]; +libs = '-lOpenEXR -lIex -lImath -lIlmThread -lz'; + +% Verbose +extra_flags = ''; +if verbose + extra_flags = '-v'; +end + +% Build loop +for k = 1:numel(build_files) + src = build_files{k}; + [~, outname, ~] = fileparts(src); + out = [outname, '.mex']; + cmd = sprintf('mkoctfile --mex %s %s %s %s %s %s -o %s %s', ... + include_paths, lib_paths, extra_flags, ... + src, strjoin(companion_files, ' '), libs, out); + disp(['Building ', out, '...']); + disp(['CMD: ', cmd]) + status = system(cmd); + if status ~= 0 + error(['Failed to compile: ', src]); + end +end + +clear; +disp('Finished building OpenEXR MEX files for Octave.'); \ No newline at end of file diff --git a/octave-functions/README.md b/octave-functions/README.md new file mode 100644 index 00000000..60509ca9 --- /dev/null +++ b/octave-functions/README.md @@ -0,0 +1,5 @@ +### MATLAB Functions for Octave + +- This directory hosts custom implementations of MATLAB functions that are otherwise unavailable in Octave. +- New: Included an `isOctave` test to keep the code both Matlab and Octave compatible. +Note: Some of these functions are written with the help of AI tools and have been tested on ISET examples. \ No newline at end of file diff --git a/octave-functions/insertShape.m b/octave-functions/insertShape.m new file mode 100644 index 00000000..a015e366 --- /dev/null +++ b/octave-functions/insertShape.m @@ -0,0 +1,110 @@ +% In MATLAB, this function comes from the CV Toolbox. +% Here, we replicate its functionality using a custom implementation. + +function imgOut = insertShape(img, shapeType, shapeParams, varargin) + if ndims(img) == 2 + img = repmat(img, [1, 1, 3]); % promote grayscale to RGB + end + + p = inputParser; + addParameter(p, 'Color', 'black'); + addParameter(p, 'Opacity', 1.0); + addParameter(p, 'LineWidth', 1); + parse(p, varargin{:}); + + color = parseColor(p.Results.Color); + opacity = p.Results.Opacity; + lineWidth = p.Results.LineWidth; + + imgOut = im2double(img); % Work in double precision [0,1] + + % Normalize shape name + shape = lower(strrep(strrep(shapeType, '-', ''), '_', '')); + + [imgH, imgW, ~] = size(imgOut); + + switch shape + case 'filledcircle' + % shapeParams: [x, y, r] + x = shapeParams(1); + y = shapeParams(2); + r = shapeParams(3); + [xx, yy] = meshgrid(1:imgW, 1:imgH); + mask = (xx - x).^2 + (yy - y).^2 <= r^2; + imgOut = blendMask(imgOut, mask, color, opacity); + + case 'filledrectangle' + % shapeParams: [x, y, w, h] + x = shapeParams(1); + y = shapeParams(2); + w = shapeParams(3); + h = shapeParams(4); + mask = false(imgH, imgW); + x1 = max(1, round(x)); + y1 = max(1, round(y)); + x2 = min(imgW, round(x + w - 1)); + y2 = min(imgH, round(y + h - 1)); + mask(y1:y2, x1:x2) = true; + imgOut = blendMask(imgOut, mask, color, opacity); + + case 'line' + % shapeParams: [x1, y1, x2, y2] + x1 = shapeParams(1); + y1 = shapeParams(2); + x2 = shapeParams(3); + y2 = shapeParams(4); + imgOut = drawLine(imgOut, x1, y1, x2, y2, lineWidth, color, opacity); + + otherwise + error('Unsupported shape: %s', shapeType); + end + + imgOut = im2uint8(imgOut); +end + +function color = parseColor(c) + if ischar(c) || isstring(c) + switch lower(c) + case 'black', color = [0, 0, 0]; + case 'white', color = [1, 1, 1]; + case 'red', color = [1, 0, 0]; + case 'green', color = [0, 1, 0]; + case 'blue', color = [0, 0, 1]; + otherwise, error('Unsupported color name'); + end + elseif isnumeric(c) && numel(c) == 3 + color = double(c(:))'; + if max(color) > 1, color = color / 255; end + else + error('Invalid color input'); + end +end + +function img = blendMask(img, mask, color, opacity) + for c = 1:3 + imgChannel = img(:,:,c); + imgChannel(mask) = (1 - opacity) * imgChannel(mask) + opacity * color(c); + img(:,:,c) = imgChannel; + end +end + +function img = drawLine(img, x1, y1, x2, y2, width, color, opacity) + imgTmp = insertLinePrimitive(img, [x1, y1, x2, y2], width, color); + mask = any(imgTmp ~= img, 3); + img = blendMask(img, mask, color, opacity); +end + +function imgOut = insertLinePrimitive(img, lineParams, width, color) + % Uses built-in line plotting in a blank canvas + imgOut = img; + h = figure('Visible','off'); + imshow(zeros(size(img)), []); + hold on; + line(lineParams([1,3]), lineParams([2,4]), 'Color', color, 'LineWidth', width); + F = getframe(gca); + close(h); + maskRGB = im2double(frame2im(F)); + maskRGB = imresize(maskRGB, [size(img,1), size(img,2)]); + mask = any(maskRGB > 0.01, 3); % binary mask + imgOut = blendMask(imgOut, mask, color, 1); % solid blend +end diff --git a/octave-functions/isOctave.m b/octave-functions/isOctave.m new file mode 100644 index 00000000..f7f1b704 --- /dev/null +++ b/octave-functions/isOctave.m @@ -0,0 +1,14 @@ +% Test if running in Octave + +% This function will be called whenever code differs between +% Matlab and Octave, with the goal to extend ISET support + +function retval = isOctave + persistent cacheval; + if isempty (cacheval) + cacheval = (exist ("OCTAVE_VERSION", "builtin") > 0); + end + retval = cacheval; +end + + diff --git a/octave-functions/nsidedpoly.m b/octave-functions/nsidedpoly.m new file mode 100644 index 00000000..b9277f28 --- /dev/null +++ b/octave-functions/nsidedpoly.m @@ -0,0 +1,22 @@ +function poly = nsidedpoly(n, varargin) + % Octave-compatible replacement for nsidedpoly (regular polygon) + % poly = nsidedpoly(n, 'Center', [cx, cy], 'Radius', r, 'Rotation', angle) + + p = inputParser; + addParameter(p, 'Center', [0, 0]); + addParameter(p, 'Radius', 1); + addParameter(p, 'Rotation', 0); % in degrees + parse(p, varargin{:}); + + center = p.Results.Center; + radius = p.Results.Radius; + rot = deg2rad(p.Results.Rotation); + + % Generate regular polygon vertices + theta = (0:n-1)' * (2*pi/n) + rot; + x = center(1) + radius * cos(theta); + y = center(2) + radius * sin(theta); + + % Output struct similar to MATLAB's nsidedpoly result + poly.Vertices = [x, y]; +end diff --git a/octave-functions/strings.m b/octave-functions/strings.m new file mode 100644 index 00000000..b386f010 --- /dev/null +++ b/octave-functions/strings.m @@ -0,0 +1,8 @@ +function out = strings(varargin) + %STRINGS Emulate MATLAB's strings() in Octave using cell array of char vectors + + dims = cell2mat(varargin); + + % Use cell array of empty character vectors + out = repmat({''}, dims); +end diff --git a/octave-requirements.txt b/octave-requirements.txt new file mode 100644 index 00000000..606bfb08 --- /dev/null +++ b/octave-requirements.txt @@ -0,0 +1,9 @@ +Package Name | Version +--------------+--------- + control *| 4.1.2 + general *| 2.1.3 + image *| 2.16.1 + io *| 2.7.0 + optiminterp *| 0.3.7 + signal *| 1.4.6 + statistics *| 1.7.4 \ No newline at end of file diff --git a/opticalimage/oiSet.m b/opticalimage/oiSet.m index aa294bf7..5a6ab6d7 100644 --- a/opticalimage/oiSet.m +++ b/opticalimage/oiSet.m @@ -289,7 +289,7 @@ case {'computemethod'} % Compute method. Only applies for shift invariant - if (strcmp(val,'opticspsf') | strcmp(val,'opticsotf') | strcmp(val,'humanmw')) + if (strcmp(val,'opticspsf') || strcmp(val,'opticsotf') || strcmp(val,'humanmw')) oi.computeMethod = val; elseif (isempty(val)) oi.computeMethod = val; diff --git a/scene/pattern/sceneHDRLights.m b/scene/pattern/sceneHDRLights.m index 211d0181..9f2d7ff4 100644 --- a/scene/pattern/sceneHDRLights.m +++ b/scene/pattern/sceneHDRLights.m @@ -54,8 +54,12 @@ xvals = round(linspace(0.2,0.8,nCircles)*imSize); radius = radius*imSize; for ii = 1:numel(xvals) - cc = mod(ii,numel(cColors)) + 1; - img = insertShape(img,'filled-circle',[xvals(ii),y,radius(ii)],'Color',cColors{cc}); + cc = mod(ii,numel(cColors)) + 1; + if isOctave () + img = insertShape(img,'filledcircle',[xvals(ii),y,radius(ii)],'Color',cColors{cc}); + else + img = insertShape(img,'filled-circle',[xvals(ii),y,radius(ii)],'Color',cColors{cc}); + end end %% Put lines of different thickness and orientation around the middle @@ -69,7 +73,11 @@ hw = round([1,7*lineLength; 1,3*lineLength; 3*lineLength,1; 8*lineLength,1]); for ii = 1:numel(xvals) cc = mod(ii,numel(lColors)) + 1; - img = insertShape(img,'filled-rectangle',[xvals(ii),y,hw(ii,1),hw(ii,2)],'Color',lColors{cc}); + if isOctave() + img = insertShape(img,'filledrectangle',[xvals(ii),y,hw(ii,1),hw(ii,2)],'Color',lColors{cc}); + else + img = insertShape(img,'filled-rectangle',[xvals(ii),y,hw(ii,1),hw(ii,2)],'Color',lColors{cc}); + end end %% Squares @@ -78,7 +86,11 @@ squareEdge = imSize/64; hw = [2 2; 5 5; 9 9]*squareEdge; for ii = 1:numel(xvals) - img = insertShape(img,'filled-rectangle',[xvals(ii),y,hw(ii,1),hw(ii,2)],'Color','white'); + if isOctave() + img = insertShape(img,'filledrectangle',[xvals(ii),y,hw(ii,1),hw(ii,2)],'Color','white'); + else + img = insertShape(img,'filled-rectangle',[xvals(ii),y,hw(ii,1),hw(ii,2)],'Color','white'); + end end % ieNewGraphWin; imagesc(img); axis image diff --git a/sensor/sensorComputeArray.m b/sensor/sensorComputeArray.m index c01ec41b..0cb606b1 100644 --- a/sensor/sensorComputeArray.m +++ b/sensor/sensorComputeArray.m @@ -193,7 +193,8 @@ % The names are usually ovt-large or imx490-small. So we split on the % '-' to create the name. -tmp = split(sensorGet(sensors(1),'name'),'-'); thisDesign = tmp{1}; +tmp = strsplit(sensorGet(sensors(1),'name'),'-'); +thisDesign = tmp{1}; sensorCombined = sensorSet(sensorCombined,'name',sprintf('%s-%s',thisDesign,method)); diff --git a/sensor/sensorCreate.m b/sensor/sensorCreate.m index 6a1c37fb..85a80b6d 100644 --- a/sensor/sensorCreate.m +++ b/sensor/sensorCreate.m @@ -357,10 +357,19 @@ end qeFile = fullfile(isetRootPath,'data','sensor','colorfilters','OVT','ovt-large.mat'); - wave = sensorGet(sensor,'wave'); - [data,filterNames] = ieReadColorFilter(wave,qeFile); - sensor = sensorSet(sensor,'filter spectra',data); - sensor = sensorSet(sensor,'filter names',filterNames); + + wave = sensorGet(sensor, 'wave'); + + if isOctave() + % Hardcoded filter names (since Octave can't read the cell array from .mat) + [data, ~] = ieReadColorFilter(wave, qeFile); + filterNames = {'r', 'g', 'b'}; + else + [data,filterNames] = ieReadColorFilter(wave,qeFile); + end + + sensor = sensorSet(sensor, 'filter spectra', data); + sensor = sensorSet(sensor, 'filter names', filterNames); % LPD-HCG - Higher voltage output sensor(2) = sensor(1); @@ -401,12 +410,21 @@ % We have the small, but correcting for the differences is a % challenge. So, we use the large twice. % qeFile = fullfile(isetRootPath,'data','sensor','colorfilters','OVT','ovt-small.mat'); - qeFile = fullfile(isetRootPath,'data','sensor','colorfilters','OVT','ovt-large.mat'); + qeFile = fullfile(isetRootPath, 'data', 'sensor', 'colorfilters', 'OVT', 'ovt-large.mat'); + + wave = sensorGet(sensor, 'wave'); + + if isOctave() + % Hardcoded filter names (since Octave can't read the cell array from .mat) + [data, ~] = ieReadColorFilter(wave, qeFile); + filterNames = {'r', 'g', 'b'}; + else + [data,filterNames] = ieReadColorFilter(wave,qeFile); + end + + sensor = sensorSet(sensor, 'filter spectra', data); + sensor = sensorSet(sensor, 'filter names', filterNames); - wave = sensorGet(sensor,'wave'); - [data,filterNames] = ieReadColorFilter(wave,qeFile); - sensor = sensorSet(sensor,'filter spectra',data); - sensor = sensorSet(sensor,'filter names',filterNames); return; diff --git a/utility/dll70/md5/md5.cpp b/utility/dll70/md5/md5.cpp index 67814895..d6233a10 100644 --- a/utility/dll70/md5/md5.cpp +++ b/utility/dll70/md5/md5.cpp @@ -60,7 +60,11 @@ char* MD5File(char* szFilename); * */ #include -#include +#ifdef OCTAVE + // #include +#else + #include +#endif /********************************************************************* diff --git a/utility/programming/ieParamFormat.m b/utility/programming/ieParamFormat.m index 6688b9db..61c1b872 100644 --- a/utility/programming/ieParamFormat.m +++ b/utility/programming/ieParamFormat.m @@ -40,14 +40,14 @@ end % If it definitely isn't a string, return it. -if (~ischar(s) && ~iscell(s) & ~isstring(s)) +if (~ischar(s) && ~iscell(s)&& ~isstring(s)) % error('s has to be a character array or cell array'); sformatted = s; return; end % Lower case -if (ischar(s) | isstring(s)) +if (ischar(s) || isstring(s)) % To lower and remove spaces sformatted = lower(s); sformatted = strrep(sformatted,' ','');