def find_offset(fname_path, xy=None, meter_id=None, code=None): # if not fname_path.endswith('008_v.jpg'): # return if xy is None: xy = (978, 516) xy = tuple(map(int, xy)) image0 = cv.imread(fname_path) # , cv.IMREAD_GRAYSCALE) # Debug.log_image('image0') qr_list = QrDecode.get_all_qrs(image0) if not len(qr_list): logger.error(f' not found any qr mark in {fname_path}') return 9999, 9999 # if len(qr_list) > 1: # Debug.error(f' more than one ({len(qr_list)}) marks in {fname_path}') # qr_list = sorted(qr_list, key=lambda x: x.box.area(), reverse=True) qr_list = [qr_mark for qr_mark in qr_list if code == qr_mark.code.decode('utf-8')] if not len(qr_list): logger.error(f'not found mark for code {code} in file {fname_path}') return 9999, 9999 anchor = qr_list[0].anchor img = image0.copy() cv.circle(img, xy, 10, colors.BGR_YELLOW, 2) KeyPoint.draw_list(anchor, ['kp', 'kp1', 'kp2'], img) Debug.log_image(f'{meter_id}_offs_target', img) offset = KeyPoint.xy_to_offset(xy, anchor) print(f'file {fname_path}: code={qr_list[0].code} offset={offset[0]:.5f},{offset[1]:.5f}') return offset
def main_find_qr(): shutil.rmtree(Cfg.log_folder, ignore_errors=True) os.makedirs(Cfg.log_folder, exist_ok=True) Debug.set_params(log_folder=Cfg.log_folder, log_image=Cfg.verbose) for folder in sorted(glob.glob(f'{Cfg.inp_folders}')): if not os.path.isdir(folder): continue files_cnt = 0 files_found = 0 for fname_path in glob.glob(f'{folder}/{Cfg.inp_fname_mask}'): Debug.set_log_image_names(fname_path) ok_cnt = process_file(fname_path) # ok_cnt=0 # find_offset(fname_path) if ok_cnt: files_found += 1 files_cnt += 1 print( f'\t\t{fname_path}: {ok_cnt} marks. Files: ' f'{files_found}/{files_cnt} rate={100*files_found/files_cnt:.2f}%') print(f'Folder {folder}: {files_found}/{files_cnt} rate={100*files_found/files_cnt:.2f}%')
def main(): shutil.rmtree(res_folder, ignore_errors=True) os.makedirs(res_folder, exist_ok=True) tot_cnt, ok_cnt = 0, 0 for fname_path in glob.glob(f'{inp_folder}/{inp_mask}'): Debug.set_log_image_names(fname_path, res_folder) image0 = cv.imread(fname_path, cv.IMREAD_GRAYSCALE) qr_anchor_list = QrAnchorList( image0) # qr_anchor - 3 big squares keypoints for qr_anchor in qr_anchor_list: qr_area = QrArea( image0, qr_anchor ) # qr_are is a part of image0 with qr_code and all related methods # qr_area_img = qr_area.draw() # Debug.log_image('qr_area_img') qr_list = qr_read.QrMarkList(qr_area.image_finished) qr_list.draw_boxes() qr_list.draw_polygons() found = len(qr_list.qr_marks) ok_cnt += 1 if found else 0 tot_cnt += 1 print( f'{fname_path}: Total: {ok_cnt}/{tot_cnt} ({ok_cnt/tot_cnt*100:.2f}%)' )
def __init__(self, anchors): # anchors --> candidate_areas: self.image = anchors.image self.anchors = anchors.anchors self.candidate_qr_areas = [] for ind in range(len(self.anchors)): anchor = self.anchors[ind] kp, kp1, kp2 = anchor # # if Cfg.area_preparing_method == 'warp_affine': # todo remove, it's just a test # # # Extract subregion of qr_area from the entire image # # qr_area = self.get_subimage_warp(anchor, self.image) # # Debug.log_image('area_after_warp', qr_area) # else: # method=='subimage': find 4th corner -> stretch -> fit_to_shape -> crop # find 4th point kp4 = kp.find_4th_corner(kp1, kp2) # 3 squares --> 4th square # rectangle of 4 square centers --> qr_area rectangle corners = KeyPoint.expand(kp1, kp, kp2, kp4, Cfg.expand_ratio) # correct corners which are out of image (after stretching) qr_area_keypoints = KeyPoint.fit_to_shape(corners, self.image.shape) # Extract subregion of qr_area from the entire image qr_area, center, size, theta = self.get_subimage(qr_area_keypoints, self.image) # make otsu binarization if ordered in Cfg if Cfg.use_otsu_threshold: blur = cv.GaussianBlur(qr_area, (5, 5), 0) ret, candidate_area = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) else: candidate_area = qr_area # set axis info (cross_x,cross_y,xaxis_angle) Debug.log_image(f'finished_{ind}', candidate_area) self.candidate_qr_areas.append((candidate_area, center, size, theta))
def __init__(self, image, qr_ancor): self.image = image self.qr_anchor = qr_ancor self.center = KeyPoint(size=0, x=int(mean([kp.x for kp in self.qr_anchor])), y=int(mean([kp.x for kp in self.qr_anchor]))) self.radius = max([self.center.distance(kp) for kp in qr_ancor]) self.qr_roi = self.image[self.center.x - self.radius:self.center.x + self.radius, self.center.y - self.radius:self.center.y + self.radius] blur = cv.GaussianBlur(self.qr_roi, (5, 5), 0) ret, self.thresh_otsu = cv.threshold(blur, 0, 255, cv.THRESH_BINARY + cv.THRESH_OTSU) ret2, self.image_thresholded = cv.threshold(image, self.thresh_otsu, 255, cv.THRESH_BINARY) self.mask = np.zeros(self.image_thresholded.shape, dtype=np.uint8) self.mask[self.center.x - self.radius:self.center.x + self.radius, self.center.y - self.radius:self.center.y + self.radius] = 255 Debug.log_image('mask') self.image_finished = self.image_thresholded self.image_thresholded[self.mask == 0] = 255 Debug.log_image('image_finished')
def draw_anchors(self): for ind in range(len(self.anchors)): anchor = self.anchors[ind] img = cv.cvtColor(self.image, cv.COLOR_GRAY2BGR) for kp in anchor: cv.circle(img, (kp.x, kp.y), kp.size, colors.BGR_RED, 2) anchor[0].draw_beam(anchor[1], ' ', img, colors.BGR_ORANGE) # angle>0 ==> kp1 is xaxis (I suppose) Debug.log_image(f'anchor_{ind}', img)
def draw_qr_area(self, image): if len(image.shape == 2): img_qr_area = cv.cvtColor(image, cv.COLOR_GRAY2BGR) else: img_qr_area = image.copy() for kp in self.qr_anchor: cv.circle(img_qr_area, (kp.x, kp.y), int(kp.size / 2), colors.BGR_GREEN, 1) cv.circle(img_qr_area, self.center, self.radius, colors.BGR_GREEN, 1) Debug.log_image('img_qr_area') return img_qr_area
def main_find_offset(): MeterXy = collections.namedtuple('MeterXy', ['file_name', 'meter_id', 'code', 'x', 'y']) MeterOff = collections.namedtuple('MeterXy', ['file_name', 'meter_id', 'code', 'x', 'y', 'off_x', 'off_y']) folder = '../tmp/out/images/2019-08-08' # read meter_xy.csv with open(Cfg.xy_info_file, newline='') as csvfile: reader = csv.reader(csvfile, delimiter=',', quotechar='"') next(reader, None) # skip headers meter_xy_list = [MeterXy(row[0], row[1], row[2], row[3], row[4]) for row in reader] shutil.rmtree(Cfg.log_folder, ignore_errors=True) os.makedirs(Cfg.log_folder, exist_ok=True) Debug.set_params(log_folder=Cfg.log_folder, log_image=Cfg.verbose) offsets = [] for m in meter_xy_list: # if not str(m.file_name).endswith('447_v'): # 554 # continue fname_path = f'{folder}/{m.file_name}.jpg' Debug.set_log_image_names(fname_path) off = find_offset(fname_path, (m.x, m.y), m.meter_id, m.code) offsets.append(MeterOff(m.file_name, m.meter_id, m.code, m.x, m.y, off[0], off[1])) offsets = sorted(offsets, key=lambda x: x.meter_id) print('offsets:\n\t', '\n\t'.join([f'{m.meter_id} : {m.file_name} : ({m.off_x:.5f},{m.off_y:.5f}) ' f'xy:({m.x},{m.y})' for m in offsets if m.off_x != 9999.]), sep='') for meter_id in sorted(list(set([off.meter_id for off in offsets])), key=lambda x: int(x)): lst = [(m.off_x, m.off_y) for m in offsets if m.meter_id == meter_id and m.off_x != 9999] xy = [[l[i] for l in lst] for i in [0, 1]] # avg = (statistics.mean([l[0] for l in lst]), statistics.mean([l[0] for l in lst])) avg = (statistics.mean(xy[0]), statistics.mean(xy[1])) min_v = (min(xy[0]), min(xy[1])) max_v = (max(xy[0]), max(xy[1])) diff = (max_v[0] - min_v[0], max_v[1] - min_v[1]) print(f'meter_id={meter_id} ' f'avg=({avg[0]: .5f},{avg[1]: .5f}) ' f'min=({min_v[0]: .5f},{min_v[1]: .5f}) ' f'max=({max_v[0]: .5f},{max_v[1]: .5f})' f'diff=({diff[0]: .5f},{diff[1]: .5f})' )
def main(): logger.debug('metering - start') Debug.set_params(log_folder=Cfg.log_folder, log_image=Cfg.log_image) if not os.path.isdir(Cfg.inp_folder): logger.error(f'Input folder {Cfg.inp_folder} does not exist.') return try: while True: if Cfg.need_sync: sync_meterings() files_list = sorted( glob.glob(f'{Cfg.inp_folder}/{Cfg.inp_fname_mask}')) if not len(files_list): if _GroupEquip.ready_to_analyze(event='empty_dir'): Analyzer.run() logger.debug(f'timeout {Cfg.inp_timeout} sec') time.sleep(Cfg.inp_timeout) # sec continue start = datetime.datetime.now() for (files_cnt, fname_path_flir) in enumerate(files_list, 1): cnt, meter_ids = take_readings(fname_path_flir) remove_input_files(fname_path_flir) if not cnt or not len(meter_ids): continue # skip it, no mark/equips/readings here logger.info( f'{files_cnt} of {len(files_list)}: {fname_path_flir} readings:{cnt} ' f'equip:{list(set([Db.meter_to_equip(m) for m in meter_ids]))}' ) seconds = (datetime.datetime.now() - start).seconds print( f'Processed {files_cnt} files in {seconds:.0f}s, ({seconds/files_cnt:.0f} sec/file)' ) Db.close() except KeyboardInterrupt: logger.info('metering is interrupted by user') if _GroupEquip.ready_to_analyze(event='the_end'): Analyzer.run() finally: Db.close()
def get_blob_keypoints(self): # input image --> list of blob keypoints params = cv.SimpleBlobDetector_Params() # Filter by Area. params.filterByArea = True params.minArea = MyMath.circle_area_by_diameter(Cfg.min_blob_diameter) # Set up the detector detector = cv.SimpleBlobDetector_create(params) # Detect blobs keypoints = detector.detect(self.image) # Draw detected blobs as red circles. # cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures the size of the circle corresponds to the size of blob img_with_keypoints = cv.drawKeypoints(self.image, keypoints, np.array([]), colors.BGR_BLUE, cv.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS) Debug.log_image('img_with_keypoints', img_with_keypoints) blob_keypoints = KeyPointList(blob_detector_keypoints_list=keypoints) logger.debug(f'keypoints found:{blob_keypoints}') return blob_keypoints
def __init__(self, candidate_areas): self.image = candidate_areas.image self.areas = candidate_areas.candidate_qr_areas self.anchors = candidate_areas.anchors self.qr_decoded_marks = None # areas --> qr_decoded_marks: marks = [] for ind in range(len(self.areas)): area, center, size, theta = self.areas[ind] pyzbar_objs = pyzbar.decode(area) if not len(pyzbar_objs): continue if len(pyzbar_objs) > 1: logger.info(f'Multiple codes ({len(pyzbar_objs)}) found in area {ind}') # skip it, definitely they should be found separately continue mark = QrMark(pyzbar_objs[0], area, self.anchors[ind], ind, center, size, theta) if self.area_ratio(mark, area, ind) < Cfg.min_area_ratio: logger.debug(f'found box area ratio is too small relatively to CandidateArea. Skipped. ind={ind}') continue marks.append(mark) Debug.log_image(f'found_{ind}_{mark.code}', mark.draw_box(area)) # Debug.log_image(f'found_area_{ind}_{mark.code}', area) # duplicates rarely created if several triplets looks like anchor while referencing to the same qr code area # remove duplicates from marks: # for equal codes take one with the minimal area uniq_codes = list(set([m.code for m in marks])) # list of unique codes from marks code_minareas = [(c, min([m.box_area for m in marks if m.code == c])) for c in uniq_codes] # list of tuples (code, min area for all marks with this code) # get items from marks which have minimal area for each uniq code self.qr_decoded_marks = [m for m in marks if (m.code, m.box.area()) in code_minareas] logger.debug(f'decoded marks list after removing duplicates:{self.qr_decoded_marks}]')
def take_readings(fname_path_flir): # file --> db + files in images/date Debug.set_log_image_names(fname_path_flir) try: fi = FlirImage(fname_path_flir) except (ValueError, KeyError): logger.exception( f'error while flir-processing file {fname_path_flir}. Skipped.') return 0, None qr_mark_list = QrDecode.get_all_qrs(fi.visual_img) visual_img_copy = fi.visual_img.copy() thermal_img_copy = fi.thermal_img.copy() reading_cnt = 0 meter_ids = [] for qr_mark in qr_mark_list: meter_records = Db.get_meters_from_db(qr_mark.code) if meter_records is None: logger.info(f'qrs_to_readings: no equips for qr_mark {qr_mark}') continue for meter_id, offset_x, offset_y in meter_records: meter_ids.append(meter_id) if offset_x == 9999.: continue logger.debug(f'take readings: meter_id={meter_id}: ') reading = Reading(meter_id, (offset_x, offset_y), qr_mark, fi) if reading.temperature is None: logger.error( f'cannot create Reading ' f'for meter_id={meter_id} offset=({offset_x},{offset_y}) ' f'due to illegal coordinates after offset_to_xy()') continue if _GroupEquip.ready_to_analyze(event='readings_taken', meter_ids=meter_ids): Analyzer.run() reading.save_to_db() # reading.save_to_csv() # logger.debug(reading) reading.draw_visual(visual_img_copy) reading.draw_thermal(thermal_img_copy) Debug.log_image('visual_read', visual_img_copy) Debug.log_image('thermal_read', thermal_img_copy) reading_cnt += 1 return reading_cnt, meter_ids
def process_file(fname_path): image0 = cv.imread(fname_path, cv.IMREAD_GRAYSCALE) Debug.log_image('image0') qr_list = QrDecode.get_all_qrs(image0) found = len(qr_list) # cnt marks found return found