diff --git a/rawpy/__init__.py b/rawpy/__init__.py index 9292975..88d8c48 100644 --- a/rawpy/__init__.py +++ b/rawpy/__init__.py @@ -5,12 +5,14 @@ import rawpy._rawpy globals().update({k:v for k,v in rawpy._rawpy.__dict__.items() if not k.startswith('_')}) -def imread(pathOrFile): +def imread(pathOrFile, shot_select=0): """ Convenience function that creates a :class:`rawpy.RawPy` instance, opens the given file, and returns the :class:`rawpy.RawPy` instance for further processing. :param str|file pathOrFile: path or file object of RAW image that will be read + :param int shot_select: select which image to extract from RAW files that contain multiple images + (e.g., Dual Pixel RAW). Default is 0 for the first/main image. :rtype: :class:`rawpy.RawPy` """ d = RawPy() @@ -18,4 +20,5 @@ def imread(pathOrFile): d.open_buffer(pathOrFile) else: d.open_file(pathOrFile) + d.set_unpack_params(shot_select=shot_select) return d \ No newline at end of file diff --git a/rawpy/_rawpy.pyx b/rawpy/_rawpy.pyx index c7c5710..68013cd 100644 --- a/rawpy/_rawpy.pyx +++ b/rawpy/_rawpy.pyx @@ -102,7 +102,6 @@ cdef extern from "libraw.h": double aber[4] # -C double gamm[6] # -g float user_mul[4] # -r mul0 mul1 mul2 mul3 - unsigned shot_select # -s float bright # -b float threshold # -n int half_size # -h @@ -165,6 +164,18 @@ cdef extern from "libraw.h": # Force use x3f data decoding either if demosaic pack GPL2 enabled int force_foveon_x3f + ctypedef struct libraw_raw_unpack_params_t: + int use_rawspeed + int use_dngsdk + unsigned options + unsigned shot_select + unsigned specials + unsigned max_raw_memory_mb + int sony_arw2_posterization_thr + float coolscan_nef_gamma + char p4shot_order[5] + char **custom_camera_strings + ctypedef struct libraw_iparams_t: char make[64] char model[64] @@ -183,6 +194,7 @@ cdef extern from "libraw.h": libraw_image_sizes_t sizes libraw_iparams_t idata libraw_output_params_t params + libraw_raw_unpack_params_t rawparams # unsigned int progress_flags # unsigned int process_warnings libraw_colordata_t color @@ -440,6 +452,21 @@ cdef class RawPy: e = self.p.open_buffer(buf, buf_len) self.handle_error(e) + def set_unpack_params(self, shot_select=0): + """ + Set parameters that affect RAW image unpacking. + + This should be called after opening a file and before unpacking. + + .. NOTE:: This is a low-level method. When using :func:`rawpy.imread`, + unpack parameters can be provided directly. + + :param int shot_select: select which image to extract from RAW files that contain multiple images + (e.g., Dual Pixel RAW). Default is 0 for the first/main image. + """ + cdef libraw_raw_unpack_params_t* rp = &self.p.imgdata.rawparams + rp.shot_select = shot_select + def unpack(self): """ Unpacks/decodes the opened RAW image. @@ -1208,7 +1235,7 @@ class Params(object): self.aber = (chromatic_aberration[0], chromatic_aberration[1]) else: self.aber = (1, 1) - self.bad_pixels = bad_pixels_path + self.bad_pixels = bad_pixels_path cdef class processed_image_wrapper: cdef RawPy raw diff --git a/test/test_shot_select.py b/test/test_shot_select.py new file mode 100644 index 0000000..4c74ce0 --- /dev/null +++ b/test/test_shot_select.py @@ -0,0 +1,46 @@ +""" +Test for shot_select parameter functionality +""" +import os +import pytest +import rawpy + +thisDir = os.path.dirname(__file__) +rawTestPath = os.path.join(thisDir, 'iss030e122639.NEF') + + +def test_shot_select_parameter_via_imread(): + """Test that shot_select parameter can be passed to imread""" + # Test default shot_select=0 + with rawpy.imread(rawTestPath, shot_select=0) as raw: + rgb = raw.postprocess(no_auto_bright=True) + assert rgb is not None + assert rgb.shape[2] == 3 # RGB image + + +def test_shot_select_nonexistent_image(): + """Test that shot_select=1 raises error for single-image files""" + # Test shot_select=1 on a file with only one image should raise an error + with pytest.raises(rawpy.LibRawRequestForNonexistentImageError): + with rawpy.imread(rawTestPath, shot_select=1) as raw: + rgb = raw.postprocess(no_auto_bright=True) + + +def test_shot_select_via_open_file(): + """Test that shot_select can be set via set_unpack_params""" + raw = rawpy.RawPy() + raw.open_file(rawTestPath) + raw.set_unpack_params(shot_select=0) + raw.unpack() + rgb = raw.postprocess() + assert rgb is not None + assert rgb.shape[2] == 3 + raw.close() + + +def test_shot_select_default_value(): + """Test that default shot_select value is 0""" + with rawpy.imread(rawTestPath) as raw: + rgb = raw.postprocess() + assert rgb is not None + assert rgb.shape[2] == 3