Source code for openalea.phenomenal.segmentation.maize_stem_detection

# -*- python -*-
#
#       Copyright INRIA - CIRAD - INRA
#
#       Distributed under the Cecill-C License.
#       See accompanying file LICENSE.txt or copy at
#           http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.html
#
# ==============================================================================
from __future__ import print_function, absolute_import

import numpy
import scipy.interpolate
import scipy.spatial
import scipy.signal

from .peak_detection import peak_detection, smooth
from .plane_interception import (
    intercept_points_along_path_with_planes,
    intercept_points_along_polyline_with_ball,
    max_distance_in_points,
)
# ==============================================================================


[docs] def maize_stem_peak_detection(values, stop_index): if len(values) > 15: nodes_length_smooth2 = list(smooth(numpy.array(values), window_len=15)) _, min_peaks_smooth2 = peak_detection(nodes_length_smooth2, order=3) i_peaks = [i for i, v in min_peaks_smooth2 if i <= stop_index] if i_peaks: stop_index = max(i_peaks) _, min_peaks = peak_detection(values, order=3) min_peaks = [(i, v) for i, v in min_peaks if i <= stop_index] if len(min_peaks) <= 1: min_peaks = [(0, values[0]), (1, values[1])] + min_peaks min_peaks = list(set(min_peaks)) return min_peaks
[docs] def get_nodes_radius(center, points, radius): x, y, z = center result = numpy.sqrt( (points[:, 0] - x) ** 2 + (points[:, 1] - y) ** 2 + (points[:, 2] - z) ** 2 ) index = numpy.where(result <= numpy.array(radius)) result = set(map(tuple, list(points[index]))) return result
[docs] def stem_detection( stem_segment_voxel, stem_segment_path, voxels_size, graph, z_stem=None, distance_plane=1.0, ): # ========================================================================== arr_stem_segment_voxel = numpy.array(list(stem_segment_voxel)) arr_stem_segment_path = numpy.array(stem_segment_path) closest_nodes_planes, _ = intercept_points_along_path_with_planes( arr_stem_segment_voxel, arr_stem_segment_path, distance_from_plane=distance_plane * voxels_size, voxels_size=voxels_size, points_graph=graph, ) arr_closest_nodes_planes = [ numpy.array(list(nodes)) for nodes in closest_nodes_planes ] distances = [] for i in range(len(arr_closest_nodes_planes)): distance = max_distance_in_points(arr_closest_nodes_planes[i]) distances.append(float(distance)) ball_radius = min(max(max(distances) / 2, 25), 75) closest_nodes_ball = intercept_points_along_polyline_with_ball( arr_stem_segment_voxel, graph, arr_stem_segment_path, ball_radius=ball_radius ) if z_stem is None: nodes_length = list(map(float, map(len, closest_nodes_ball))) index_20_percent = int(float(len(nodes_length)) * 0.20) stop_index = nodes_length.index((max(nodes_length))) if stop_index <= index_20_percent: stop_index = len(nodes_length) nodes_length = list(map(float, map(len, arr_closest_nodes_planes))) min_peaks_stem = maize_stem_peak_detection(nodes_length, stop_index) else: list_z = [numpy.mean(plane[:, 2]) for plane in arr_closest_nodes_planes] stop_index = numpy.argmin(abs(numpy.array(list_z) - z_stem)) nodes_length = list(map(float, map(len, arr_closest_nodes_planes))) min_peaks_stem = maize_stem_peak_detection(nodes_length, stop_index) if stop_index not in numpy.array(min_peaks_stem)[:, 0]: min_peaks_stem += [(stop_index, 0.0)] window_length = max(4, len(nodes_length) // 8) window_length = window_length + 1 if window_length % 2 == 0 else window_length smooth_distances = scipy.signal.savgol_filter( numpy.array(distances, dtype=numpy.uint16), window_length=window_length, polyorder=2, ) # ========================================================================== stem_segment_centred_path = [ nodes.mean(axis=0) for nodes in arr_closest_nodes_planes ] stem_voxel = set() radius = {} stem_centred_path_min_peak = [] max_index_min_peak = 0 xx_yy_raw = [] for i, _ in min_peaks_stem: max_index_min_peak = max(max_index_min_peak, i) radius[i] = smooth_distances[i] / 2.0 xx_yy_raw.append((i, numpy.array(distances)[i] / 2.0)) stem_centred_path_min_peak.append((i, stem_segment_centred_path[i])) stem_voxel = stem_voxel.union(closest_nodes_planes[i]) # ========================================================================== if (0, stem_segment_centred_path[0]) not in stem_centred_path_min_peak: stem_centred_path_min_peak.append((0, stem_segment_centred_path[0])) stem_centred_path_min_peak.sort(key=lambda x: x[0]) stem_centred_path_min_peak = [v for i, v in stem_centred_path_min_peak] xx_yy_raw.sort(key=lambda x: x[0]) xx = numpy.array([x for x, y in xx_yy_raw]) yy_raw = numpy.array([y for x, y in xx_yy_raw]) radius_raw = numpy.poly1d( numpy.polyfit(xx, numpy.array(yy_raw), deg=min(len(min_peaks_stem) - 1, 5)) ) rad = numpy.array(distances)[: max_index_min_peak + 1] / 2.0 # ========================================================================== # Interpolate arr_stem_centred_path_min_peak = numpy.array(stem_centred_path_min_peak).transpose() arr_stem_centred_path_min_peak = numpy.unique( arr_stem_centred_path_min_peak, axis=1 ) # remove redundancies tck, _ = scipy.interpolate.splprep(arr_stem_centred_path_min_peak, k=1) xxx, yyy, zzz = scipy.interpolate.splev(numpy.linspace(0, 1, 500), tck) # ========================================================================== arr_stem_voxels = set() for nodes in closest_nodes_planes[: max_index_min_peak + 1]: arr_stem_voxels = arr_stem_voxels.union(set(nodes)) arr_stem_voxels = numpy.array(list(arr_stem_voxels)) # ========================================================================== real_path = [] for i in range(len(xxx)): r = radius_raw(i * len(rad) / 500.0) x, y, z = xxx[i], yyy[i], zzz[i] real_path.append((x, y, z)) result = get_nodes_radius((x, y, z), arr_stem_voxels, r) stem_voxel = stem_voxel.union(result) not_stem_voxel = stem_segment_voxel - stem_voxel stem_path = arr_stem_segment_path[: max_index_min_peak + 1] stem_top = set(closest_nodes_planes[max_index_min_peak]) return stem_voxel, not_stem_voxel, stem_path, stem_top