def prepare(rgb_img, gray_low, gray_high): logger.debug('received rbg image') gray_img = pcv.rgb2gray(rgb_img=rgb_img) logger.debug('converted rbg image to grayscale image') masked_img = cv2.inRange(gray_img, gray_low, gray_high) logger.debug('filtered grayscale image') fill_image = pcv.fill_holes(masked_img) logger.debug('filled holes in binary mask') logger.debug('returned binary image mask') return fill_image
def segmentation(imgW, imgNIR, shape): # VIS example from PlantCV with few modifications # Higher value = more strict selection s_threshold = 165 b_threshold = 200 # Read image img = imread(imgW) #img = cvtColor(img, COLOR_BGR2RGB) imgNIR = imread(imgNIR) #imgNIR = cvtColor(imgNIR, COLOR_BGR2RGB) #img, path, img_filename = pcv.readimage(filename=imgW, mode="native") #imgNIR, pathNIR, imgNIR_filename = pcv.readimage(filename=imgNIR, mode="native") # Convert RGB to HSV and extract the saturation channel s = pcv.rgb2gray_hsv(rgb_img=img, channel='s') # Threshold the saturation image s_thresh = pcv.threshold.binary(gray_img=s, threshold=s_threshold, max_value=255, object_type='light') # Median Blur s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5) s_cnt = pcv.median_blur(gray_img=s_thresh, ksize=5) # Convert RGB to LAB and extract the Blue channel b = pcv.rgb2gray_lab(rgb_img=img, channel='b') # Threshold the blue image ORIGINAL 160 b_thresh = pcv.threshold.binary(gray_img=b, threshold=b_threshold, max_value=255, object_type='light') b_cnt = pcv.threshold.binary(gray_img=b, threshold=b_threshold, max_value=255, object_type='light') # Join the thresholded saturation and blue-yellow images bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_cnt) # Apply Mask (for VIS images, mask_color=white) masked = pcv.apply_mask(img=img, mask=bs, mask_color='white') # Convert RGB to LAB and extract the Green-Magenta and Blue-Yellow channels masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a') masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b') # Threshold the green-magenta and blue images # 115 # 135 # 128 maskeda_thresh = pcv.threshold.binary(gray_img=masked_a, threshold=115, max_value=255, object_type='dark') maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a, threshold=135, max_value=255, object_type='light') maskedb_thresh = pcv.threshold.binary(gray_img=masked_b, threshold=128, max_value=255, object_type='light') # Join the thresholded saturation and blue-yellow images (OR) ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh) ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1) # Fill small objects ab_fill = pcv.fill(bin_img=ab, size=200) # Apply mask (for VIS images, mask_color=white) masked2 = pcv.apply_mask(img=masked, mask=ab_fill, mask_color='white') # Identify objects id_objects, obj_hierarchy = pcv.find_objects(img=masked2, mask=ab_fill) # Define ROI height = shape[0] width = shape[1] roi1, roi_hierarchy= pcv.roi.rectangle(img=masked2, x=0, y=0, h=height, w=width) # Decide which objects to keep roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects(img=img, roi_contour=roi1, roi_hierarchy=roi_hierarchy, object_contour=id_objects, obj_hierarchy=obj_hierarchy, roi_type='partial') # Object combine kept objects obj, mask = pcv.object_composition(img=img, contours=roi_objects, hierarchy=hierarchy3) # Filling holes in the mask, works great for alive plants, not so good for dead plants filled_mask = pcv.fill_holes(mask) final = pcv.apply_mask(img=imgNIR, mask=mask, mask_color='white') pcv.print_image(final, "./segment/segment-temp.png")
def main(): # Create input arguments object args = options() # Set debug mode pcv.params.debug = args.debug # Open a single image img, imgpath, imgname = pcv.readimage(filename=args.image) # Visualize colorspaces all_cs = pcv.visualize.colorspaces(rgb_img=img) # Extract the Blue-Yellow ("b") channel from the LAB colorspace gray_img = pcv.rgb2gray_lab(rgb_img=img, channel="b") # Plot a histogram of pixel values for the Blue-Yellow ("b") channel. hist_plot = pcv.visualize.histogram(gray_img=gray_img) # Apply a binary threshold to the Blue-Yellow ("b") grayscale image. thresh_img = pcv.threshold.binary(gray_img=gray_img, threshold=140, max_value=255, object_type="light") # Apply a dilation with a 5x5 kernel and 3 iterations dil_img = pcv.dilate(gray_img=thresh_img, ksize=5, i=3) # Fill in small holes in the leaves closed_img = pcv.fill_holes(bin_img=dil_img) # Erode the plant pixels using a 5x5 kernel and 3 iterations er_img = pcv.erode(gray_img=closed_img, ksize=5, i=3) # Apply a Gaussian blur with a 5 x 5 kernel. blur_img = pcv.gaussian_blur(img=er_img, ksize=(5, 5)) # Set pixel values less than 255 to 0 blur_img[np.where(blur_img < 255)] = 0 # Fill/remove objects less than 300 pixels in area cleaned = pcv.fill(bin_img=blur_img, size=300) # Create a circular ROI roi, roi_str = pcv.roi.circle(img=img, x=1725, y=1155, r=400) # Identify objects in the binary image cnts, cnts_str = pcv.find_objects(img=img, mask=cleaned) # Filter objects by region of interest plant_cnt, plant_str, plant_mask, plant_area = pcv.roi_objects( img=img, roi_contour=roi, roi_hierarchy=roi_str, object_contour=cnts, obj_hierarchy=cnts_str) # Combine objects into one plant, mask = pcv.object_composition(img=img, contours=plant_cnt, hierarchy=plant_str) # Measure size and shape properties shape_img = pcv.analyze_object(img=img, obj=plant, mask=mask) if args.writeimg: pcv.print_image(img=shape_img, filename=os.path.join(args.outdir, "shapes_" + imgname)) # Analyze color properties color_img = pcv.analyze_color(rgb_img=img, mask=mask, hist_plot_type="hsv") if args.writeimg: pcv.print_image(img=color_img, filename=os.path.join(args.outdir, "histogram_" + imgname)) # Save the measurements to a file pcv.print_results(filename=args.result)
def main(): # Get options args = options() # Set variables device = 0 pcv.params.debug = args.debug img_file = args.image # Read image img, path, filename = pcv.readimage(filename=img_file, mode='rgb') # Process saturation channel from HSV colour space s = pcv.rgb2gray_hsv(rgb_img=img, channel='s') lp_s = pcv.laplace_filter(s, 1, 1) shrp_s = pcv.image_subtract(s, lp_s) s_eq = pcv.hist_equalization(shrp_s) s_thresh = pcv.threshold.binary(gray_img=s_eq, threshold=215, max_value=255, object_type='light') s_mblur = pcv.median_blur(gray_img=s_thresh, ksize=5) # Process green-magenta channel from LAB colour space b = pcv.rgb2gray_lab(rgb_img=img, channel='a') b_lp = pcv.laplace_filter(b, 1, 1) b_shrp = pcv.image_subtract(b, b_lp) b_thresh = pcv.threshold.otsu(b_shrp, 255, object_type='dark') # Create and apply mask bs = pcv.logical_or(bin_img1=s_mblur, bin_img2=b_thresh) filled = pcv.fill_holes(bs) masked = pcv.apply_mask(img=img, mask=filled, mask_color='white') # Extract colour channels from masked image masked_a = pcv.rgb2gray_lab(rgb_img=masked, channel='a') masked_b = pcv.rgb2gray_lab(rgb_img=masked, channel='b') # Threshold the green-magenta and blue images maskeda_thresh = pcv.threshold.binary(gray_img=masked_a, threshold=115, max_value=255, object_type='dark') maskeda_thresh1 = pcv.threshold.binary(gray_img=masked_a, threshold=140, max_value=255, object_type='light') maskedb_thresh = pcv.threshold.binary(gray_img=masked_b, threshold=128, max_value=255, object_type='light') # Join the thresholded saturation and blue-yellow images (OR) ab1 = pcv.logical_or(bin_img1=maskeda_thresh, bin_img2=maskedb_thresh) ab = pcv.logical_or(bin_img1=maskeda_thresh1, bin_img2=ab1) # Produce and apply a mask opened_ab = pcv.opening(gray_img=ab) ab_fill = pcv.fill(bin_img=ab, size=200) closed_ab = pcv.closing(gray_img=ab_fill) masked2 = pcv.apply_mask(img=masked, mask=bs, mask_color='white') # Identify objects id_objects, obj_hierarchy = pcv.find_objects(img=masked2, mask=ab_fill) # Define region of interest (ROI) roi1, roi_hierarchy = pcv.roi.rectangle(img=masked2, x=250, y=100, h=200, w=200) # Decide what objects to keep roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects( img=img, roi_contour=roi1, roi_hierarchy=roi_hierarchy, object_contour=id_objects, obj_hierarchy=obj_hierarchy, roi_type='partial') # Object combine kept objects obj, mask = pcv.object_composition(img=img, contours=roi_objects, hierarchy=hierarchy3) ############### Analysis ################ outfile = False if args.writeimg == True: outfile = args.outdir + "/" + filename # Analyze the plant analysis_image = pcv.analyze_object(img=img, obj=obj, mask=mask) color_histogram = pcv.analyze_color(rgb_img=img, mask=kept_mask, hist_plot_type='all') top_x, bottom_x, center_v_x = pcv.x_axis_pseudolandmarks(img=img, obj=obj, mask=mask) top_y, bottom_y, center_v_y = pcv.y_axis_pseudolandmarks(img=img, obj=obj, mask=mask) # Print results of the analysis pcv.print_results(filename=args.result) pcv.output_mask(img, kept_mask, filename, outdir=args.outdir, mask_only=True)
def main(): # Initialize options args = options() # Set PlantCV debug mode to input debug method pcv.params.debug = args.debug # Use PlantCV to read in the input image. The function outputs an image as a NumPy array, the path to the file, # and the image filename img, path, filename = pcv.readimage(filename=args.image) # ## Segmentation # ### Saturation channel # Convert the RGB image to HSV colorspace and extract the saturation channel s = pcv.rgb2gray_hsv(rgb_img=img, channel='s') # Use a binary threshold to set an inflection value where all pixels in the grayscale saturation image below the # threshold get set to zero (pure black) and all pixels at or above the threshold get set to 255 (pure white) s_thresh = pcv.threshold.binary(gray_img=s, threshold=80, max_value=255, object_type='light') # ### Blue-yellow channel # Convert the RGB image to LAB colorspace and extract the blue-yellow channel b = pcv.rgb2gray_lab(rgb_img=img, channel='b') # Use a binary threshold to set an inflection value where all pixels in the grayscale blue-yellow image below the # threshold get set to zero (pure black) and all pixels at or above the threshold get set to 255 (pure white) b_thresh = pcv.threshold.binary(gray_img=b, threshold=134, max_value=255, object_type='light') # ### Green-magenta channel # Convert the RGB image to LAB colorspace and extract the green-magenta channel a = pcv.rgb2gray_lab(rgb_img=img, channel='a') # In the green-magenta image the plant pixels are darker than the background. Setting object_type="dark" will # invert the image first and then use a binary threshold to set an inflection value where all pixels in the # grayscale green-magenta image below the threshold get set to zero (pure black) and all pixels at or above the # threshold get set to 255 (pure white) a_thresh = pcv.threshold.binary(gray_img=a, threshold=122, max_value=255, object_type='dark') # Combine the binary images for the saturation and blue-yellow channels. The "or" operator returns a binary image # that is white when a pixel was white in either or both input images bs = pcv.logical_or(bin_img1=s_thresh, bin_img2=b_thresh) # Combine the binary images for the combined saturation and blue-yellow channels and the green-magenta channel. # The "or" operator returns a binary image that is white when a pixel was white in either or both input images bsa = pcv.logical_or(bin_img1=bs, bin_img2=a_thresh) # The combined binary image labels plant pixels well but the background still has pixels labeled as foreground. # Small white noise (salt) in the background can be removed by filtering white objects in the image by size and # setting a size threshold where smaller objects can be removed bsa_fill1 = pcv.fill(bin_img=bsa, size=15) # Fill small noise # Before more stringent size filtering is done we want to connect plant parts that may still be disconnected from # the main plant. Use a dilation to expand the boundary of white regions. Ksize is the size of a box scanned # across the image and i is the number of times a scan is done bsa_fill2 = pcv.dilate(gray_img=bsa_fill1, ksize=3, i=3) # Remove small objects by size again but use a higher threshold bsa_fill3 = pcv.fill(bin_img=bsa_fill2, size=250) # Use the binary image to identify objects or connected components. id_objects, obj_hierarchy = pcv.find_objects(img=img, mask=bsa_fill3) # Because the background still contains pixels labeled as foreground, the object list contains background. # Because these images were collected in an automated system the plant is always centered in the image at the # same position each time. Define a region of interest (ROI) to set the area where we expect to find plant # pixels. PlantCV can make simple ROI shapes like rectangles, circles, etc. but here we use a custom ROI to fit a # polygon around the plant area roi_custom, roi_hier_custom = pcv.roi.custom(img=img, vertices=[[1085, 1560], [1395, 1560], [1395, 1685], [1890, 1744], [1890, 25], [600, 25], [615, 1744], [1085, 1685]]) # Use the ROI to filter out objects found outside the ROI. When `roi_type = "cutto"` objects outside the ROI are # cropped out. The default `roi_type` is "partial" which allows objects to overlap the ROI and be retained roi_objects, hierarchy, kept_mask, obj_area = pcv.roi_objects(img=img, roi_contour=roi_custom, roi_hierarchy=roi_hier_custom, object_contour=id_objects, obj_hierarchy=obj_hierarchy, roi_type='cutto') # Filter remaining objects by size again to remove any remaining background objects filled_mask1 = pcv.fill(bin_img=kept_mask, size=350) # Use a closing operation to first dilate (expand) and then erode (shrink) the plant to fill in any additional # gaps in leaves or stems filled_mask2 = pcv.closing(gray_img=filled_mask1) # Remove holes or dark spot noise (pepper) in the plant binary image filled_mask3 = pcv.fill_holes(filled_mask2) # With the clean binary image identify the contour of the plant id_objects, obj_hierarchy = pcv.find_objects(img=img, mask=filled_mask3) # Because a plant or object of interest may be composed of multiple contours, it is required to combine all # remaining contours into a single contour before measurements can be done obj, mask = pcv.object_composition(img=img, contours=id_objects, hierarchy=obj_hierarchy) # ## Measurements PlantCV has several built-in measurement or analysis methods. Here, basic measurements of size # and shape are done. Additional typical modules would include plant height (`pcv.analyze_bound_horizontal`) and # color (`pcv.analyze_color`) shape_img = pcv.analyze_object(img=img, obj=obj, mask=mask) # Save the shape image if requested if args.writeimg: outfile = os.path.join(args.outdir, filename[:-4] + "_shapes.png") pcv.print_image(img=shape_img, filename=outfile) # ## Morphology workflow # Update a few PlantCV parameters for plotting purposes pcv.params.text_size = 1.5 pcv.params.text_thickness = 5 pcv.params.line_thickness = 15 # Convert the plant mask into a "skeletonized" image where each path along the stem and leaves are a single pixel # wide skel = pcv.morphology.skeletonize(mask=mask) # Sometimes wide parts of leaves or stems are skeletonized in the direction perpendicular to the main path. These # "barbs" or "spurs" can be removed by pruning the skeleton to remove small paths. Pruning will also separate the # individual path segments (leaves and stem parts) pruned, segmented_img, segment_objects = pcv.morphology.prune(skel_img=skel, size=30, mask=mask) pruned, segmented_img, segment_objects = pcv.morphology.prune(skel_img=pruned, size=3, mask=mask) # Leaf and stem segments above are separated but only into individual paths. We can sort the segments into stem # and leaf paths by identifying primary segments (stems; those that end in a branch point) and secondary segments # (leaves; those that begin at a branch point and end at a tip point) leaf_objects, other_objects = pcv.morphology.segment_sort(skel_img=pruned, objects=segment_objects, mask=mask) # Label the segment unique IDs segmented_img, labeled_id_img = pcv.morphology.segment_id(skel_img=pruned, objects=leaf_objects, mask=mask) # Measure leaf insertion angles. Measures the angle between a line fit through the stem paths and a line fit # through the first `size` points of each leaf path labeled_angle_img = pcv.morphology.segment_insertion_angle(skel_img=pruned, segmented_img=segmented_img, leaf_objects=leaf_objects, stem_objects=other_objects, size=22) # Save leaf angle image if requested if args.writeimg: outfile = os.path.join(args.outdir, filename[:-4] + "_leaf_insertion_angles.png") pcv.print_image(img=labeled_angle_img, filename=outfile) # ## Other potential morphological measurements There are many other functions that extract data from within the # morphology sub-package of PlantCV. For our purposes, we are most interested in the relative angle between each # leaf and the stem which we measure with `plantcv.morphology.segment_insertion_angle`. However, the following # cells show some of the other traits that we are able to measure from images that can be succesfully sorted into # primary and secondary segments. # Segment the plant binary mask using the leaf and stem segments. Allows for the measurement of individual leaf # areas # filled_img = pcv.morphology.fill_segments(mask=mask, objects=leaf_objects) # Measure the path length of each leaf (geodesic distance) # labeled_img2 = pcv.morphology.segment_path_length(segmented_img=segmented_img, objects=leaf_objects) # Measure the straight-line, branch point to tip distance (Euclidean) for each leaf # labeled_img3 = pcv.morphology.segment_euclidean_length(segmented_img=segmented_img, objects=leaf_objects) # Measure the curvature of each leaf (Values closer to 1 indicate that a segment is a straight line while larger # values indicate the segment has more curvature) # labeled_img4 = pcv.morphology.segment_curvature(segmented_img=segmented_img, objects=leaf_objects) # Measure absolute leaf angles (angle of linear regression line fit to each leaf object) Note: negative values # signify leaves to the left of the stem, positive values signify leaves to the right of the stem # labeled_img5 = pcv.morphology.segment_angle(segmented_img=segmented_img, objects=leaf_objects) # Measure leaf curvature in degrees # labeled_img6 = pcv.morphology.segment_tangent_angle(segmented_img=segmented_img, objects=leaf_objects, size=35) # Measure stem characteristics like stem angle and length # stem_img = pcv.morphology.analyze_stem(rgb_img=img, stem_objects=other_objects) # Remove unneeded observations (hack) _ = pcv.outputs.observations.pop("tips") _ = pcv.outputs.observations.pop("branch_pts") angles = pcv.outputs.observations["segment_insertion_angle"]["value"] remove_indices = [] for i, value in enumerate(angles): if value == "NA": remove_indices.append(i) remove_indices.sort(reverse=True) for i in remove_indices: _ = pcv.outputs.observations["segment_insertion_angle"]["value"].pop(i) # ## Save the results out to file for downsteam analysis pcv.print_results(filename=args.result)
def initcrop(imagePath): dire = dir path = dire + '/Classifyer_dump' try: os.makedirs(path) except OSError: pass image = cv2.imread(imagePath) blue_image = pcv.rgb2gray_lab(image, 'l') Gaussian_blue = cv2.adaptiveThreshold(blue_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 981, -1) # 241 is good 981 cv2.imwrite(os.path.join(path, "blue_test.png"), Gaussian_blue) fill = pcv.fill_holes(Gaussian_blue) fill_again = pcv.fill(fill, 100000) id_objects, obj_hierarchy = pcv.find_objects( img=image, mask=fill_again) # lazy way to findContours and draw them roi1, roi_hierarchy = pcv.roi.rectangle(img=image, x=3000, y=1000, h=200, w=300) roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects( img=image, roi_contour=roi1, roi_hierarchy=roi_hierarchy, object_contour=id_objects, obj_hierarchy=obj_hierarchy, roi_type='partial') cv2.imwrite(os.path.join(path, "plate_mask.png"), kept_mask) mask = cv2.imread(os.path.join(path, "plate_mask.png")) result = image * (mask.astype(image.dtype)) result = cv2.bitwise_not(result) cv2.imwrite(os.path.join(path, "AutoCrop.png"), result) output = cv2.connectedComponentsWithStats(kept_mask, connectivity=8) stats = output[2] left = (stats[1, cv2.CC_STAT_LEFT]) # print(stats[1, cv2.CC_STAT_TOP]) # print(stats[1, cv2.CC_STAT_HEIGHT]) # exit(2) L, a, b = cv2.split(result) # cv2.imwrite("gray_scale.png", L) plate_threshold = cv2.adaptiveThreshold(b, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 87, -1) # 867 is good 241 cv2.imwrite(os.path.join(path, "plate_threshold.png"), plate_threshold) fill_again2 = pcv.fill(plate_threshold, 1000) cv2.imwrite(os.path.join(path, "fill_test.png"), fill_again2) # fill = pcv.fill_holes(fill_again2) # cv2.imwrite(os.path.join(path, "fill_test2.png"), fill) blur_image = pcv.median_blur(fill_again2, 10) nb_components, output, stats, centroids = cv2.connectedComponentsWithStats( blur_image, connectivity=8) sizes = stats[1:, -1] nb_components = nb_components - 1 min_size = 20000 img2 = np.zeros((output.shape)) for i in range(0, nb_components): if sizes[i] <= min_size: img2[output == i + 1] = 255 cv2.imwrite(os.path.join(path, "remove_20000.png"), img2) # this can be made better to speed it up thresh_image = img2.astype( np.uint8) # maybe crop to the roi below then do it thresh_image = pcv.fill_holes(thresh_image) cv2.imwrite("NEWTEST.jpg", thresh_image) id_objects, obj_hierarchy = pcv.find_objects(img=image, mask=thresh_image) roi1, roi_hierarchy = pcv.roi.rectangle(img=image, x=(left + 380), y=750, h=175, w=100) try: where_cell = 0 roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects( img=image, roi_contour=roi1, roi_hierarchy=roi_hierarchy, object_contour=id_objects, obj_hierarchy=obj_hierarchy, roi_type='partial') cv2.imwrite(os.path.join(path, "test_mask.png"), kept_mask) mask = cv2.imread(os.path.join(path, "test_mask.png")) result = image * (mask.astype(image.dtype)) result = cv2.bitwise_not(result) cv2.imwrite(os.path.join(path, "TEST.png"), result) output = cv2.connectedComponentsWithStats(kept_mask, connectivity=8) stats = output[2] centroids = output[3] centroids_x = (int(centroids[1][0])) centroids_y = (int(centroids[1][1])) except: where_cell = 1 print("did this work?") roi1, roi_hierarchy = pcv.roi.rectangle(img=image, x=(left + 380), y=3200, h=100, w=100) roi_objects, hierarchy3, kept_mask, obj_area = pcv.roi_objects( img=image, roi_contour=roi1, roi_hierarchy=roi_hierarchy, object_contour=id_objects, obj_hierarchy=obj_hierarchy, roi_type='partial') cv2.imwrite(os.path.join(path, "test_mask.png"), kept_mask) mask = cv2.imread(os.path.join(path, "test_mask.png")) result = image * (mask.astype(image.dtype)) result = cv2.bitwise_not(result) cv2.imwrite(os.path.join(path, "TEST.png"), result) output = cv2.connectedComponentsWithStats(kept_mask, connectivity=8) stats = output[2] centroids = output[3] centroids_x = (int(centroids[1][0])) centroids_y = (int(centroids[1][1])) flag = 0 # print(stats[1, cv2.CC_STAT_AREA]) if ((stats[1, cv2.CC_STAT_AREA]) > 4000): flag = 30 # print(centroids_x) # print(centroids_y) # print(centroids) if (where_cell == 0): left = (centroids_x - 70) right = (centroids_x + 3695 + flag) # was 3715 top = (centroids_y - 80) bottom = (centroids_y + 2462) if (where_cell == 1): left = (centroids_x - 70) right = (centroids_x + 3715 + flag) top = (centroids_y - 2480) bottom = (centroids_y + 62) # print(top) # print(bottom) image = Image.open(imagePath) img_crop = image.crop((left, top, right, bottom)) # img_crop.show() img_crop.save(os.path.join(path, 'Cropped_full_yeast.png')) circle_me = cv2.imread(os.path.join(path, "Cropped_full_yeast.png")) cropped_img = cv2.imread( os.path.join(path, "Cropped_full_yeast.png" )) # changed from Yeast_Cluster.%d.png %counter L, a, b = cv2.split(cropped_img) # can do l a or b Gaussian_blue = cv2.adaptiveThreshold(b, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 241, -1) # For liz's pictures 241 cv2.imwrite(os.path.join(path, "blue_test.png"), Gaussian_blue) blur_image = pcv.median_blur(Gaussian_blue, 10) heavy_fill_blue = pcv.fill(blur_image, 1000) # value 400 hole_fill = pcv.fill_holes(heavy_fill_blue) cv2.imwrite(os.path.join(path, "Cropped_Threshold.png"), hole_fill)
def initcrop(imagePath): dire = os.getcwd() path = dire + '/Classifyer_dump' try: os.makedirs(path) except OSError: pass image = cv2.imread(imagePath) gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) cv2.imwrite(os.path.join(path, "gray.png"), gray) ret, thres = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY) # 127 137 cv2.imwrite(os.path.join(path, 'Wholepic.png'), thres) fill = pcv.fill(thres, 100000) cv2.imwrite(os.path.join(path, "noiseReduced.png"), fill) im2, contours, hierarchy = cv2.findContours(fill, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) test = [] for c in contours: peri = cv2.arcLength(c, True) test.append(peri) index_max = np.argmax(test) x, y, w, h = cv2.boundingRect(contours[index_max]) c = max(contours, key=cv2.contourArea) x, y, w, h = cv2.boundingRect(c) # draw the biggest contour (c) in green # cv2.rectangle(image,(x,y),(x+w,y+h),(0,255,0),5) image = image[y:y + h, x:x + w] cv2.imwrite(os.path.join(path, "cropped.png"), image) L, a, b = cv2.split(image) # can do l a or b thres = cv2.adaptiveThreshold(b, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 255, -1) # For liz's pictures 241 cv2.imwrite(os.path.join(path, "cropped_thres.png"), thres) fill = pcv.fill(thres, 1000) cv2.imwrite(os.path.join(path, "cropped_thres_filled.png"), fill) blur = cv2.blur(fill, (15, 15), 0) fill_hole = cv2.morphologyEx(fill, cv2.MORPH_CLOSE, cv2.getStructuringElement( cv2.MORPH_RECT, (21, 21)), iterations=2) cv2.imwrite(os.path.join(path, "cropped_thres_filled.png"), fill_hole) nb_components, output, stats, centroids = cv2.connectedComponentsWithStats( fill_hole, connectivity=8) new_centroids = [] sizes = stats[1:, -1] nb_components = nb_components - 1 min_size = 20000 img2 = np.zeros((output.shape)) for i in range(0, nb_components): if sizes[i] <= min_size: img2[output == i + 1] = 255 new_centroids.append(centroids[i]) cv2.imwrite(os.path.join(path, "filter.png"), img2) # print(len(centroids)) # print(len(new_centroids)) # print(new_centroids[0]) # print(new_centroids[0][1]) y = [] x = [] for i in range(len(new_centroids)): y.append(new_centroids[i][1]) x.append(new_centroids[i][0]) # print(len(x)) XARRAY = sorted(x) YARRAY = sorted(y) smallX = int(XARRAY[4]) - 80 bigX = int(XARRAY[len(XARRAY) - 4]) + 80 smallY = int(YARRAY[4]) - 80 bigY = int(YARRAY[len(YARRAY) - 4]) + 80 # print(smallX, smallY, bigX, bigY) image = cv2.imread(os.path.join(path, "cropped.png")) image = image[smallY:bigY, smallX:bigX] #print(image.shape) cv2.imwrite(os.path.join(path, "final.png"), image) L, a, b = cv2.split(image) # can do l a or b blur_image = pcv.median_blur(a, 10) Gaussian_blue = cv2.adaptiveThreshold(a, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 281, -1) # For liz's pictures 241 heavy_fill_blue = pcv.fill(Gaussian_blue, 1000) # value 400 hole_fill = pcv.fill_holes(heavy_fill_blue) cv2.imwrite(os.path.join(path, "Cropped_Threshold.png"), hole_fill) # cv2.imwrite(os.path.join(test1, "Cropped_thres%d.png" % image_counter),hole_fill) image = cv2.resize(image, (800, 600)) cv2.imwrite(os.path.join(path, "scaled.png"), image) image = os.path.join(path, "scaled.png") msg = "Does this crop look good" choices = ["Yes", "No"] reply = easygui.indexbox(msg, image=image, choices=choices) if reply == 0: df = pd.DataFrame([smallY, bigY, smallX, bigX]) df.to_pickle('pickled_setup')
def main(): # Get options args = options() if args.debug: pcv.params.debug = args.debug # set debug mode if args.debugdir: pcv.params.debug_outdir = args.debugdir # set debug directory os.makedirs(args.debugdir, exist_ok=True) # pixel_resolution # mm # see pixel_resolution.xlsx for calibration curve for pixel to mm translation pixelresolution = 0.052 # The result file should exist if plantcv-workflow.py was run if os.path.exists(args.result): # Open the result file results = open(args.result, "r") # The result file would have image metadata in it from plantcv-workflow.py, read it into memory metadata = json.load(results) # Close the file results.close() # Delete the file, we will create new ones os.remove(args.result) plantbarcode = metadata['metadata']['plantbarcode']['value'] print(plantbarcode, metadata['metadata']['timestamp']['value'], sep=' - ') else: # If the file did not exist (for testing), initialize metadata as an empty string metadata = "{}" regpat = re.compile(args.regex) plantbarcode = re.search(regpat, args.image).groups()[0] # read images and create mask img, _, fn = pcv.readimage(args.image) imagename = os.path.splitext(fn)[0] # create mask # taf=filters.try_all_threshold(s_img) ## remove background s_img = pcv.rgb2gray_hsv(img, 's') min_s = filters.threshold_minimum(s_img) thresh_s = pcv.threshold.binary(s_img, min_s, 255, 'light') rm_bkgrd = pcv.fill_holes(thresh_s) ## low greenness thresh_s = pcv.threshold.binary(s_img, min_s + 15, 255, 'dark') # taf = filters.try_all_threshold(s_img) c = pcv.logical_xor(rm_bkgrd, thresh_s) cinv = pcv.invert(c) cinv_f = pcv.fill(cinv, 500) cinv_f_c = pcv.closing(cinv_f, np.ones((5, 5))) cinv_f_c_e = pcv.erode(cinv_f_c, 2, 1) ## high greenness a_img = pcv.rgb2gray_lab(img, channel='a') # taf = filters.try_all_threshold(a_img) t_a = filters.threshold_isodata(a_img) thresh_a = pcv.threshold.binary(a_img, t_a, 255, 'dark') thresh_a = pcv.closing(thresh_a, np.ones((5, 5))) thresh_a_f = pcv.fill(thresh_a, 500) ## combined mask lor = pcv.logical_or(cinv_f_c_e, thresh_a_f) close = pcv.closing(lor, np.ones((2, 2))) fill = pcv.fill(close, 800) erode = pcv.erode(fill, 3, 1) fill2 = pcv.fill(erode, 1200) # dilate = pcv.dilate(fill2,2,2) mask = fill2 final_mask = np.zeros_like(mask) # Compute greenness # split color channels b, g, r = cv2.split(img) # print green intensity # g_img = pcv.visualize.pseudocolor(g, cmap='Greens', background='white', min_value=0, max_value=255, mask=mask, axes=False) # convert color channels to int16 so we can add them (values will be greater than 255 which is max of current uint8 format) g = g.astype('uint16') r = r.astype('uint16') b = b.astype('uint16') denom = g + r + b # greenness index out_flt = np.zeros_like(denom, dtype='float32') # divide green by sum of channels to compute greenness index with values 0-1 gi = np.divide(g, denom, out=out_flt, where=np.logical_and(denom != 0, mask > 0)) # find objects c, h = pcv.find_objects(img, mask) rc, rh = pcv.roi.multi(img, coord=[(1300, 900), (1300, 2400)], radius=350) # Turn off debug temporarily, otherwise there will be a lot of plots pcv.params.debug = None # Loop over each region of interest i = 0 rc_i = rc[i] for i, rc_i in enumerate(rc): rh_i = rh[i] # Add ROI number to output. Before roi_objects so result has NA if no object. pcv.outputs.add_observation(variable='roi', trait='roi', method='roi', scale='int', datatype=int, value=i, label='#') roi_obj, hierarchy_obj, submask, obj_area = pcv.roi_objects( img, roi_contour=rc_i, roi_hierarchy=rh_i, object_contour=c, obj_hierarchy=h, roi_type='partial') if obj_area == 0: print('\t!!! No object found in ROI', str(i)) pcv.outputs.add_observation( variable='plantarea', trait='plant area in sq mm', method='observations.area*pixelresolution^2', scale=pixelresolution, datatype="<class 'float'>", value=0, label='sq mm') else: # Combine multiple objects # ple plant objects within an roi together plant_object, plant_mask = pcv.object_composition( img=img, contours=roi_obj, hierarchy=hierarchy_obj) final_mask = pcv.image_add(final_mask, plant_mask) # Save greenness for individual ROI grnindex = np.mean(gi[np.where(plant_mask > 0)]) pcv.outputs.add_observation( variable='greenness_index', trait='mean normalized greenness index', method='g/sum(b+g+r)', scale='[0,1]', datatype="<class 'float'>", value=float(grnindex), label='/1') # Analyze all colors hist = pcv.analyze_color(img, plant_mask, 'all') # Analyze the shape of the current plant shape_img = pcv.analyze_object(img, plant_object, plant_mask) plant_area = pcv.outputs.observations['area'][ 'value'] * pixelresolution**2 pcv.outputs.add_observation( variable='plantarea', trait='plant area in sq mm', method='observations.area*pixelresolution^2', scale=pixelresolution, datatype="<class 'float'>", value=plant_area, label='sq mm') # end if-else # At this point we have observations for one plant # We can write these out to a unique results file # Here I will name the results file with the ROI ID combined with the original result filename basename, ext = os.path.splitext(args.result) filename = basename + "-roi" + str(i) + ext # Save the existing metadata to the new file with open(filename, "w") as r: json.dump(metadata, r) pcv.print_results(filename=filename) # The results are saved, now clear out the observations so the next loop adds new ones for the next plant pcv.outputs.clear() if args.writeimg and obj_area != 0: imgdir = os.path.join(args.outdir, 'shape_images', plantbarcode) os.makedirs(imgdir, exist_ok=True) pcv.print_image( shape_img, os.path.join(imgdir, imagename + '-roi' + str(i) + '-shape.png')) imgdir = os.path.join(args.outdir, 'colorhist_images', plantbarcode) os.makedirs(imgdir, exist_ok=True) pcv.print_image( hist, os.path.join(imgdir, imagename + '-roi' + str(i) + '-colorhist.png')) # end roi loop if args.writeimg: # save grnness image of entire tray imgdir = os.path.join(args.outdir, 'pseudocolor_images', plantbarcode) os.makedirs(imgdir, exist_ok=True) gi_img = pcv.visualize.pseudocolor(gi, obj=None, mask=final_mask, cmap='viridis', axes=False, min_value=0.3, max_value=0.6, background='black', obj_padding=0) gi_img = add_scalebar(gi_img, pixelresolution=pixelresolution, barwidth=20, barlocation='lower left') gi_img.set_size_inches(6, 6, forward=False) gi_img.savefig(os.path.join(imgdir, imagename + '-greenness.png'), bbox_inches='tight') gi_img.clf()