def comp_pixel_ternary(image): # 3x3 and 2x2 pixel cross-correlation within image # orthogonal comp orthdy__ = image[1:] - image[:-1] # vertical orthdx__ = image[:, 1:] - image[:, :-1] # horizontal # compute gdert__ p__ = (image[:-2, :-2] + image[:-2, 1:-1] + image[1:-1, :-2] + image[1:-1, 1:-1]) * 0.25 dy__ = (orthdy__[:-1, 1:-1] + orthdy__[:-1, :-2]) * 0.5 dx__ = (orthdx__[1:-1, :-1] + orthdx__[:-2, :-1]) * 0.5 g__ = ma.hypot(dy__, dx__) gdert__ = ma.stack((p__, g__, dy__, dx__)) # diagonal comp diag1__ = image[2:, 2:] - image[:-2, :-2] diag2__ = image[2:, :-2] - image[:-2, 2:] # compute rdert__ p3__ = image[1:-1, 1:-1] dy3__ = (orthdy__[1:, 1:-1] + orthdy__[:-1, 1:-1]) * 0.25 +\ (diag1__ + diag2__) * 0.125 dx3__ = (orthdx__[1:-1, 1:] + orthdx__[1:-1, :-1]) * 0.25 + \ (diag1__ - diag2__) * 0.125 g3__ = ma.hypot(dy3__, dx3__) rdert__ = ma.stack((p3__, g3__, dy3__, dx3__)) # gdert__ = comp_2x2(image) # cross-compare four adjacent pixels diagonally # rdert__ = comp_3x3(image) # compare each pixel to 8 rim pixels return gdert__, rdert__
def comp_g(dert__, rng): """ Compare g over predetermined range. """ # Unpack dert__: g, m, dy, dx = dert__ # Compare gs: d = translated_operation(g, rng, op.sub) comp_field = central_slice(rng) # Decompose and add to corresponding dy and dx: dy[comp_field] += (d * Y_COEFFS[rng]).sum(axis=-1) dx[comp_field] += (d * X_COEFFS[rng]).sum(axis=-1) # Compute ms: m[comp_field] += translated_operation(g, rng, ma.minimum).sum(axis=-1) # Apply mask: msq = np.ones(g.shape, dtype=int) # Rim mask. msq[comp_field] = g.mask[comp_field] + d.mask.sum( axis=-1) # Summed d mask. imsq = msq.nonzero() m[imsq] = dy[imsq] = dx[imsq] = ma.masked # Apply mask. # Compute gg: gg = ma.hypot(dy, dx) * SCALER_g[rng] return ma.stack((g, gg, m, dy, dx), axis=0) # ma.stack() for extra array dimension.
def comp_a(dert__, rng): """ Compute and compare a over predetermined range. """ # Unpack dert__: if len(dert__) in (5, 12): # idert or full dert with m. i__, g__, m__, dy__, dx__ = dert__[:5] else: # idert or full dert without m. i__, g__, dy__, dx__ = dert__[:4] if len(dert__) > 10: # if ra+: a__ = dert__[-7:-5] # Computed angle (use reverse indexing to avoid m check). day__ = dert__[-4:-2] # Accumulated day__. dax__ = dert__[-2:] # Accumulated day__. else: # if fa: # Compute angles: a__ = ma.stack((dy__, dx__), axis=0) / g__ a__.mask = g__.mask # Initialize dax, day: day__, dax__ = [ma.zeros((2,) + i__.shape) for _ in range(2)] # Compute angle differences: da__ = translated_operation(a__, rng, angle_diff) comp_field = central_slice(rng) # Decompose and add to corresponding day and dax: day__[comp_field] = (da__ * Y_COEFFS[rng]).mean(axis=-1) dax__[comp_field] = (da__ * X_COEFFS[rng]).mean(axis=-1) # Apply mask: msq = np.ones(a__.shape, dtype=int) # Rim mask. msq[comp_field] = a__.mask[comp_field] + da__.mask.sum(axis=-1) # Summed d mask. imsq = msq.nonzero() day__[imsq] = dax__[imsq] = ma.masked # Apply mask. # Compute ga: ga__ = ma.hypot( ma.arctan2(*day__), ma.arctan2(*dax__) )[np.newaxis, ...] * SCALER_ga try: # dert with m is more common: return ma.concatenate( # Concatenate on the first dimension. ( ma.stack((i__, g__, m__, dy__, dx__), axis=0), a__, ga__, day__, dax__, ), axis=0, ) except NameError: # m doesn't exist: return ma.concatenate( # Concatenate on the first dimension. ( ma.stack((i__, g__, dy__, dx__), axis=0), a__, ga__, day__, dax__, ), axis=0, )
def comp_a(gdert__, rng): """ Compute and compare a over predetermined range. """ # Unpack dert__: try: g, gg, m, dy, dx = gdert__ except ValueError: # Initial dert doesn't contain m. g, gg, dy, dx = gdert__ # Initialize dax, day: day, dax = [ma.zeros((2, ) + g.shape) for _ in range(2)] # Compute angles: a = ma.stack((dy, dx), axis=0) / gg a.mask = gg.mask # Compute angle differences: da = translated_operation(a, rng, angle_diff) comp_field = central_slice(rng) # Decompose and add to corresponding day and dax: day[comp_field] = (da * Y_COEFFS[rng]).mean(axis=-1) dax[comp_field] = (da * X_COEFFS[rng]).mean(axis=-1) # Apply mask: msq = np.ones(a.shape, dtype=int) # Rim mask. msq[comp_field] = a.mask[comp_field] + da.mask.sum( axis=-1) # Summed d mask. imsq = msq.nonzero() day[imsq] = dax[imsq] = ma.masked # Apply mask. # Compute ga: ga = ma.hypot(ma.arctan2(*day), ma.arctan2(*dax))[np.newaxis, ...] * SCALER_ga try: return ma.concatenate( # Concatenate on the first dimension. ( ma.stack((g, gg, m), axis=0), ga, day, dax, ), axis=0, ) except NameError: # m doesn't exist. return ma.concatenate( # Concatenate on the first dimension. ( ma.stack((g, gg), axis=0), a, ga, day, dax, ), axis=0, )
def _compare(self): """Generate derivatives from inputs.""" dy, dx = compare_slices(self._i, self._rng) self._derts[-2:][central_slice(self._rng)] += (dy, dx) self._derts[0] = ma.hypot(*self._derts[-2:]) self._derts[rim_mask(self._i.shape, rng)] = ma.masked return self
def _compare(self): dy, dx = compare_slices(self._i, self._rng, operator=angle_diff) self._derts[1:][central_slice(self._rng)] += np.concatenate((dy, dx)) self._derts[0] = ma.hypot( ma.arctan2(*self.dy), ma.arctan2(*self.dx), ) self._derts[rim_mask(self._i.shape, rng)] = ma.masked return self
def comp_pixel_old(image): # 2x2 pixel cross-correlation within image dy__ = image[1:] - image[:-1] # orthogonal vertical com dx__ = image[:, 1:] - image[:, :-1] # orthogonal horizontal comp p__ = image[:-1, :-1] # top-left pixel mean_dy__ = (dy__[:, 1:] + dy__[:, :-1]) * 0.5 # mean dy per kernel mean_dx__ = (dx__[1:, :] + dx__[:-1, :]) * 0.5 # mean dx per kernel g__ = ma.hypot(mean_dy__, mean_dx__) # central gradient of four rim pixels dert__ = ma.stack((p__, g__, mean_dy__, mean_dx__)) return dert__
def comp_r(dert__, fig): i__, g__, dy__, dx__ = dert__[:] dy__ = dy__[:, rng:-rng, rng:-rng] dx__ = dx__[:, rng:-rng, rng:-rng] # comparison d__ = translated_operation(i__, rng=rng, operator=op.sub) # sum within kernels dy__ += (d__ * Y_COEFFS[rng]).sum(axis=-1) dx__ += (d__ * X_COEFFS[rng]).sum(axis=-1) # compute gradient magnitudes g__ = ma.hypot(dy__, dx__) return ma.stack((i__, g__, dy__, dx__))
def comp_3x3_loop(image): buff_ = deque() # buffer for derts Y, X = image.shape for p_ in image: # loop through rows for p in p_: # loop through each pixel dx = dy = 0 # uni-lateral differences # loop through buffers: for k, xcoeff, ycoeff in zip( [0, X - 1, X, X + 1], # indices of _dert [0.25, -0.125, 0, 0.125], # x axis coefficient [0, 0.125, 0.25, 0.125]): # y axis coefficient try: _p, _dy, _dx = buff_[k] # unpack buff_[k] d = p - _p # compute difference dx_buff = d * xcoeff # decompose difference dy_buff = d * ycoeff dx += dx_buff # accumulate fuzzy difference over the kernel _dx += dx_buff dy += dy_buff _dy += dy_buff buff_[k] = _p, _dy, _dx # repack buff_[k] except TypeError: # buff_[k] is None pass except IndexError: # k >= len(buff_) break buff_.appendleft( (p, dy, dx)) # initialize dert with uni-lateral differences buff_.appendleft(None) # add empty dert at the end of each row # reshape data and compute g (temporary, to perform tests) p__, dy__, dx__ = np.array([buff for buff in reversed(buff_) if buff is not None])\ .reshape(Y, X, 3).swapaxes(1, 2).swapaxes(0, 1)[:, 1:-1, 1:-1] g__ = ma.hypot(dy__, dx__) return ma.stack((p__, g__, dy__, dx__))
def comp_g(dert__, odd): g__ = dert__[0] a__ = dert__[1:] # loop through each pair of comparands in a kernel dgy__ = ma.zeros(np.subtract(g.shape, rng)) dgx__ = ma.zeros(np.subtract(g.shape, rng)) for x_coeff, y_coeff, (ts, _ts) in zip(X_COEFFS[rng], Y_COEFFS[rng], TRANSLATING_SLICES_PAIRS_[rng]): # find angle differences da__ = angle_diff(a__[ts], a__[_ts]) # compute dg: dg = g - _g * cos(da) at each position dg__ = g__[ts] - g__[_ts] * da__[1] # accumulate dgy, dgx dgx__ += dg__ * x_coeff dgy__ += dg__ * y_coeff gg__ = ma.hypot(dgy__, dgx__) return ma.stack((g__, gg__, dgy__, dgx__))
def comp_r(dert__, fig, root_fcr): ''' Cross-comparison of input param (dert[0]) over rng passed from intra_blob. This fork is selective for blobs with below-average gradient, where input intensity didn't vary much in shorter-range cross-comparison. Such input is predictable enough for selective sampling: skipping current rim derts as kernel-central derts in following comparison kernels. Skipping forms increasingly sparse output dert__ for greater-range cross-comp, hence rng (distance between centers of compared derts) increases as 2^n, starting at 0: rng = 1: 3x3 kernel, rng = 2: 5x5 kernel, rng = 4: 9x9 kernel, ... Due to skipping, configuration of input derts in next-rng kernel will always be 3x3, see: https://github.com/boris-kz/CogAlg/blob/master/frame_2D_alg/Illustrations/intra_comp_diagrams.png ''' # initialize new dert structure new_dert__ = ma.zeros((dert__.shape[0], (dert__.shape[1] - 1) // 2, (dert__.shape[2] - 1) // 2), dtype=dert__.dtype) new_dert__.mask = True # extract new_dert__ 'views', use [:] to 'update' views and new_dert__ at the same time i__center, idy__, idx__, g__, dy__, dx__, m__ = new_dert__ i__ = dert__[0] # i is ig if fig else pixel ''' sparse aligned i__center and i__rim arrays: ''' i__center[:] = i__[1:-1:2, 1:-1:2] # also assignment to new_dert__[0] i__topleft = i__[:-2:2, :-2:2] i__top = i__[:-2:2, 1:-1:2] i__topright = i__[:-2:2, 2::2] i__right = i__[1:-1:2, 2::2] i__bottomright = i__[2::2, 2::2] i__bottom = i__[2::2, 1:-1:2] i__bottomleft = i__[2::2, :-2:2] i__left = i__[1:-1:2, :-2:2] ''' unmask all derts in kernels with only one masked dert (can be set to any number of masked derts), to avoid extreme blob shrinking and loss of info in other derts of partially masked kernels unmasked derts were computed due to extend_dert() in intra_blob ''' majority_mask = (i__[:, 1:-1:2, 1:-1:2].mask.astype(int) + i__[:, :-2:2, :-2:2].mask.astype(int) + i__[:, :-2:2, 1:-1:2].mask.astype(int) + i__[:, :-2:2, 2::2].mask.astype(int) + i__[:, 1:-1:2, 2::2].mask.astype(int) + i__[:, 2::2, 2::2].mask.astype(int) + i__[:, 2::2, 1:-1:2].mask.astype(int) + i__[:, 2::2, :-2:2].mask.astype(int) + i__[:, 1:-1:2, :-2:2].mask.astype(int)) > 1 i__center.mask = i__topleft.mask = i__top.mask = i__topright.mask = i__right.mask = i__bottomright.mask = \ i__bottom.mask = i__bottomleft.mask = i__left.mask = majority_mask # not only i__center idy__[:], idx__[:] = dert__[[1, 2], 1:-1:2, 1:-1:2] if root_fcr: # root fork is comp_r, accumulate derivatives: dy__[:] = dert__[4, 1:-1:2, 1:-1:2] # sparse to align with i__center dx__[:] = dert__[5, 1:-1:2, 1:-1:2] m__[:] = dert__[6, 1:-1:2, 1:-1:2] dy__.mask = dx__.mask = m__.mask = majority_mask if not fig: # compare four diametrically opposed pairs of rim pixels: d_tl_br = i__topleft.data - i__bottomright.data d_t_b = i__top.data - i__bottom.data d_tr_bl = i__topright.data - i__bottomleft.data d_r_l = i__right.data - i__left.data dy__ += (d_tl_br * YCOEFs[0] + d_t_b * YCOEFs[1] + d_tr_bl * YCOEFs[2] + d_r_l * YCOEFs[3]) dx__ += (d_tl_br * XCOEFs[0] + d_t_b * XCOEFs[1] + d_tr_bl * XCOEFs[2] + d_r_l * XCOEFs[3]) g__[:] = ma.hypot(dy__, dx__) # gradient ''' inverse match = SAD, direction-invariant and more precise measure of variation than g (all diagonal derivatives can be imported from prior 2x2 comp) ''' m__ += (abs(i__center.data - i__topleft.data) + abs(i__center.data - i__top.data) + abs(i__center.data - i__topright.data) + abs(i__center.data - i__right.data) + abs(i__center.data - i__bottomright.data) + abs(i__center.data - i__bottom.data) + abs(i__center.data - i__bottomleft.data) + abs(i__center.data - i__left.data)) else: # fig is TRUE, compare angle and then magnitude of 8 center-rim pairs i__[ma.where(i__ == 0)] = 1 # to avoid / 0 a__ = dert__[[1, 2]] / i__ # sin = idy / i, cos = idx / i, i = ig ''' sparse aligned a__center and a__rim arrays: ''' a__center = a__[:, 1:-1:2, 1:-1:2] a__topleft = a__[:, :-2:2, :-2:2] a__top = a__[:, :-2:2, 1:-1:2] a__topright = a__[:, :-2:2, 2::2] a__right = a__[:, 1:-1:2, 2::2] a__bottomright = a__[:, 2::2, 2::2] a__bottom = a__[:, 2::2, 1:-1:2] a__bottomleft = a__[:, 2::2, :-2:2] a__left = a__[:, 1:-1:2, :-2:2] ''' only mask kernels with more than one masked dert, for all operations below: ''' majority_mask_a = (a__[:, 1:-1:2, 1:-1:2].mask.astype(int) + a__[:, :-2:2, :-2:2].mask.astype(int) + a__[:, :-2:2, 1:-1:2].mask.astype(int) + a__[:, :-2:2, 2::2].mask.astype(int) + a__[:, 1:-1:2, 2::2].mask.astype(int) + a__[:, 2::2, 2::2].mask.astype(int) + a__[:, 2::2, 1:-1:2].mask.astype(int) + a__[:, 2::2, :-2:2].mask.astype(int) + a__[:, 1:-1:2, :-2:2].mask.astype(int)) > 1 a__center.mask = a__topleft.mask = a__top.mask = a__topright.mask = a__right.mask = a__bottomright.mask = \ a__bottom.mask = a__bottomleft.mask = a__left.mask = majority_mask_a assert (majority_mask_a[0] == majority_mask_a[1]).all() # what does that do? dy__.mask = dx__.mask = m__.mask = majority_mask_a[0] ''' 8-tuple of differences between central dert angle and rim dert angle: ''' cos_da = [((a__topleft[1].data * a__center[1].data) + (a__center[0].data * a__topleft[0].data)), ((a__top[1].data * a__center[1].data) + (a__center[0].data * a__top[0].data)), ((a__topright[1].data * a__center[1].data) + (a__center[0].data * a__topright[0].data)), ((a__right[1].data * a__center[1].data) + (a__center[0].data * a__right[0].data)), ((a__bottomright[1].data * a__center[1].data) + (a__center[0].data * a__bottomright[0].data)), ((a__bottom[1].data * a__center[1].data) + (a__center[0].data * a__bottom[0].data)), ((a__bottomleft[1].data * a__center[1].data) + (a__center[0].data * a__bottomleft[0].data)), ((a__left[1].data * a__center[1].data) + (a__center[0].data * a__left[0].data))] ''' 8-tuple of cosine matches per direction: ''' m__ += (ma.minimum(i__center.data, i__topleft.data) * cos_da[0] + ma.minimum(i__center.data, i__top.data) * cos_da[1] + ma.minimum(i__center.data, i__topright.data) * cos_da[2] + ma.minimum(i__center.data, i__right.data) * cos_da[3] + ma.minimum(i__center.data, i__bottomright.data) * cos_da[4] + ma.minimum(i__center.data, i__bottom.data) * cos_da[5] + ma.minimum(i__center.data, i__bottomleft.data) * cos_da[6] + ma.minimum(i__center.data, i__left.data) * cos_da[7]) ''' 8-tuple of cosine differences per direction: ''' dt__ = [(i__center.data - i__topleft.data * cos_da[0]), (i__center.data - i__top.data * cos_da[1]), (i__center.data - i__topright.data * cos_da[2]), (i__center.data - i__right.data * cos_da[3]), (i__center.data - i__bottomright.data * cos_da[4]), (i__center.data - i__bottom.data * cos_da[5]), (i__center.data - i__bottomleft.data * cos_da[6]), (i__center.data - i__left.data * cos_da[7])] for d__, YCOEF, XCOEF in zip(dt__, YCOEFs, XCOEFs): dy__ += d__ * YCOEF # decompose differences into dy and dx, dx__ += d__ * XCOEF # accumulate with prior-rng dy, dx ''' accumulate in prior-range dy, dx: 3x3 -> 5x5 -> 9x9 ''' g__[:] = ma.hypot(dy__, dx__) ''' next comp_r will use full dert next comp_g will use g__, dy__, dx__ ''' return new_dert__ # new_dert__ has been updated along with 'view' arrays: i__center, idy__, idx__, g__, dy__, dx__, m__
def comp_g( dert__ ): # cross-comp of g in 2x2 kernels, between derts in ma.stack dert__ # initialize return variable new_dert__ = ma.zeros( (dert__.shape[0], dert__.shape[1] - 1, dert__.shape[2] - 1)) new_dert__.mask = True # initialize mask ig__, idy__, idx__, gg__, dgy__, dgx__, mg__ = new_dert__ # assign 'views'. Use [:] to update views # Unpack relevant params g__, dy__, dx__ = dert__[[3, 4, 5]] # g, dy, dx -> local i, idy, idx g__.data[np.where( g__.data == 0 )] = 1 # replace 0 values with 1 to avoid error, not needed in high-g blobs? ''' for all operations below: only mask kernels with more than one masked dert ''' majority_mask = ( g__[:-1, :-1].mask.astype(int) + g__[:-1, 1:].mask.astype(int) + g__[1:, 1:].mask.astype(int) + g__[1:, :-1].mask.astype(int)) > 1 g__.mask = dy__.mask = dx__.mask = majority_mask g0__, dy0__, dx0__ = g__[:-1, : -1].data, dy__[:-1, : -1].data, dx__[:-1, : -1].data # top left g1__, dy1__, dx1__ = g__[:-1, 1:].data, dy__[:-1, 1:].data, dx__[:-1, 1:].data # top right g2__, dy2__, dx2__ = g__[1:, 1:].data, dy__[1:, 1:].data, dx__[ 1:, 1:].data # bottom right g3__, dy3__, dx3__ = g__[1:, :-1].data, dy__[1:, :-1].data, dx__[ 1:, :-1].data # bottom left sin0__ = dy0__ / g0__ cos0__ = dx0__ / g0__ sin1__ = dy1__ / g1__ cos1__ = dx1__ / g1__ sin2__ = dy2__ / g2__ cos2__ = dx2__ / g2__ sin3__ = dy3__ / g3__ cos3__ = dx3__ / g3__ ''' cosine of difference between diagonally opposite angles, in vector representation print(cos_da1__.shape, type(cos_da1__)) ''' cos_da0__ = (cos2__ * cos0__) + (sin2__ * sin0__ ) # top left to bottom right cos_da1__ = (cos3__ * cos1__) + (sin3__ * sin1__ ) # top right to bottom left dgy__[:] = ((g3__ + g2__) - (g0__ * cos_da0__ + g1__ * cos_da0__)) # y-decomposed cosine difference between gs dgx__[:] = ((g1__ + g2__) - (g0__ * cos_da0__ + g3__ * cos_da1__)) # x-decomposed cosine difference between gs gg__[:] = ma.hypot(dgy__, dgx__) # gradient of gradient mg0__ = ma.minimum(g0__, g2__) * (cos_da1__ + 1) # +1 to make all positive mg1__ = ma.minimum(g1__, g3__) * (cos_da1__ + 1) mg__[:] = mg0__ + mg1__ # match of gradient ig__[:] = g__[:-1, : -1] # remove last row and column to align with derived params idy__[:] = dy__[:-1, :-1] idx__[:] = dx__[:-1, :-1] # -> idy, idx to compute cos for comp rg # unnecessary?: gg__.mask = mg__.mask = dgy__.mask = dgx__.mask = majority_mask ''' next comp_rg will use g, dy, dx next comp_gg will use gg, dgy, dgx ''' return new_dert__ # new_dert__ has been updated along with 'view' arrays: ig__, idy__, idx__, gg__, dgy__, dgx__, mg__