diff --git a/.flake8 b/.flake8 deleted file mode 100644 index f9b52a9..0000000 --- a/.flake8 +++ /dev/null @@ -1,12 +0,0 @@ -[flake8] -exclude = - .git, - __pycache__, - build, - dist, - versioneer.py, - csxtools/doc/conf.py - *.ipynb_checkpoints, - -max-line-length = 140 -ignore = E203, W503 \ No newline at end of file diff --git a/.github/workflows/flake8.yml b/.github/workflows/flake8.yml deleted file mode 100644 index 528bc64..0000000 --- a/.github/workflows/flake8.yml +++ /dev/null @@ -1,23 +0,0 @@ -name: Check Code Style - FLAKE8 - -on: [push, pull_request] - -jobs: - lint: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-python@v4 - with: - python-version: "3.10" - - name: Install Dependencies - run: | - # These packages are installed in the base environment but may be older - # versions. Explicitly upgrade them because they often create - # installation problems if out of date. - python -m pip install --upgrade pip setuptools numpy - - pip install flake8 - - name: Run flake8 - run: | - flake8 diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 0000000..599d4cf --- /dev/null +++ b/.github/workflows/ruff.yml @@ -0,0 +1,18 @@ +name: Check Code Style - RUFF + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install Dependencies + run: | + python -m pip install --upgrade pip setuptools numpy + pip install ruff + - name: Run ruff + run: ruff check . diff --git a/csxtools/helpers/fastccd.py b/csxtools/helpers/fastccd.py index 30277ca..9e0df48 100644 --- a/csxtools/helpers/fastccd.py +++ b/csxtools/helpers/fastccd.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) -def browse_3Darray(res, title="Frame"): # , extra_scalar_dict=None): +def browse_3Darray(res, fig, ax, im, title="Frame"): # , extra_scalar_dict=None): """Widget for notebooks. Sliding bar to browse 3D python array. Must plot using subplots method with 1 axes. res : 3D array with the first element being interated @@ -21,24 +21,15 @@ def browse_3Darray(res, title="Frame"): # , extra_scalar_dict=None): """ N = len(res) - def view_image(i=0): + def view_image(i=0, fig = fig, ax = ax, im = im): im.set_data(res[i]) - # if extra_scalar_dict is not None: - # key = extra_scalr_dict.keys()[0] - # values = extra_scalar_dict.values() - - # if extra_scalar_dict is None: - # ax.set_title(f'{title} {i} {key} {values[i]}') - # else: ax.set_title(f"{title} {i}") fig.canvas.draw_idle() interact(view_image, i=(0, N - 1)) - # FCCD specific stuff starts here - def find_possible_darks( header, dark_gain, diff --git a/csxtools/settings.py b/csxtools/settings.py index cdd9c41..462c331 100644 --- a/csxtools/settings.py +++ b/csxtools/settings.py @@ -1,8 +1,4 @@ detectors = {} detectors["fccd"] = "fccd_image" -detectors["axis1"] = "axis1_image" -detectors["axis"] = "axis_image" -detectors["axis_standard"] = "axis_standard_image" -detectors["axis_cont"] = "axis_cont_image" diff_angles = ["delta", "theta", "gamma", None, None, None] diff --git a/csxtools/utils.py b/csxtools/utils.py index 38795a8..35dfb1b 100644 --- a/csxtools/utils.py +++ b/csxtools/utils.py @@ -128,42 +128,35 @@ def get_fastccd_images( def get_axis_images(light_header, dark_header=None, flat=None, tag=None, roi=None): - """Retreive and correct AXIS Images from associated headers - - Retrieve AXIS Images from databroker and correct for: - - - Bad Pixels (converted to ``np.nan``) - - Backgorund. - - Flatfield correction. - - Rotation (returned images are rotated 90 deg cw) - + """Retrieve and correct AXIS Images from associated headers. + + This function retrieves AXIS images from a databroker header and applies several corrections: + - **Bad Pixels**: Converts bad pixels to ``np.nan``. + - **Background Subtraction**: Subtracts a background from the images. + - **Flatfield Correction**: Applies a flatfield correction. + - **Rotation**: Rotates the returned images by 90 degrees clockwise. + Parameters ---------- - light_header : databorker header - This header defines the images to convert - - dark_header : databroker header , optional - The header is the dark images. - - flat : array_like - Array to use for the flatfield correction. This should be a 2D - array sized as the last two dimensions of the image stack. - - - tag : string - Data tag used to retrieve images. Used in the call to - ``databroker.get_images()``. If `None`, use the defualt from - the settings. - - roi : tuple - coordinates of the upper-left corner and width and height of - the ROI: e.g., (x, y, w, h) + light_header : databroker header + This header defines the images to convert. + dark_header : databroker header, optional + The header containing dark images for background subtraction. + flat : array_like, optional, default=None + 2D array to use for the flatfield correction, sized as the last two + dimensions of the image stack. If None, no flatfield correction is applied. + tag : str + Data tag used to retrieve images (e.g., obtained by ``header.start["detectors"]``). + roi : tuple, optional, default=None + Coordinates of the upper-left corner and width and height of the ROI: + e.g., ``(x, y, w, h)``. If None, the entire image is used. Returns ------- - dask.array : corrected images - + dask.array + The corrected image stack. """ + flipped_image = _get_axis1_images(light_header, dark_header, flat, tag, roi) return flipped_image[..., ::-1] @@ -171,9 +164,10 @@ def get_axis_images(light_header, dark_header=None, flat=None, tag=None, roi=Non def _get_axis1_images(light_header, dark_header=None, flat=None, tag=None, roi=None): if tag is None: - logger.error("Must pass 'tag' argument to get_axis_images()") - raise ValueError("Must pass 'tag' argument") + raise ValueError("Must pass a detector tag (e.g., 'axis1', 'axis_standard', etc.)") + tag_key = f"{tag}_image" + # Now lets sort out the ROI if roi is not None: roi = list(roi) @@ -191,7 +185,7 @@ def _get_axis1_images(light_header, dark_header=None, flat=None, tag=None, roi=N t = ttime.time() d = dark_header - bgnd_events = _get_images(d, tag, roi) + bgnd_events = _get_images(d, tag_key, roi) tt = ttime.time() b = bgnd_events.astype(dtype=np.uint16) @@ -204,7 +198,7 @@ def _get_axis1_images(light_header, dark_header=None, flat=None, tag=None, roi=N logger.info("Computed dark images in %.3f seconds", ttime.time() - t) - events = _get_images(light_header, tag, roi) + events = _get_images(light_header, tag_key, roi) # Ok, so lets return a pims pipeline which does the image conversion @@ -317,8 +311,7 @@ def get_fastccd_timestamps(header, tag="fccd_image"): return timestamps - -def get_axis_timestamps(header, tag="axis1_hdf5_time_stamp"): +def get_axis_timestamps(header, tag= None): """Return the AXIS timestamps from the Areadetector Data File Return a list of numpy arrays of the timestamps for the images as @@ -326,21 +319,24 @@ def get_axis_timestamps(header, tag="axis1_hdf5_time_stamp"): Parameters ---------- - header : databorker header + header : databroker header This header defines the run tag : string - This is the tag or name of the fastccd. + User-level tag (e.g., 'axis1', 'axis_standard', etc.). + Internally converted to the correct timestamp key. Returns ------- - list of arrays of the timestamps - + list of arrays of the timestamps """ - timestamps = list(header.data(tag)) + if tag is None: + raise ValueError("Must pass a detector tag (e.g., 'axis1', 'axis_standard', etc.)") - return timestamps + tag_key = f"{tag}_hdf5_time_stamp" + timestamps = list(header.data(tag_key)) + return timestamps def calculate_flatfield(image, limits=(0.6, 1.4)): """Calculate a flatfield from fluo data @@ -423,35 +419,48 @@ def get_fastccd_flatfield( ) return flat +def get_axis_flatfield(light, dark, flat=None, tag=None, limits=(0.6, 1.4), half_interval=False): + """Calculate a flatfield from two databroker headers. -def get_axis_flatfield(light, dark, flat=None, limits=(0.6, 1.4), half_interval=False): - """Calculate a flatfield from two headers - - This routine calculates the flatfield using the - :func:calculate_flatfield() function after obtaining the images from - the headers. + This routine calculates the flatfield using the :func:`calculate_flatfield()` + function after obtaining the images from the provided light and dark headers. Parameters ---------- light : databroker header - The header containing the light images - dark : databroker header(s) - The header(s) from the run containin the dark images. - flat : flatfield image (optional) - The array to be used for the initial flatfield - limits : tuple limits used for returning corrected pixel flatfield - The tuple setting lower and upper bound. np.nan returned value is outside bounds - half_interval : boolean or tuple to perform calculation for only half of the FastCCD - Default is False. If True, then the hard-code portion is retained. Customize image - manipulation using a tuple of length 2 for (row_start, row_stop). - + The header containing the light images. + dark : databroker header + The header from the run containing the dark images. This can be a + single header or a list/tuple of headers if dark images span multiple + runs. + flat : array_like, optional, default=None + An array to be used as the initial flatfield. If provided, the calculation + will refine this initial flatfield. If None, a flatfield is calculated + from scratch. + tag : str + Data tag used to retrieve images (e.g., obtained by ``header.start["detectors"]``). + limits : tuple, optional, default=None + A tuple ``(lower_bound, upper_bound)`` defining the limits for the + corrected pixel flatfield values. Any calculated flatfield values + outside these bounds will be converted to ``np.nan``. If None, no + clipping is performed. + half_interval : bool or tuple, optional, default=False + Controls the image manipulation for FastCCD. + - If ``False`` (default), calculations are performed on the full image. + - If ``True``, a hard-coded portion (e.g., half) of the FastCCD image + is retained for the calculation. + - If a ``tuple`` of length 2, ``(row_start, row_stop)``, it specifies + a custom row interval for image manipulation. Returns ------- array_like - Flatfield correction. The correction is orientated as "raw data" not final data generated by get_fastccd_images(). + The calculated flatfield correction array. The orientation of this + correction array corresponds to the "raw data" orientation, not the + final data generated by ``get_axis_images()``. """ - images = get_images_to_3D(_get_axis1_images(light, dark, flat)) + + images = get_images_to_3D(_get_axis1_images(light, dark, flat, tag)) images = stackmean(images) if half_interval: if isinstance(half_interval, bool): @@ -469,7 +478,6 @@ def get_axis_flatfield(light, dark, flat=None, limits=(0.6, 1.4), half_interval= ) return flat - def fccd_mask(): """Return the initial flatfield mask for the FastCCD diff --git a/pyproject.toml b/pyproject.toml index 145d9bf..c058f1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,3 +6,12 @@ requires = [ "versioneer[toml]>=0.28" ] build-backend = "setuptools.build_meta" +[tool.ruff] +exclude = [ + "examples", + "doc", + "build", + "dist", + "csxtools.egg-info", + "__pycache__" +] diff --git a/requirements.txt b/requirements.txt index 24ce15a..a6b1da2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1 +1,2 @@ numpy +ruff>=0.4.0