|
@@ -20,6 +20,8 @@ import shapely.ops
|
|
from shapely.geometry import Polygon, MultiPolygon, GeometryCollection
|
|
from shapely.geometry import Polygon, MultiPolygon, GeometryCollection
|
|
from functools import reduce
|
|
from functools import reduce
|
|
from sklearn.decomposition import PCA
|
|
from sklearn.decomposition import PCA
|
|
|
|
+from sklearn.linear_model import LinearRegression
|
|
|
|
+from skimage import exposure
|
|
|
|
|
|
|
|
|
|
def normalize(im, mean, std, min_value=[0, 0, 0], max_value=[255, 255, 255]):
|
|
def normalize(im, mean, std, min_value=[0, 0, 0], max_value=[255, 255, 255]):
|
|
@@ -378,4 +380,65 @@ def pca(im, dim=3, whiten=True):
|
|
im_pca = pca.fit_transform(n_im)
|
|
im_pca = pca.fit_transform(n_im)
|
|
result = np.reshape(im_pca, (H, W, dim))
|
|
result = np.reshape(im_pca, (H, W, dim))
|
|
result = np.clip(result, 0, 1)
|
|
result = np.clip(result, 0, 1)
|
|
- return (result * 255).astype("uint8")
|
|
|
|
|
|
+ return (result * 255).astype("uint8")
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def match_histograms(im, ref):
|
|
|
|
+ """
|
|
|
|
+ Match the cumulative histogram of one image to another.
|
|
|
|
+
|
|
|
|
+ Args:
|
|
|
|
+ im (np.ndarray): The input image.
|
|
|
|
+ ref (np.ndarray): The reference image to match histogram of. `ref` must have the same number of channels as `im`.
|
|
|
|
+
|
|
|
|
+ Returns:
|
|
|
|
+ np.ndarray: The transformed input image.
|
|
|
|
+
|
|
|
|
+ Raises:
|
|
|
|
+ ValueError: When the number of channels of `ref` differs from that of im`.
|
|
|
|
+ """
|
|
|
|
+ # TODO: Check the data types of the inputs to see if they are supported by skimage
|
|
|
|
+ return exposure.match_histograms(im, ref, channel_axis=-1 if im.ndim>2 else None)
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def match_by_regression(im, ref, pif_loc=None):
|
|
|
|
+ """
|
|
|
|
+ Match the brightness values of two images using a linear regression method.
|
|
|
|
+
|
|
|
|
+ Args:
|
|
|
|
+ im (np.ndarray): The input image.
|
|
|
|
+ ref (np.ndarray): The reference image to match. `ref` must have the same shape as `im`.
|
|
|
|
+ pif_loc (tuple|None, optional): The spatial locations where pseudo-invariant features (PIFs) are obtained. If
|
|
|
|
+ `pif_loc` is set to None, all pixels in the image will be used as training samples for the regression model.
|
|
|
|
+ In other cases, `pif_loc` should be a tuple of np.ndarrays. Default: None.
|
|
|
|
+
|
|
|
|
+ Returns:
|
|
|
|
+ np.ndarray: The transformed input image.
|
|
|
|
+
|
|
|
|
+ Raises:
|
|
|
|
+ ValueError: When the shape of `ref` differs from that of `im`.
|
|
|
|
+ """
|
|
|
|
+ def _linear_regress(im, ref, loc):
|
|
|
|
+ regressor = LinearRegression()
|
|
|
|
+ if loc is not None:
|
|
|
|
+ x, y = im[loc], ref[loc]
|
|
|
|
+ else:
|
|
|
|
+ x, y = im, ref
|
|
|
|
+ x, y = x.reshape(-1,1), y.ravel()
|
|
|
|
+ regressor.fit(x, y)
|
|
|
|
+ matched = regressor.predict(im.reshape(-1,1))
|
|
|
|
+ return matched.reshape(im.shape)
|
|
|
|
+
|
|
|
|
+ if im.shape != ref.shape:
|
|
|
|
+ raise ValueError("Image and Reference must have the same shape!")
|
|
|
|
+
|
|
|
|
+ if im.ndim > 2:
|
|
|
|
+ # Multiple channels
|
|
|
|
+ matched = np.empty(im.shape, dtype=im.dtype)
|
|
|
|
+ for ch in range(im.shape[-1]):
|
|
|
|
+ matched[..., ch] = _linear_regress(im[..., ch], ref[..., ch], pif_loc)
|
|
|
|
+ else:
|
|
|
|
+ # Single channel
|
|
|
|
+ matched = _linear_regress(im, ref, pif_loc).astype(im.dtype)
|
|
|
|
+
|
|
|
|
+ return matched
|