def _count_du(self, x, y, size=5, bg=None): wmrg = size // 4 mmrg = 1 if bg is None else 0 mask = ImageProc.bsphkern(size + 2 * mmrg) if bg is None: mask[0, :] = 0 mask[-1, :] = 0 mask[:, 0] = 0 mask[:, -1] = 0 mask = mask.astype(np.bool) mr = size // 2 + mmrg mn = size + 2 * mmrg h, w, _ = self.image.shape x, y = int(round(x)), int(round(y)) if h - y + wmrg <= mr or w - x + wmrg <= mr or x + wmrg < mr or y + wmrg < mr: return zip([None] * 3, [None] * 3) win = self.image[max(0, y - mr):min(h, y + mr + 1), max(0, x - mr):min(w, x + mr + 1), :].reshape((-1, 3)) mx0, mx1 = -min(0, x - mr), mn - (max(w, x + mr + 1) - w) my0, my1 = -min(0, y - mr), mn - (max(h, y + mr + 1) - h) mask = mask[my0:my1, mx0:mx1].flatten() bg = np.mean(win[np.logical_not(mask), :], axis=0) if bg is None else bg if False: tot = np.sum(win[mask, :], axis=0) tot_bg = bg * np.sum(mask) tot = np.max(np.array((tot, tot_bg)), axis=0) # tried to take into account thumbnail mean resizing after gamma correction, # also assuming no saturation of original pixels because of motion blur # => better results if tune Camera->emp_coef instead resizing_gain = (1 / self.resize_scale)**2 g = self.applied_gamma # ([sum over i in n: (bg+s_i)**g] / n) ** (1/g) # => cannot compensate for gamma correction as signal components not summable anymore, # only possible if assume that only a single pixel has signal (or some known distribution of signal?) # signal in a single, non-saturating pixel (conflicting assumptions): adj_tot = (((tot - tot_bg + bg)**g * resizing_gain) - (resizing_gain - 1) * bg**g)**(1 / g) - bg signal = adj_tot else: #signal = tot - tot_bg signal = np.clip(np.sum(win[mask, :] - bg, axis=0), 0, np.inf) return zip(signal, bg)
def _feature_detection_mask(self, image): _, mask = cv2.threshold(image, self.min_feature_intensity, 255, cv2.THRESH_BINARY) kernel = ImageProc.bsphkern(round(6 * image.shape[0] / 512) * 2 + 1) # exclude asteroid limb from feature detection mask = cv2.erode(mask, ImageProc.bsphkern(7), iterations=1) # remove stars mask = cv2.dilate(mask, kernel, iterations=1) # remove small shadows inside asteroid mask = cv2.erode(mask, kernel, iterations=2) # remove asteroid limb # exclude overexposed parts _, mask_oe = cv2.threshold(image, self.max_feature_intensity, 255, cv2.THRESH_BINARY) mask_oe = cv2.dilate(mask_oe, kernel, iterations=1) mask_oe = cv2.erode(mask_oe, kernel, iterations=1) mask[mask_oe > 0] = 0 if 0: cv2.imshow('mask', ImageProc.overlay_mask(image, mask)) cv2.waitKey() return mask
def calc_source_kernel(cams, spectrum_fn, patch_size, points=None): # detect source kernel = ImageProc.bsphkern( tuple(map(int, patch_size)) if '__iter__' in dir(patch_size) else int(patch_size)) expected_bgr = np.zeros(3) for i, cam in enumerate(cams): ef, _ = Camera.electron_flux_in_sensed_spectrum_fn(cam.qeff_coefs, spectrum_fn, cam.lambda_min, cam.lambda_max, fast=False, points=points) expected_bgr[i] = cam.gain * cam.aperture_area * cam.emp_coef * ef kernel = np.repeat(np.expand_dims(kernel, axis=2), 3, axis=2) * expected_bgr return kernel
def detect_moon(self): x, y = tuple(int(v) for v in self.moon_loc) win = self.image[y - 24:y + 25, x - 29:x + 30, :] if 0: # TODO: what transformation would make this work? win = ImageProc.adjust_gamma(win / 662 * 1023, 2.2, inverse=1, max_val=1023) h, w, s, c = *win.shape[0:2], 19, 18 mask = np.zeros((h, w), dtype='uint8') mask[(h // 2 - s):(h // 2 + s + 1), (w // 2 - s):(w // 2 + s + 1)] = ImageProc.bsphkern(s * 2 + 1).astype('uint8') mask[0:c, :] = 0 if SHOW_MEASURES: mask_img = (mask.reshape( (h, w, 1)) * np.array([255, 0, 0]).reshape( (1, 1, 3))).astype('uint8') win_img = np.clip(win, 0, 255).astype('uint8') plt.imshow(np.flip(ImageProc.merge((mask_img, win_img)), axis=2)) plt.show() mask = mask.flatten().astype('bool') n = np.sum(mask) measures = [] for i, cam in enumerate(self.cam): raw_du = np.sum(win[:, :, i].flatten()[mask]) bg_du = np.mean(win[:, :, i].flatten()[np.logical_not(mask)]) du_count = raw_du - bg_du * n measures.append(MoonMeasure(self, i, du_count)) self.measures = measures return measures
def detect_source(self, kernel, total_radiation=False): assert kernel.shape[0] % 2 and kernel.shape[ 1] % 2, 'kernel width and height must be odd numbers' kernel = self.gain * self.exposure * kernel fkernel = ImageProc.fuzzy_kernel(kernel, self.impulse_spread) method = [cv2.TM_CCORR_NORMED, cv2.TM_SQDIFF][0] corr = cv2.matchTemplate(self.image.astype(np.float32), fkernel.astype(np.float32), method) _, _, minloc, maxloc = cv2.minMaxLoc( corr) # minval, maxval, minloc, maxloc loc = minloc if method in (cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED) else maxloc loc_i = tuple(np.round(np.array(loc)).astype(np.int)) if SHOW_LAB_MEASURES: sc = 1024 / self.image.shape[1] img = cv2.resize(self.image.astype(np.float), None, fx=sc, fy=sc) / np.max(self.image) center = np.array(loc) + np.flip(fkernel.shape[:2]) / 2 img = cv2.circle( img, tuple(np.round(center * sc).astype(np.int)), round((kernel.shape[0] - self.impulse_spread) / 2 * sc), [0, 0, 1.0]) cv2.imshow('te', img) print('waiting...', end='', flush=True) cv2.waitKey() print('done') if True: # use result as a mask to calculate mean and variance kh, kw = np.array(fkernel.shape[:2]) // 2 win = self.image[loc_i[1]:loc_i[1] + kh * 2 + 1, loc_i[0]:loc_i[0] + kw * 2 + 1, :].reshape( (-1, 3)) kernel_max = np.max(np.sum(kernel, axis=2)) mask = np.sum(fkernel, axis=2) / kernel_max > 0.95 mean = np.median(win[mask.flatten(), :], axis=0) std = np.std(win[mask.flatten(), :], axis=0) n = np.sum(mask) tmp = np.zeros(self.image.shape[:2]) tmp[loc_i[1]:loc_i[1] + kh * 2 + 1, loc_i[0]:loc_i[0] + kw * 2 + 1] = mask img_m = tmp else: # calculate a correlation channel (over whole image) k = kernel.shape[0] // 2 corr = cv2.matchTemplate( self.image.astype(np.float32), kernel[k:k + 1, k:k + 1, :].astype(np.float32), method) # calculate mean & variance of kernel area using corr channel win = corr[loc_i[1] - k:loc_i[1] + k + 1, loc_i[0] - k:loc_i[0] + k + 1] corr_mean = np.mean(win) corr_std = np.std(win) # threshold using mean - sd _, mask = cv2.threshold(corr, corr_mean - corr_std, 1, cv2.THRESH_BINARY) # dilate & erode to remove inner spots krn1 = ImageProc.bsphkern(round(1.5 * corr.shape[0] / 512) * 2 + 1) krn2 = ImageProc.bsphkern(round(2 * corr.shape[0] / 512) * 2 + 1) mask = cv2.dilate(mask, krn1, iterations=1) # remove holes mask = cv2.erode(mask, krn2, iterations=1) # same size mask = mask.astype(np.bool) # use result as a mask to calculate mean and variance mean = np.mean(self.image.reshape((-1, 3))[mask.flatten()], axis=0) var = np.var(self.image.reshape((-1, 3))[mask.flatten()], axis=0) n = np.sum(mask) if self.debug: sc = 1024 / self.image.shape[1] img_m = np.repeat(np.atleast_3d(img_m.astype(np.uint8) * 127), 3, axis=2) merged = ImageProc.merge( (self.image.astype(np.float32) / np.max(self.image), img_m.astype(np.float32) / 255)) img = cv2.resize(merged, None, fx=sc, fy=sc) cv2.imshow('te', img) arr_n, lims, _ = plt.hist(self.image[:, :, 1].flatten(), bins=np.max(self.image) + 1, log=True, histtype='step') plt.hist(win[mask.flatten(), 1].flatten(), bins=np.max(self.image) + 1, log=True, histtype='step') x = np.linspace(0, np.max(win), np.max(win) + 1) i = list(np.logical_and(lims[1:] > mean[1], arr_n > 0)).index(True) plt.plot(x, arr_n[i] * np.exp(-((x - mean[1]) / std[1])**2)) plt.ylim(1e-1, 1e6) plt.figure() plt.imshow(self.image[loc_i[1]:loc_i[1] + kh * 2 + 1, loc_i[0]:loc_i[0] + kw * 2 + 1, :] / np.max(self.image)) print('waiting (%.1f, %.1f)...' % (mean[1], std[1]), end='', flush=True) cv2.waitKey(1) plt.show() print('done') return LabMeasure(self, mean, std, n)