Source code for pylablib.core.dataproc.image

import numpy as np
from ..utils import funcargparse


_default_indexing={"rc":"rcb","xy":"xyt"}
[docs] def convert_shape_indexing(shape, src, dst, axes=(0,1)): """ Convert image indexing style. `shape` is the source image shape (2-tuple), `src` and `dst` are current format and desired format. Formats can be ``"rcb"`` (first index is row, second is column, rows count from the bottom), ``"rct"`` (same, but rows count from the top). ``"xyb"`` (first index is column, second is row, rows count from the bottom), or ``"xyt"`` (same but rows count form the top). ``"rc"`` is interpreted as ``"rct"``, ``"xy"`` as ``"xyt"`` """ src=_default_indexing.get(src,src) dst=_default_indexing.get(dst,dst) funcargparse.check_parameter_range(src,"src",["rcb","rct","xyb","xyt"]) funcargparse.check_parameter_range(dst,"dst",["rcb","rct","xyb","xyt"]) if src[:2]==dst[:2]: return shape else: shape=list(shape) shape[axes[0]],shape[axes[1]]=shape[axes[1]],shape[axes[0]] return tuple(shape)
def _flip(img, axis): """Flip an image along the given axis""" try: return np.flip(img,axis) except AttributeError: idx=range(img.shape[axis]-1,-1,-1) return img.take(idx,axis=axis)
[docs] def convert_image_indexing(img, src, dst, axes=(0,1)): """ Convert image indexing style. `img` is the source image (ND numpy array with N>=2), `src` and `dst` are current format and desired format, `axes` specify correspondingly the row and the column axes (by default, the first two array axes). Formats can be ``"rcb"`` (first index is row, second is column, rows count from the bottom), ``"rct"`` (same, but rows count from the top). ``"xyb"`` (first index is column, second is row, rows count from the bottom), or ``"xyt"`` (same but rows count form the top). ``"rc"`` is interpreted as ``"rct"``, ``"xy"`` as ``"xyt"`` """ src=_default_indexing.get(src,src) dst=_default_indexing.get(dst,dst) funcargparse.check_parameter_range(src,"src",["rcb","rct","xyb","xyt"]) funcargparse.check_parameter_range(dst,"dst",["rcb","rct","xyb","xyt"]) if src==dst: return img if src[:2]==dst[:2]: # same order, different row direction return _flip(img,axes[0]) if src[:2]=="rc" else _flip(img,axes[1]) if src[2]==dst[2]: # same row direction, different order if src[2]=="t": return img.swapaxes(*axes) if src=="rcb": return _flip(_flip(img,axes[0]).swapaxes(*axes),axes[1]) else: return _flip(_flip(img,axes[1]).swapaxes(*axes),axes[0]) # different row direction, different order if src=="rcb": # dst=="xyt" return _flip(img,axes[0]).swapaxes(*axes) if src=="rct": # dst=="xyb" return _flip(img.swapaxes(*axes),axes[1]) if src=="xyb": # dst=="rct" return _flip(img,axes[1]).swapaxes(*axes) if src=="xyt": # dst=="rcb" return _flip(img.swapaxes(*axes),axes[0])
[docs] class ROI: def __init__(self, imin=0, imax=None, jmin=0, jmax=None): self.imin=imin self.imax=imax self.jmin=jmin self.jmax=jmax self._order() def _order(self): if self.imax is not None: self.imin,self.imax=sorted((self.imin,self.imax)) if self.jmax is not None: self.jmin,self.jmax=sorted((self.jmin,self.jmax)) def _get_limited(self, shape=None): if shape is None: if self.imax is None or self.jmax is None: raise ValueError("one of the ROI dimensions is unconstrained") return self.imin,self.imax,self.jmin,self.jmax imin=max(self.imin,0) imax=shape[0] if self.imax is None else min(self.imax,shape[0]) jmin=max(self.jmin,0) jmax=shape[1] if self.jmax is None else min(self.jmax,shape[1]) return sorted((imin,imax))+sorted((jmin,jmax))
[docs] def copy(self): return ROI(self.imin,self.imax,self.jmin,self.jmax)
def __repr__(self): return "{}{}".format(self.__class__.__name__,self.tup())
[docs] def center(self, shape=None): imin,imax,jmin,jmax=self._get_limited(shape) return (imin+imax)/2, (jmin+jmax)/2
[docs] def size(self, shape=None): imin,imax,jmin,jmax=self._get_limited(shape) return (imax-imin), (jmax-jmin)
[docs] def area(self, shape=None): size=self.size(shape) return size[0]*size[1]
[docs] def tup(self, shape=None): return self._get_limited(shape)
[docs] def ispan(self, shape=None): return self.tup(shape)[0:2]
[docs] def jspan(self, shape=None): return self.tup(shape)[2:4]
[docs] @classmethod def from_centersize(cls, center, size, shape=None): size=funcargparse.as_sequence(size,2) imin,imax=int(round(center[0]-abs(size[0])/2)),int(round(center[0]+abs(size[0])/2)) jmin,jmax=int(round(center[1]-abs(size[1])/2)),int(round(center[1]+abs(size[1])/2)) res=cls(imin,imax,jmin,jmax) if shape is not None: res.limit(shape) return res
[docs] @classmethod def intersect(cls, *args): imin=max(r.imin for r in args) imax=min(r.imax for r in args) jmin=max(r.jmin for r in args) jmax=min(r.jmax for r in args) if imin>=imax or jmin>=jmax: return None return cls(imin,imax,jmin,jmax)
[docs] def limit(self, shape): self.imin,self.imax,self.jmin,self.jmax=self._get_limited(shape) return self
[docs] def get_region(image, center, size, axis=(-2,-1)): """ Get part of the image with the given center and size (both are tuples ``(i, j)``). The region is automatically reduced if a part of it is outside of the image. """ roi=ROI.from_centersize(center,size,shape=(image.shape[axis[0]],image.shape[axis[1]])) ispan,jspan=roi.ispan(),roi.jspan() index=[slice(None)]*image.ndim index[axis[0]]=slice(ispan[0],ispan[1]) index[axis[1]]=slice(jspan[0],jspan[1]) return image[tuple(index)]
[docs] def get_region_sum(image, center, size, axis=(-2,-1)): """ Sum part of the image with the given center and size (both are tuples ``(i, j)``). The region is automatically reduced if a part of it is outside of the image. Return tuple ``(sum, area)``, where area is the actual summer region are (in pixels). """ roi=ROI.from_centersize(center,size,shape=(image.shape[axis[0]],image.shape[axis[1]])) ispan,jspan=roi.ispan(),roi.jspan() index=[slice(None)]*image.ndim index[axis[0]]=slice(ispan[0],ispan[1]) index[axis[1]]=slice(jspan[0],jspan[1]) return np.sum(image[tuple(index)],axis=axis), roi.area()