Commit 1c3370dd authored by payno's avatar payno Committed by payno
Browse files

add flat field normalization

parent 39052a0f
......@@ -29,11 +29,15 @@ __date__ = "09/10/2019"
import os
import typing
import logging
import numpy
from typing import Union
from collections import OrderedDict
from .unitsystem.metricsystem import MetricSystem
from silx.utils.enum import Enum as _Enum
from silx.io.url import DataUrl
from silx.io.utils import get_data
logger = logging.getLogger(__name__)
......@@ -65,11 +69,34 @@ class TomoScanBase:
def __init__(self, scan: Union[None, str], type_: str):
self.path = scan
self._type = type_
self._normed_flats = None
"""darks normed. When set a dict is expected with index as the key
and median or median of darks serie as value"""
self._normed_darks = None
"""flats normed. When set a dict is expected with index as the key
and median or median of darks serie as value"""
self._notify_ffc_rsc_missing = True
"""Should we notify the user if ffc fails because cannot find dark or
flat. Used to avoid several warnings. Only display one"""
def clear_caches(self):
"""clear caches. Might be call if some data changed after
first read of data or metadata"""
raise NotImplementedError("Base class")
self._notify_ffc_rsc_missing = True
@property
def normed_darks(self):
return self._normed_darks
def set_normed_darks(self, darks):
self._normed_darks = darks
@property
def normed_flats(self):
return self._normed_flats
def set_normed_flats(self, flats):
self._normed_flats = flats
@property
def path(self) -> Union[None, str]:
......@@ -362,3 +389,124 @@ class TomoScanBase:
"incoherent data information to retrieve scan" "extra images angle"
)
return res
def _flat_field_correction(
self,
data: typing.Union[numpy.ndarray, DataUrl],
index_proj: typing.Union[int, None],
dark,
flat1,
flat2,
index_flat1: int,
index_flat2: int,
):
"""
compute flat field correction for a provided data from is index
one dark and two flats (require also indexes)
"""
assert isinstance(data, (numpy.ndarray, DataUrl))
if isinstance(data, DataUrl):
data = get_data(data)
can_process = True
if dark is None:
if self._notify_ffc_rsc_missing:
logger.error("cannot make flat field correction, dark not found")
can_process = False
if dark is not None and dark.ndim != 2:
logger.error(
"cannot make flat field correction, dark should be of " "dimension 2"
)
can_process = False
if flat1 is None:
if self._notify_ffc_rsc_missing:
logger.error("cannot make flat field correction, flat not found")
can_process = False
else:
if flat1.ndim != 2:
logger.error(
"cannot make flat field correction, flat should be of "
"dimension 2"
)
can_process = False
if flat2 is not None and flat1.shape != flat2.shape:
logger.error("the tow flats provided have different shapes.")
can_process = False
if dark is not None and flat1 is not None and dark.shape != flat1.shape:
logger.error("Given dark and flat have incoherent dimension")
can_process = False
if dark is not None and data.shape != dark.shape:
logger.error(
"Image has invalid shape. Cannot apply flat field" "correction it"
)
can_process = False
if can_process is False:
self._notify_ffc_rsc_missing = False
return data
if flat2 is None:
flat_value = flat1
else:
# compute weight and clip it if necessary
if index_proj is None:
w = 0.5
else:
w = (index_proj - index_flat1) / (index_flat2 - index_flat1)
w = min(1, w)
w = max(0, w)
flat_value = flat1 * w + flat2 * (1 - w)
div = flat_value - dark
div[div == 0] = 1
return (data - dark) / div
def flat_field_correction(
self, projs: typing.Iterable, proj_indexes: typing.Iterable
):
"""Apply flat field correction on the given data
:param Iterable projs: list of projection (numpy array) to apply correction
on
:param Iterable data proj_indexes: list of indexes of the projection in
the acquisition sequence. Values can
be int or None. If None then the
index take will be the one in the
middle of the flats taken.
:return: corrected data: list of numpy array
:rtype: list
"""
assert isinstance(projs, typing.Iterable)
assert isinstance(proj_indexes, typing.Iterable)
flats = self.normed_flats
flat1 = flat2 = None
index_flat1 = index_flat2 = None
if flats is not None:
flat_indexes = sorted(list(flats.keys()))
if len(flats) > 0:
index_flat1 = flat_indexes[0]
flat1 = flats[index_flat1]
if len(flats) > 1:
index_flat2 = flat_indexes[-1]
flat2 = flats[index_flat2]
darks = self.normed_darks
dark = None
if darks is not None and len(darks) > 0:
# take only one dark into account for now
dark = list(darks.values())[0]
return [
self._flat_field_correction(
data=frame,
dark=dark,
flat1=flat1,
flat2=flat2,
index_flat1=index_flat1,
index_flat2=index_flat2,
index_proj=proj_i,
)
for frame, proj_i in zip(projs, proj_indexes)
]
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment