def main(): features = {} fn = r"C:\Users\Neil\Desktop\R3 crack\raw PL images\cracked wafer PL image.tif" im = ip.open_image(fn).astype(np.float32) if im.shape[0] > 700: print ' WARNING: Image resized' im_max = im.max() im = ndimage.zoom(im, 0.5) if im.max() > im_max: im[im > im_max] = im_max if False: view = ImageViewer(im) view.show() features['_alg_mode'] = 'mono wafer' crop_props = cropping.crop_wafer_cz(im, create_mask=True, skip_crop=False) features['corners'] = crop_props['corners'] cropped = cropping.correct_rotation( im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE_CZ, fix_chamfer=False) mono_wafer.feature_extraction(cropped, crop_props, features=features) ip.print_metrics(features) rgb = mono_wafer.create_overlay(features) view = ImageViewer(rgb) view.show()
def feature_extraction(im, features): t_start = timeit.default_timer() # crop crop_props = crop(im) features['corners'] = crop_props['corners'] #print crop_props.keys() #features['crop_top'] = crop_props['crop_top'] # features['corner_tl_x'] = crop_props['corners'][0][1] # features['corner_tl_y'] = crop_props['corners'][0][0] # features['corner_tr_x'] = crop_props['corners'][1][1] # features['corner_tr_y'] = crop_props['corners'][1][0] # features['corner_br_x'] = crop_props['corners'][2][1] # features['corner_br_y'] = crop_props['corners'][2][0] # features['corner_bl_x'] = crop_props['corners'][3][1] # features['corner_bl_y'] = crop_props['corners'][3][0] features['wafer_radius'] = crop_props['radius'] features['_wafer_middle_orig'] = crop_props['center'] features['crop_rotation'] = crop_props['estimated_rotation'] cropped = cropping.correct_rotation(im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE_CZ, fix_chamfer=False) if not cropped.flags['C_CONTIGUOUS']: cropped = np.ascontiguousarray(cropped) if False: view = ImageViewer(im) ImageViewer(cropped) view.show() # histogram features h, w = cropped.shape ip.histogram_percentiles(cropped, features, h // 2, w // 2, features['wafer_radius']) # normalise image min_val = features['hist_percentile_01'] / float(features['hist_percentile_99']) norm_upper = features['hist_percentile_99'] norm_lower = min(0.2, min_val) normed = ((cropped / norm_upper) - norm_lower) / (1 - norm_lower) # calculate distance from wafer rotation middle r, theta = np.empty_like(normed, np.float32), np.empty_like(normed, np.float32) pixel_ops.CenterDistance(r, theta, h // 2, w // 2) features['im_center_dist_im'] = r # create mask: 1=background wafer_mask = np.zeros_like(cropped, np.uint8) pixel_ops.ApplyThresholdGT_F32_U8(features['im_center_dist_im'], wafer_mask, features['wafer_radius'], 1) features['bl_cropped_u8'] = wafer_mask features['im_cropped_u8'] = (np.clip(normed, 0.0, 1.0) * 255).astype(np.uint8) if cropped.dtype.type is np.uint16: features['im_cropped_u16'] = cropped else: features['im_cropped_u16'] = cropped.astype(np.uint16) # compute runtime t_stop = timeit.default_timer() features['runtime'] = t_stop - t_start return crop_props
def run_single(fn, display=True, downsize=True): features = {} im = ip.open_image(fn).astype(np.float32) if downsize and im.shape[0] > 750: print ' WARNING: Image resized' im_max = im.max() im = ndimage.zoom(im, 0.5) if im.max() > im_max: im[im > im_max] = im_max if False: view = ImageViewer(im) view.show() parameters.SLOPE_MULTI_WAFER = True parameters.BORDER_ERODE = 3 parameters.MIN_IMPURE_AREA = 0.01 features['_alg_mode'] = 'multi wafer' features['_fn'] = os.path.splitext(os.path.split(fn)[1])[0] crop_props = cropping.crop_wafer(im, create_mask=True) features['corners'] = crop_props['corners'] cropped = cropping.correct_rotation(im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE) multi_wafer.feature_extraction(cropped, crop_props, features=features) multi_wafer.combined_features(features) rgb = multi_wafer.create_overlay(features) f = ip.print_metrics(features, display=display) if display: print "Wafer type: %s" % multi_wafer.WaferType.types[ features['wafer_type']] view = ImageViewer(rgb) ImageViewer(im) view.show() return f, features['im_cropped_u8'], rgb
def run_cropping(files, mode=None, display=True): for e, fn in enumerate(files): print "%s (%d/%d)" % (fn, e, len(files)) features = {} im = ip.open_image(fn).astype(np.float32) if mode == "cell": rotated = cropping.correct_cell_rotation(im, features, already_cropped=False) cropped = cropping.crop_cell(rotated, im, features, width=None, already_cropped=False) elif mode == "mono wafer": features['_alg_mode'] = 'mono wafer' crop_props = cropping.crop_wafer_cz(im, create_mask=True, skip_crop=False) features.update(crop_props) cropped = cropping.correct_rotation( im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE_CZ, fix_chamfer=False) if False: # save crop results pil_im = cropping.draw_crop_box(im, features, mode="pil") fn_root = os.path.splitext(os.path.split(fn)[1])[0] fn_out = os.path.join(r"C:\Users\Neil\Desktop\results\crop", fn_root + ".png") pil_im.save(fn_out) else: rgb = cropping.draw_crop_box(im, features, mode="rgb") pprint(features) view = ImageViewer(rgb) view.show()
def feature_extraction(im, features, skip_features=False): # median filter to remove noise im = cv2.medianBlur(im, 3) h, w = im.shape # normalize hist_features = {} ip.histogram_percentiles(im, hist_features) norm = im / hist_features['hist_percentile_99.9'] pixel_ops.ClipImage(norm, 0, 1) # automatically determine if square or round is_round = True crop_props = None try: # try cropping using wafer alg # im = np.ascontiguousarray(im[::-1, :]) crop_props = cropping.crop_wafer_cz(im, create_mask=True, output_error=False) # if round, rotation will likely be high if abs(crop_props['estimated_rotation']) < 5: # make sure most of foreground mask is actually foreground f = {} ip.histogram_percentiles(im, f) norm = im / f['hist_percentile_99.9'] coverage = (norm[crop_props['mask'] == 0] > 0.5).mean() if coverage > 0.97: is_round = False if False: print coverage view = ImageViewer(norm) ImageViewer(crop_props['mask']) view.show() except: pass if False: print "Is round:", is_round view = ImageViewer(im) view.show() if is_round: # find center and radius find_slug(norm, features) else: # pre-crop cropped = cropping.correct_rotation(im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE_CZ, fix_chamfer=False) features['bl_uncropped_u8'] = crop_props['mask'] features['bl_cropped_u8'] = crop_props['mask'] features['center_y'] = crop_props['center'][0] features['center_x'] = crop_props['center'][1] features['radius'] = crop_props['radius'] features['corners'] = crop_props['corners'] features['center'] = crop_props['center'] features['crop_rotation'] = 0 if False: view = ImageViewer(im) ImageViewer(cropped) ImageViewer(crop_props['mask']) view.show() im = np.ascontiguousarray(cropped, dtype=im.dtype) norm = im / hist_features['hist_percentile_99.9'] # set corners (note: this is for consistency. in current implementation there is no cropping) features['corner_tl_x'] = 0 features['corner_tl_y'] = 0 features['corner_tr_x'] = w - 1 features['corner_tr_y'] = 0 features['corner_br_x'] = w - 1 features['corner_br_y'] = h - 1 features['corner_bl_x'] = 0 features['corner_bl_y'] = h - 1 if False: view = ImageViewer(norm) ImageViewer(features['bl_uncropped_u8']) view.show() if skip_features or ('input_param_skip_features' in features and int(features['input_param_skip_features']) == 1): return # PL metrics hist = ip.histogram_percentiles(im, features, features['center_y'], features['center_x'], features['radius']) if False: # features['radius'] = features['radius'] rgb = create_overlay(im, features) # ImageViewer(im) ImageViewer(rgb) plt.figure() plt.plot(hist) plt.show() # rds rds(norm, features) # dark/bright corners radial_profile(norm, features) # rings ring_strength(norm, features)
def handle(self): reload(parameters) # self.request is the TCP socket connected to the client # get the image dimensions, which is contain in the first two # unsigned shorts (two bytes each) start_time = str(datetime.datetime.now()) mode = struct.unpack('B', self.get_data(1))[0] print('Request received at %s (mode=%d)' % (start_time, mode)) if mode == 255: print(' Mode: Exit') self.server.shutdown() return if mode == 0: msg = struct.pack('=B', 0) self.send_data(msg) return # get input images image_desc_length = struct.unpack('=I', self.get_data(4))[0] if image_desc_length == 0: print "ERROR: No images passed as input" return image_names_in = self.get_data(image_desc_length).split(',') images = {} for im_name in image_names_in: data = self.get_data(6) width, height, bit_depth, binning = struct.unpack('=HHBB', data) num_pixels = width * height if num_pixels == 0: # read from disk fn_len = struct.unpack('=B', self.get_data(1))[0] fn = str(self.get_data(fn_len)) images[im_name] = ip.open_image(fn) else: if bit_depth == 8: pixel_data = self.get_data(num_pixels) im_data = np.frombuffer(pixel_data, np.uint8) elif bit_depth == 16: pixel_data = self.get_data(num_pixels * 2) im_data = np.frombuffer(pixel_data, np.uint16) elif bit_depth == 32: pixel_data = self.get_data(num_pixels * 4) im_data = np.frombuffer(pixel_data, np.float32) images[im_name] = im_data.reshape(height, width).astype(np.float32) # get numerical parameters data = self.get_data(4) param_desc_length = struct.unpack('=I', data)[0] if param_desc_length > 0: param_names = self.get_data(param_desc_length).split(",") num_params = len(param_names) param_data = self.get_data(num_params * 4) params_array = list(np.frombuffer(param_data, np.float32)) else: param_names = [] params_array = [] # get string parameters data = self.get_data(4) param_desc_length = struct.unpack('=I', data)[0] if param_desc_length > 0: param_names += self.get_data(param_desc_length).split(",") param_vals_length = struct.unpack('=I', self.get_data(4))[0] params_array += self.get_data(param_vals_length).split(",") # override defaults in parameters.py for pn, pv in zip(param_names, params_array): if pn.upper() in dir(parameters): setattr(parameters, pn.upper(), pv) # store input parameters in the features dict param_names = ['input_param_' + pn for pn in param_names] features = dict(zip(param_names, params_array)) if 'input_param_already_cropped' in features and int( features['input_param_already_cropped']) == 1: already_cropped = True else: already_cropped = False if 'input_param_return_uncropped' in features and int( features['input_param_return_uncropped']) == 1: return_uncropped = True else: return_uncropped = False if 'input_param_return_cropped' in features and int( features['input_param_return_cropped']) == 0: return_cropped = False else: return_cropped = True if 'input_param_return_outline' in features and int( features['input_param_return_outline']) == 1: return_outline = True else: return_outline = False # call image processing algorithm try: if mode == 10: print(' Mode: Hash Pattern correction') im_raw = images['im_pl'].astype(np.float32) im_corrected = FF.correct_hash_pattern(im_raw) features['im_corrected_u16'] = im_corrected.astype(np.uint16) elif mode == 40: print(' Mode: Block processing') im = images['im_pl'].astype(np.float32) block.feature_extraction(im, features, crop=not already_cropped) features['crop_left'] = features['_crop_bounds'][0] features['crop_right'] = features['_crop_bounds'][1] features['crop_top'] = features['_crop_bounds'][2] features['crop_bottom'] = features['_crop_bounds'][3] features['bl_cropped_u8'] = np.zeros_like( features['im_cropped_u8'], np.uint8) if return_uncropped or return_outline: left, right, top, bottom = features['_crop_bounds'] mask = np.ones_like(images['im_pl'], np.uint8) mask[top:bottom, left:right] = 0 if abs(features['crop_rotation']) > 0.01: h, w = mask.shape rot_mat = cv2.getRotationMatrix2D( (w // 2, h // 2), features['crop_rotation'] * -1, 1.0) mask = cv2.warpAffine(mask, rot_mat, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE ) # .astype(np.uint8) if return_uncropped: features['bl_uncropped_u8'] = mask elif mode in [70, 71]: print(' Mode: Slugs') im = images['im_pl'].astype(np.float32) if 'input_param_rds_percent' not in features: features['param_rds_percent'] = 50 else: features['param_rds_percent'] = int( features['input_param_rds_percent']) if 'param_radius_prior' not in features: features['param_radius_prior'] = 0 else: features['param_radius_prior'] = int( features['input_param_slug_radius']) slugs.feature_extraction(im, features) update_corner_features(features, features) features['im_cropped_u8'] = (ip.scale_image(images['im_pl']) * 255).astype(np.uint8) features['im_cropped_u16'] = images['im_pl'].astype(np.uint16) mask = features['bl_uncropped_u8'] if not return_uncropped: del features['bl_uncropped_u8'] elif mode in [84, 85, 89]: if mode == 84: print(' Mode: Mono wafer') im = images['im_pl'].astype(np.float32) features['_alg_mode'] = 'mono wafer' crop_props = cropping.crop_wafer_cz( im, create_mask=True, skip_crop=already_cropped) features['corners'] = crop_props['corners'] features['_wafer_middle_orig'] = crop_props['center'] cropped = cropping.correct_rotation( im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE_CZ, fix_chamfer=False) cz_wafer.feature_extraction(cropped, crop_props, features=features) update_corner_features(features, crop_props) elif mode == 85: print(' Mode: Multi wafer') im = images['im_pl'].astype(np.float32) features['_alg_mode'] = 'multi wafer' if not already_cropped: crop_props = cropping.crop_wafer(im, create_mask=True) features['corners'] = crop_props['corners'] cropped = cropping.correct_rotation( im, crop_props, pad=False, border_erode=parameters.BORDER_ERODE) else: crop_props = {} crop_props['estimated_width'] = im.shape[0] crop_props['center'] = (im.shape[0] / 2, im.shape[1] / 2) crop_props['corners'] = [ [0, 0], [0, im.shape[1]], [im.shape[0], im.shape[1]], [im.shape[0], 0], ] crop_props['corners_floats'] = crop_props['corners'] crop_props['estimated_rotation'] = 0 crop_props['mask'] = np.ones_like(im, np.uint8) cropped = im multi_wafer.feature_extraction(cropped, crop_props, features=features) multi_wafer.combined_features(features) update_corner_features(features, crop_props) elif mode == 89: print(' Mode: QC-C3') features['_alg_mode'] = 'qc' im = images['im_pl'].astype(np.float32) crop_props = qc.feature_extraction(im, features) if return_uncropped: features['bl_uncropped_u8'] = crop_props['mask'] elif mode in [80, 81, 82, 83, 86, 87, 88]: if mode == 80: print(' Mode: PERC mono') im = images['im_pl'].astype(np.float32) features['_alg_mode'] = 'perc mono' perc.feature_extraction(im, features, already_cropped=already_cropped) elif mode == 81: print(' Mode: PERC multi') im = images['im_pl'].astype(np.float32) features['_alg_mode'] = 'perc multi' perc.feature_extraction_multi( im, features, already_cropped=already_cropped) elif mode == 82: print(' Mode: Mono cells') im = images['im_pl'].astype(np.float32) features['_alg_mode'] = 'mono cell' cz_cell.feature_extraction(im, features, skip_crop=already_cropped) elif mode == 83: print(' Mode: Multi cells') im = images['im_pl'].astype(np.float32) features['_alg_mode'] = 'multi cell' multi_cell.feature_extraction( im, features, already_cropped=already_cropped) elif mode == 86: print(' Mode: X3') features['_alg_mode'] = 'x3' im = images['im_pl'].astype(np.float32) x3.feature_extraction(im, features, already_cropped=already_cropped) elif mode == 87: print(' Mode: Stripe (mono)') features['_alg_mode'] = 'stripe' features['_cell_type'] = 'mono' im = images['im_pl'].astype(np.float32) stripe.feature_extraction(im, features, skip_crop=already_cropped) elif mode == 88: print(' Mode: Stripe (multi)') features['_alg_mode'] = 'stripe' features['_cell_type'] = 'multi' im = images['im_pl'].astype(np.float32) stripe.feature_extraction(im, features, skip_crop=already_cropped) update_corner_features(features, features) if return_uncropped: mask = features['bl_cropped_u8'] im_h, im_w = im.shape if 'cell_rotated' in features and features['cell_rotated']: if parameters.ORIGINAL_ORIENTATION: mask = mask[:, ::-1].T im_h = im.shape[1] im_w = im.shape[0] # undo rotation and cropping mask = np.pad(mask, ((features['crop_top'], im_h - features['crop_bottom']), (features['crop_left'], im_w - features['crop_right'])), mode='constant', constant_values=((1, 1), (1, 1))) # created rotated version of full image mask_rotated = np.empty(im.shape, np.float32) h, w = mask.shape if 'cell_rotated' not in features or not features[ 'cell_rotated']: rot_mat = cv2.getRotationMatrix2D( (w // 2, h // 2), -features['crop_rotation'], 1.0) else: rot_mat = cv2.getRotationMatrix2D( (h // 2, h // 2), -features['crop_rotation'], 1.0) cv2.warpAffine(mask.astype(np.float32), rot_mat, (im.shape[1], im.shape[0]), flags=cv2.INTER_NEAREST, borderMode=cv2.BORDER_CONSTANT, dst=mask_rotated, borderValue=1) #print mask.shape, im.shape assert mask_rotated.shape == im.shape features['bl_uncropped_u8'] = np.round( mask_rotated).astype(np.uint8) elif mode == 90: print(' Mode: plir') im_sp = images['im_sp'].astype(np.float32) im_lp = images['im_lp'].astype(np.float32) if 'im_xfer' not in images: print "ERROR: Transfer functions not found" self.send_data(struct.pack('=B', 6)) return spline_plir, spline_nf, spline_sp, spline_lp = block.interpolate_transfer( images['im_xfer']) if 'im_pl' in images: im_pl = images['im_pl'].astype(np.float32) plc_found = block.plir(im_sp, im_lp, im_pl, features, spline_plir, spline_nf) else: plc_found = block.plir2(im_sp, im_lp, features, spline_plir, spline_sp) if not plc_found: self.send_data(struct.pack('=B', 5)) return if return_uncropped or return_outline: left, right, top, bottom = features['_crop_bounds'] if 'im_pl' in images: left *= 2 right *= 2 top *= 2 bottom *= 2 mask = np.ones_like(images['im_pl'], np.uint8) else: mask = np.ones_like(images['im_sp'], np.uint8) mask[top:bottom, left:right] = 0 if abs(features['crop_rotation']) > 0.01: h, w = mask.shape rot_mat = cv2.getRotationMatrix2D( (w // 2, h // 2), features['crop_rotation'] * -1, 1.0) mask = cv2.warpAffine(mask, rot_mat, (w, h), flags=cv2.INTER_LINEAR, borderMode=cv2.BORDER_REPLICATE ) # .astype(np.uint8) if return_uncropped: features['bl_uncropped_u8'] = mask elif mode == 92: print(' Mode: Distance between brick markers') im = images['im_pl'].astype(np.float32) block.MarkerLineDist(im, features) elif mode == 95: print(' Mode: Pixels per mm') im = images['im_pl'].astype(np.float32) resolution.resolution(im, features) elif mode == 100: print(' Mode: M1') if 'im_el' in images: im_el = images['im_el'].astype(np.float32) else: im_el = None im_pl = images['im_pl'].astype(np.float32) m1.feature_extraction(im_pl, im_el, features) else: print("ERROR: Mode %d not supported" % mode) self.send_data(struct.pack('=B', 1)) return if not return_cropped: for im_name in [ 'im_cropped_u16', 'im_cropped_u8', 'bl_cropped_u8', "im_cropped_sp_u8", 'im_cropped_nf_u8', 'im_cropped_sp_u16', 'im_cropped_nf_u16', 'im_cropped_lp_u16' ]: if im_name in features: del features[im_name] if return_outline: if mode in [40, 70, 90]: binary_struct = ndimage.generate_binary_structure(2, 1) foreground = 1 - mask outline = foreground - ndimage.binary_erosion( foreground, binary_struct) features['bl_crop_outline_u8'] = outline.astype(np.uint8) else: features['bl_crop_outline_u8'] = cropping.draw_crop_box( im, features, mode="mask") except cropping.WaferMissingException: self.send_data(struct.pack('=B', 2)) return except cell.MissingBusbarsException: self.send_data(struct.pack('=B', 3)) return except cell.CellFingersException: self.send_data(struct.pack('=B', 4)) return except: traceback.print_exc(file=sys.stdout) self.send_data(struct.pack('=B', 1)) return # success msg = struct.pack('=B', 0) self.send_data(msg) # return images image_names = [] for f in features.keys(): if f.split('_')[-1] not in ['u8', 'u16', 'f32'] or f[0] == '_': continue if f[:3] not in ['bl_', 'mk_', 'im_', 'ov_']: print "ERROR: invalid image name: %s" % f image_names.append(f) image_names.sort() image_names_send = ','.join(image_names) self.send_data(struct.pack('I', len(image_names_send))) self.send_data(image_names_send) for im_name in image_names: fields = im_name.split('_') if fields[-1] == "u8": bit_depth = 8 elif fields[-1] == "u16": bit_depth = 16 elif fields[-1] == "f32": bit_depth = 32 # convert binary masks from 0,1 to 0,255 if fields[0] == 'mk' and bit_depth == 8: features[im_name] *= 255 if ('input_param_im_output_path' in features and len(features['input_param_im_output_path']) > 0 and bit_depth in [8, 16]): # send back as path. msg = struct.pack('=hhBB', 0, 0, 0, 1) if bit_depth == 8: ext = '.png' else: ext = '.tif' fn_out = os.path.join(features['input_param_im_output_path'], im_name + ext) ip.save_image(fn_out, features[im_name], scale=False) fn_len = len(fn_out) msg += struct.pack('=B', fn_len) msg += fn_out else: # image data height, width = features[im_name].shape binning = 1 msg = struct.pack('=hhBB', width, height, bit_depth, binning) if fields[-1] == "u8": png = ip.encode_png(features[im_name]) msg += struct.pack('=I', len(png)) msg += png elif fields[-1] in ["u16", "f32"]: msg += features[im_name].tostring() self.send_data(msg) # numerical features feature_names = [] feature_vals = [] for k in features.keys(): if (k in ['cropped', 'corners', 'filename', 'center'] or k.startswith("bl_") or k.startswith('_') or k.startswith("mask_") or k.startswith("mk_") or k.startswith("im_") or k.startswith("ov_")): continue if type(features[k]) is str: continue feature_names.append(k) feature_names.sort() for feature in feature_names: feature_vals.append(float(features[feature])) feature_names = ','.join(feature_names) feature_vals = np.array(feature_vals, np.float32) bytes_to_send = len(feature_names) self.send_data(struct.pack('=I', bytes_to_send)) self.send_data(feature_names) msg = feature_vals.ravel().tostring() self.send_data(msg) # string features feature_names = [] feature_vals = [] for k in features.keys(): if k.startswith('_'): continue if type(features[k]) is not str: continue feature_names.append(k) feature_names.sort() for feature in feature_names: feature_vals.append(features[feature]) feature_names = ','.join(feature_names) feature_vals = ','.join(feature_vals) bytes_to_send = len(feature_names) self.send_data(struct.pack('=I', bytes_to_send)) if bytes_to_send > 0: self.send_data(feature_names) bytes_to_send = len(feature_vals) self.send_data(struct.pack('=I', bytes_to_send)) if bytes_to_send > 0: self.send_data(feature_vals) return