helpers.py 4.41 KB
Newer Older
Josef Brandt's avatar
Josef Brandt committed
1 2
from PyQt5 import QtCore, QtGui
import numpy as np
3 4 5
import sys
sys.path.append("C://Users//xbrjos//Desktop//Python")
from gepard import dataset
Josef Brandt's avatar
Josef Brandt committed
6

7 8 9 10 11 12 13 14 15 16 17 18 19 20 21

class ParticleBinSorter(object):
    def __init__(self):
        super(ParticleBinSorter, self).__init__()
        self.bins = [5, 10, 20, 50, 100, 200, 500]
        
    def sort_particles_into_bins(self, particleList):
        particlesInBins = self._get_empty_bins()
        
        for particle in particleList:
            binIndex = self._get_binIndex_of_particle(particle)
            particlesInBins[binIndex].append(particle)
        return particlesInBins
    
    def _get_empty_bins(self):
Josef Brandt's avatar
Josef Brandt committed
22
        return [[] for _ in range(len(self.bins)+1)]
23 24 25 26 27 28 29 30 31
    
    def _get_binIndex_of_particle(self, particle):
        size = particle.getParticleSize()
        binIndex = 0
        for upperLimit in self.bins:
            if size <= upperLimit:
                break
            else:
                binIndex += 1
Josef Brandt's avatar
Josef Brandt committed
32 33 34
        return binIndex


35
def box_overlaps_contour(boxTopLeftXY: tuple, boxWidthHeight: tuple, contourData: np.ndarray) -> bool:
36 37 38 39
    """
    Calculates, if a contour is overlapping a box.
    :param boxTopLeftXY: topLeft of Box
    :param boxWidthHeight: Width and height of box
40
    :param contourData: np.ndarrayof contour data
41 42
    :return:
    """
43
    isOverlapping: bool = False
Josef Brandt's avatar
Josef Brandt committed
44

45 46 47
    xmin, xmax = np.min(contourData[:, 0, 0]), np.max(contourData[:, 0, 0])
    width: float = xmax - xmin
    boxXmin, boxXmax = boxTopLeftXY[0], boxTopLeftXY[0] + boxWidthHeight[0]
48

49 50 51 52 53 54 55 56 57
    if xmin > (boxXmin-width/2):
        if xmax < (boxXmax+width/2):
            ymin, ymax = np.min(contourData[:, 0, 1]), np.max(contourData[:, 0, 1])
            height = ymax - ymin
            boxYmin, boxYmax = boxTopLeftXY[1], boxTopLeftXY[1] + boxWidthHeight[1]

            if ymin > (boxYmin-height/2):
                if ymax < (boxYmax+width/2):
                    isOverlapping = True
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118

    return isOverlapping


def get_overlapping_fraction(polygon1: QtGui.QPolygonF, polygon2: QtGui.QPolygonF) -> float:
    """
    Takes two polygons and returns the overlapping fraction (in terms of area)
    :param polygon1: The polygon that the fraction shall be calculated of.
    :param polygon2: The overlapping polygon, which's size is not of interest
    :return:
    """
    overlap: float = 0
    overlapPoly: QtGui.QPolygonF = polygon1.intersected(polygon2)
    if overlapPoly.size() > 0:
        origSize: float = get_polygon_area(polygon1)
        overlapSize: float = get_polygon_area(overlapPoly)
        overlap = overlapSize/origSize

    return overlap


def get_polygon_area(polygon: QtGui.QPolygonF) -> float:
    """
    Calculates the area of a polygon, adapted from:
    https://stackoverflow.com/questions/24467972/calculate-area-of-polygon-given-x-y-coordinates
    :param polygon:
    :return: area
    """
    x: list = []
    y: list = []
    for index in range(polygon.size()):
        point: QtCore.QPointF = polygon.at(index)
        x.append(point.x())
        y.append(point.y())

    x: np.ndarray = np.array(x)
    y: np.ndarray = np.array(y)
    area = 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
    return area


def get_filterDimensions_from_dataset(dataset) -> tuple:
    """
    Processes the datasets boundary items to calculate diameter and offset (coord system offset of circular filter
    with respect to actual dataset). This is used to set circular filter dimensions to use in the geometric
    subsampling approaches.
    The return values are in micrometer dimensions.
    :param dataset: The dataset to read.
    :return: (radius, offset, widthHeight) in µm
    """
    maxDim = dataset.maxdim
    imgDim = dataset.imagedim_df if dataset.imagescanMode == 'df' else dataset.imagedim_bf
    minX, maxY, = maxDim[0] - imgDim[0] / 2, maxDim[1] + imgDim[1] / 2
    maxX, minY = maxDim[2] + imgDim[0] / 2, maxDim[3] - imgDim[1] / 2
    width = maxX - minX
    height = maxY - minY

    diameter: float = min([width, height])
    offset: tuple = (width - diameter)/2, (height-diameter)/2
    return offset, diameter, [width, height]

Josef Brandt's avatar
Josef Brandt committed
119

120 121 122 123 124 125 126 127 128 129
def convert_length_to_pixels(dataset: dataset.DataSet, length: float) -> float:
    """
    :param dataset: dataset to use for conversion
    :param length: length in µm
    :return: length in px
    """
    imgMode: str = dataset.imagescanMode
    pixelScale: float = (dataset.pixelscale_df if imgMode == 'df' else dataset.pixelscale_bf)
    length /= pixelScale
    return length