class CDSST: def __init__(self): path = 'frames/' day_images = SkyCameraFile.glob(path) day_images.sort() times = np.array( [SkyCameraFile.parseTime(x).datetime for x in day_images]) self.day_images = day_images self.times = times self.calibration = Calibration(catalog=SkyCatalog(True)) self.calibration.load() self.helper = CloudDetectionHelper() try: with open('cd_sst.cache', 'rb') as f: self.cache = pickle.load(f) except: self.cache = pd.DataFrame(index=pd.Index([], dtype=np.datetime64), columns=[ 'pos_x', 'pos_y', 'radius', 'stripe_min', 'stripe_max' ]) def save_cache(self): with open('cd_sst.cache', 'wb') as f: pickle.dump(self.cache, f) def detect(self, filename): image = cv2.imread(filename) time = SkyCameraFile.parseTime(filename).datetime mask = np.uint8(self.helper.get_mask(image).copy()) self.calibration.selectImage(filename) pos = self.calibration.project() pos = (pos[0], pos[1]) if time in self.cache.index: sun_x, sun_y, radius, min_x, max_x = self.cache.loc[time] sun = None sun_pos = None if not np.isnan(sun_x): sun_pos = (sun_x, sun_y) sun = CDSunRemoval.circle_mask(sun_pos, radius, mask.shape) sun_line = None if not np.isnan(min_x): sun_line = np.zeros(mask.shape, np.uint8) sun_line[:, min_x:max_x + 1] = True else: sun_line, min_x, max_x = CDSunRemoval.find_sun_line(image, pos) sun, sun_pos, radius = CDSunRemoval.find_sun(pos, mask) sun_x = sun_y = None if sun_pos is not None: sun_x = sun_pos[0] sun_y = sun_pos[1] self.cache.loc[time] = [sun_x, sun_y, radius, min_x, max_x] if sun_pos is None: sun_pos = pos mask = self.helper.fullmask.copy() mask[self.helper.mask == 0] = 0 mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((11, 11), np.uint8)) _, contours, _ = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) cloudiness = np.ones(mask.shape, np.float32) * .5 did_sun = False for contour in contours: area = cv2.contourArea(contour) is_sun = False if area > 100: # TODO: try different numbers single_contour = np.zeros(mask.shape, np.uint8) cv2.drawContours(single_contour, [contour], 0, 1, cv2.FILLED) if sun is not None and not did_sun: sun_area = np.sum(sun[self.helper.mask == 1]) if area > 0.9 * sun_area: joint_area = np.sum(np.logical_and( single_contour, sun)) if sun_area / joint_area > 0.9: is_sun = True if is_sun: if sun_area * 1.2 < area: difference = np.uint8( np.logical_and(np.logical_not(sun), single_contour)) _, contours2, _ = cv2.findContours( difference, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # filter smaller ones here! currently done with the if at the beginning contours += contours2 did_sun = True if not is_sun: cloudiness[single_contour > 0] = 1.0 b, g, r = cv2.split(np.int32(image)) mask = self.helper.mask rmb = r - b rmb[mask == 0] = 0 cloudiness[rmb < -10] = 0 cloudiness[rmb > 50] = 1 delta = np.timedelta64(39, 's') delta2 = np.timedelta64(0, 's') time_diff = time - self.times before = np.logical_and(time_diff > delta2, time_diff < delta) if np.sum(before) == 0: raise ValueError('No previous image found.') current_index = np.where(before)[0][0] prev_img = cv2.imread(self.day_images[current_index]) gray_prev = cv2.cvtColor(prev_img, cv2.COLOR_BGR2GRAY) gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) flow = cv2.calcOpticalFlowFarneback(gray_prev, gray_image, None, 0.5, 3, 15, 3, 5, 1.2, 0) flow2 = -cv2.calcOpticalFlowFarneback(gray_image, gray_prev, None, 0.5, 3, 15, 3, 5, 1.2, 0) mag1, _ = cv2.cartToPolar(flow[..., 0], flow[..., 1]) mag2, _ = cv2.cartToPolar(flow2[..., 0], flow2[..., 1]) movement = np.logical_and(mag1 > 1, mag2 > 1) no_movement = np.logical_not(movement) brightness = np.mean(image, 2) cloudiness[np.logical_and(movement == 1, cloudiness != 0)] = 1 cloudiness[self.helper.mask == 0] = 1 if sun is not None: cloudiness[sun == 1] = 1 if sun_line is not None: sun_line_dilated = cv2.morphologyEx(sun_line, cv2.MORPH_DILATE, np.ones((1, 3))) cloudiness[sun_line_dilated == 1] = 1 y, x = np.mgrid[0:brightness.shape[0], 0:brightness.shape[1]] x1 = [] y1 = [] out = [] for i in range(brightness.shape[0]): for j in range(brightness.shape[1]): if cloudiness[i, j] != 1: x1.append(x[i, j]) y1.append(y[i, j]) out.append(brightness[i, j]) x = np.array(x1) - sun_pos[0] y = np.array(y1) - sun_pos[1] out = np.array(out) dist = np.sqrt(x * x + y * y) A = np.array([dist, np.ones(x.shape), x, y]).transpose() A_inv = np.linalg.pinv(A) param = np.dot(A_inv, out) y, x = np.mgrid[0:brightness.shape[0], 0:brightness.shape[1]] x = x - pos[0] y = y - pos[1] dist = np.sqrt(x * x + y * y) A = np.array([dist, np.ones(x.shape), x, y]).transpose() gradient = np.dot(A, param).transpose() rect_size = 15 rect_border = (rect_size - 1) // 2 brightness_norm = brightness - gradient stddev = np.zeros(brightness.shape) for y in range(image.shape[0]): for x in range(image.shape[1]): if cloudiness[y, x] == 1: continue lx = x - rect_border rx = x + rect_border + 1 uy = y - rect_border dy = y + rect_border + 1 if lx < 0: lx = 0 if uy < 0: uy = 0 if rx > image.shape[1]: rx = image.shape[1] if dy > image.shape[0]: dy = image.shape[0] mask_part = cloudiness[uy:dy, lx:rx] stddev[y, x] = np.std(brightness_norm[uy:dy, lx:rx][mask_part != 1]) def_clear = np.sum(cloudiness == 0) cloudiness[cloudiness == 0.5] = (stddev > 3)[cloudiness == 0.5] if sun is None or (sun_line is None and radius < 100): if def_clear < 0.1 * np.sum(self.helper.mask == 1): cloudiness[np.logical_and(cloudiness == 0, rmb > -8)] = 1 cloudiness = self.helper.close_result(cloudiness) cloudiness[self.helper.mask == 0] = 0.5 if sun is not None: cloudiness[sun == 1] = 0.5 if sun_line is not None: cloudiness[sun_line_dilated == 1] = 0.5 return cloudiness def get_cloud_cover(self, cloudiness): return np.sum(cloudiness == 1) / np.sum(cloudiness != 0.5)
class StarCheckerHelper: def __init__(self, calibration_file): self.calibration = Calibration() self.calibration.load(calibration_file) def prepare(self, path, star_finder): with warnings.catch_warnings(): warnings.simplefilter('ignore', AstropyWarning) self.calibration.selectImage(path) self.names, self.vmag, alt, az = self.calibration.catalog.filter(Configuration.min_alt * u.deg, Configuration.max_mag) altaz = np.array([alt.radian, az.radian]).transpose() self.pos = np.column_stack(self.calibration.project(altaz)) self.finder = star_finder self.image = cv2.imread(path) self.finder.setImage(self.image) self.altaz = np.array([alt.degree, az.degree]).transpose() def count_stars(self): min_az = 0 max_az = 360 min_alt = Configuration.min_alt max_alt = 90 alt_step = Configuration.alt_step az_step = Configuration.az_step alt_bins = int((max_alt - min_alt) / alt_step) az_bins = int((max_az - min_az) / az_step) counts = np.zeros([alt_bins, az_bins, 4]) for alt_bin in range(alt_bins): alt = min_alt + alt_step * alt_bin for az_bin in range(az_bins): az = min_az + az_step * az_bin counts[alt_bin, az_bin, 2] = alt counts[alt_bin, az_bin, 3] = az for i in range(self.pos.shape[0]): aa = self.altaz[i] if aa[0] > alt and aa[0] <= alt + alt_step and aa[1] > az and aa[1] <= az + az_step: counts[alt_bin, az_bin, 0] += 1 if self.finder.isStar(self.pos[i][0], self.pos[i][1]): counts[alt_bin, az_bin, 1] += 1 return counts def get_image(self): result = self.image.copy() good_color = (0, 255, 0) bad_color = (0, 0, 255) for i in range(self.pos.shape[0]): if self.finder.isStar(self.pos[i][0], self.pos[i][1]): color = good_color else: color = bad_color cv2.circle(result, (int(self.pos[i][0]), int(self.pos[i][1])), 3, color) return result
class CDSST: def __init__(self): path = 'frames/' day_images = SkyCameraFile.glob(path) day_images.sort() times = np.array([SkyCameraFile.parseTime(x).datetime for x in day_images]) self.day_images = day_images self.times = times self.calibration = Calibration(catalog=SkyCatalog(True)) self.calibration.load() self.helper = CloudDetectionHelper() try: with open('cd_sst.cache', 'rb') as f: self.cache = pickle.load(f) except: self.cache = pd.DataFrame(index=pd.Index([], dtype=np.datetime64), columns=['pos_x', 'pos_y', 'radius', 'stripe_min', 'stripe_max']) def save_cache(self): with open('cd_sst.cache', 'wb') as f: pickle.dump(self.cache, f) def detect(self, filename): image = cv2.imread(filename) time = SkyCameraFile.parseTime(filename).datetime mask = np.uint8(self.helper.get_mask(image).copy()) self.calibration.selectImage(filename) pos = self.calibration.project() pos = (pos[0], pos[1]) if time in self.cache.index: sun_x, sun_y, radius, min_x, max_x = self.cache.loc[time] sun = None sun_pos = None if not np.isnan(sun_x): sun_pos = (sun_x, sun_y) sun = CDSunRemoval.circle_mask(sun_pos, radius, mask.shape) sun_line = None if not np.isnan(min_x): sun_line = np.zeros(mask.shape, np.uint8) sun_line[:, min_x:max_x + 1] = True else: sun_line, min_x, max_x = CDSunRemoval.find_sun_line(image, pos) sun, sun_pos, radius = CDSunRemoval.find_sun(pos, mask) sun_x = sun_y = None if sun_pos is not None: sun_x = sun_pos[0] sun_y = sun_pos[1] self.cache.loc[time] = [sun_x, sun_y, radius, min_x, max_x] if sun_pos is None: sun_pos = pos mask = self.helper.fullmask.copy() mask[self.helper.mask == 0] = 0 mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, np.ones((11, 11), np.uint8)) _, contours, _ = cv2.findContours(mask, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) cloudiness = np.ones(mask.shape, np.float32) * .5 did_sun = False for contour in contours: area = cv2.contourArea(contour) is_sun = False if area > 100: # TODO: try different numbers single_contour = np.zeros(mask.shape, np.uint8) cv2.drawContours(single_contour, [contour], 0, 1, cv2.FILLED) if sun is not None and not did_sun: sun_area = np.sum(sun[self.helper.mask == 1]) if area > 0.9 * sun_area: joint_area = np.sum(np.logical_and(single_contour, sun)) if sun_area / joint_area > 0.9: is_sun = True if is_sun: if sun_area * 1.2 < area: difference = np.uint8(np.logical_and(np.logical_not(sun), single_contour)) _, contours2, _ = cv2.findContours(difference, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) # filter smaller ones here! currently done with the if at the beginning contours += contours2 did_sun = True if not is_sun: cloudiness[single_contour > 0] = 1.0 b, g, r = cv2.split(np.int32(image)) mask = self.helper.mask rmb = r - b rmb[mask == 0] = 0 cloudiness[rmb < -10] = 0 cloudiness[rmb > 50] = 1 delta = np.timedelta64(39, 's') delta2 = np.timedelta64(0, 's') time_diff = time - self.times before = np.logical_and(time_diff > delta2, time_diff < delta) if np.sum(before) == 0: raise ValueError('No previous image found.') current_index = np.where(before)[0][0] prev_img = cv2.imread(self.day_images[current_index]) gray_prev = cv2.cvtColor(prev_img, cv2.COLOR_BGR2GRAY) gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) flow = cv2.calcOpticalFlowFarneback(gray_prev, gray_image, None, 0.5, 3, 15, 3, 5, 1.2, 0) flow2 = -cv2.calcOpticalFlowFarneback(gray_image, gray_prev, None, 0.5, 3, 15, 3, 5, 1.2, 0) mag1, _ = cv2.cartToPolar(flow[...,0], flow[...,1]) mag2, _ = cv2.cartToPolar(flow2[...,0], flow2[...,1]) movement = np.logical_and(mag1 > 1, mag2 > 1) no_movement = np.logical_not(movement) brightness = np.mean(image, 2) cloudiness[np.logical_and(movement == 1, cloudiness != 0)] = 1 cloudiness[self.helper.mask == 0] = 1 if sun is not None: cloudiness[sun == 1] = 1 if sun_line is not None: sun_line_dilated = cv2.morphologyEx(sun_line, cv2.MORPH_DILATE, np.ones((1, 3))) cloudiness[sun_line_dilated == 1] = 1 y, x = np.mgrid[0:brightness.shape[0], 0:brightness.shape[1]] x1 = [] y1 = [] out = [] for i in range(brightness.shape[0]): for j in range(brightness.shape[1]): if cloudiness[i, j] != 1: x1.append(x[i, j]) y1.append(y[i, j]) out.append(brightness[i, j]) x = np.array(x1) - sun_pos[0] y = np.array(y1) - sun_pos[1] out = np.array(out) dist = np.sqrt(x * x + y * y) A = np.array([dist, np.ones(x.shape), x, y]).transpose() A_inv = np.linalg.pinv(A) param = np.dot(A_inv, out) y, x = np.mgrid[0:brightness.shape[0], 0:brightness.shape[1]] x = x - pos[0] y = y - pos[1] dist = np.sqrt(x * x + y * y) A = np.array([dist, np.ones(x.shape), x, y]).transpose() gradient = np.dot(A, param).transpose() rect_size = 15 rect_border = (rect_size - 1) // 2 brightness_norm = brightness - gradient stddev = np.zeros(brightness.shape) for y in range(image.shape[0]): for x in range(image.shape[1]): if cloudiness[y, x] == 1: continue lx = x - rect_border rx = x + rect_border + 1 uy = y - rect_border dy = y + rect_border + 1 if lx < 0: lx = 0 if uy < 0: uy = 0 if rx > image.shape[1]: rx = image.shape[1] if dy > image.shape[0]: dy = image.shape[0] mask_part = cloudiness[uy:dy, lx:rx] stddev[y, x] = np.std(brightness_norm[uy:dy, lx:rx][mask_part != 1]) def_clear = np.sum(cloudiness == 0) cloudiness[cloudiness == 0.5] = (stddev > 3)[cloudiness == 0.5] if sun is None or (sun_line is None and radius < 100): if def_clear < 0.1 * np.sum(self.helper.mask == 1): cloudiness[np.logical_and(cloudiness == 0, rmb > -8)] = 1 cloudiness = self.helper.close_result(cloudiness) cloudiness[self.helper.mask == 0] = 0.5 if sun is not None: cloudiness[sun == 1] = 0.5 if sun_line is not None: cloudiness[sun_line_dilated == 1] = 0.5 return cloudiness def get_cloud_cover(self, cloudiness): return np.sum(cloudiness == 1) / np.sum(cloudiness != 0.5)