def to_svg(self, open_inkscape=False, filename=None): """Generate SVG file from vertex ROI masks. """ # Generate temp filename if not provided if filename is None: filename = tempfile.mktemp(suffix=".svg", prefix=self.subject + "-rois-") mpts, mpolys = surfs.getSurf(self.subject, "flat", merge=True, nudge=True) svgmpts = mpts[:, :2].copy() svgmpts -= svgmpts.min(0) svgmpts *= 1024 / svgmpts.max(0)[1] svgmpts[:, 1] = 1024 - svgmpts[:, 1] npts = len(mpts) svgroipack = get_roipack(filename, mpts, mpolys) # Add default layers # Add curvature from matplotlib import cm curv = VertexData(np.hstack(get_curvature(self.subject)), self.subject) fp = cStringIO.StringIO() curvim = quickflat.make_png(fp, curv, height=1024, with_rois=False, with_labels=False, with_colorbar=False, cmap=cm.gray, recache=True) fp.seek(0) svgroipack.add_roi("curvature", binascii.b2a_base64(fp.read()), add_path=False) # Add thickness # Add ROI boundaries svg = etree.parse(svgroipack.svgfile, parser=parser) # Find boundary vertices for each ROI lsurf, rsurf = [ Surface(*pp) for pp in surfs.getSurf(self.subject, "fiducial") ] flsurf, frsurf = [ Surface(*pp) for pp in surfs.getSurf(self.subject, "flat") ] valids = [set(np.unique(flsurf.polys)), set(np.unique(frsurf.polys))] # Construct polygon adjacency graph for each surface polygraphs = [lsurf.poly_graph, rsurf.poly_graph] for roi in self.rois.keys(): print("Adding %s.." % roi) masks = self.rois[roi].left, self.rois[roi].right mmpts = svgmpts[:len(masks[0])], svgmpts[len(masks[0]):] roilayer = _make_layer(_find_layer(svg, "rois"), roi) for valid, pgraph, surf, mask, mmp in zip(valids, polygraphs, [lsurf, rsurf], masks, mmpts): if mask.sum() == 0: continue # Find bounds inbound, exbound = surf.get_boundary(np.nonzero(mask)[0]) # Find polys allbpolys = np.unique(surf.connected[inbound + exbound].indices) selbpolys = surf.polys[allbpolys] inpolys = np.in1d(selbpolys, inbound).reshape(selbpolys.shape) expolys = np.in1d(selbpolys, exbound).reshape(selbpolys.shape) badpolys = np.logical_or(inpolys.all(1), expolys.all(1)) boundpolys = np.logical_and( np.logical_or(inpolys, expolys).all(1), ~badpolys) # Walk around boundary boundpolyinds = set(allbpolys[np.nonzero(boundpolys)[0]]) bgraph = nx.Graph() pos = dict() for pa in boundpolyinds: for pb in set(pgraph[pa]) & boundpolyinds: edge = pgraph[pa][pb]["verts"] validverts = list(valid & edge) pos[edge] = mmp[validverts].mean(0) bgraph.add_edge(*edge) cc = nx.cycles.cycle_basis(bgraph) if len(cc) > 1: edges = reduce(set.symmetric_difference, [ set( map(lambda l: tuple(sorted(l)), zip(c, c[1:] + [c[0]]))) for c in cc ]) eg = nx.from_edgelist(edges) cycles = nx.cycles.cycle_basis(eg) longest = np.argmax(map(len, cycles)) path_order = cycles[longest] else: path_order = cc[0] path_points = [ tuple(pos[frozenset(p)]) for p in zip(path_order[:-1], path_order[1:]) ] # Store poly path = "M %f %f L" % tuple(path_points[0]) path += ", ".join(["%f %f" % p for p in path_points[1:]]) path += "Z " # Insert into SVG svgpath = etree.SubElement(roilayer, "path") svgpath.attrib[ "style"] = "fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opactiy:1" svgpath.attrib["d"] = path #svgpath.attrib["sodipodi:nodetypes"] = "c" * len(pts) with open(svgroipack.svgfile, "w") as xml: xml.write(etree.tostring(svg, pretty_print=True))
def to_svg(self, open_inkscape=False, filename=None): """Generate SVG file from vertex ROI masks. """ # Generate temp filename if not provided if filename is None: filename = tempfile.mktemp(suffix=".svg", prefix=self.subject+"-rois-") mpts, mpolys = surfs.getSurf(self.subject, "flat", merge=True, nudge=True) svgmpts = mpts[:,:2].copy() svgmpts -= svgmpts.min(0) svgmpts *= 1024 / svgmpts.max(0)[1] svgmpts[:,1] = 1024 - svgmpts[:,1] npts = len(mpts) svgroipack = get_roipack(filename, mpts, mpolys) # Add default layers # Add curvature from matplotlib import cm curv = VertexData(np.hstack(get_curvature(self.subject)), self.subject) fp = io.BytesIO() curvim = quickflat.make_png(fp, curv, height=1024, with_rois=False, with_labels=False, with_colorbar=False, cmap=cm.gray,recache=True) fp.seek(0) svgroipack.add_roi("curvature", binascii.b2a_base64(fp.read()), add_path=False) # Add thickness # Add ROI boundaries svg = etree.parse(svgroipack.svgfile, parser=parser) # Find boundary vertices for each ROI lsurf, rsurf = [Surface(*pp) for pp in surfs.getSurf(self.subject, "fiducial")] flsurf, frsurf = [Surface(*pp) for pp in surfs.getSurf(self.subject, "flat")] valids = [set(np.unique(flsurf.polys)), set(np.unique(frsurf.polys))] # Construct polygon adjacency graph for each surface polygraphs = [lsurf.poly_graph, rsurf.poly_graph] for roi in self.rois.keys(): print("Adding %s.." % roi) masks = self.rois[roi].left, self.rois[roi].right mmpts = svgmpts[:len(masks[0])], svgmpts[len(masks[0]):] roilayer = _make_layer(_find_layer(svg, "rois"), roi) for valid, pgraph, surf, mask, mmp in zip(valids, polygraphs, [lsurf, rsurf], masks, mmpts): if mask.sum() == 0: continue # Find bounds inbound, exbound = surf.get_boundary(np.nonzero(mask)[0]) # Find polys allbpolys = np.unique(surf.connected[inbound+exbound].indices) selbpolys = surf.polys[allbpolys] inpolys = np.in1d(selbpolys, inbound).reshape(selbpolys.shape) expolys = np.in1d(selbpolys, exbound).reshape(selbpolys.shape) badpolys = np.logical_or(inpolys.all(1), expolys.all(1)) boundpolys = np.logical_and(np.logical_or(inpolys, expolys).all(1), ~badpolys) # Walk around boundary boundpolyinds = set(allbpolys[np.nonzero(boundpolys)[0]]) bgraph = nx.Graph() pos = dict() for pa in boundpolyinds: for pb in set(pgraph[pa]) & boundpolyinds: edge = pgraph[pa][pb]["verts"] validverts = list(valid & edge) pos[edge] = mmp[validverts].mean(0) bgraph.add_edge(*edge) cc = nx.cycles.cycle_basis(bgraph) if len(cc) > 1: # Need to deal with this later: map/reduce calls not python3 compatible edges = reduce(set.symmetric_difference, [set(map(lambda l:tuple(sorted(l)), zip(c, c[1:]+[c[0]]))) for c in cc]) eg = nx.from_edgelist(edges) cycles = nx.cycles.cycle_basis(eg) #longest = np.argmax(map(len, cycles)) longest = np.argmax([len(x) for x in cycles]) # python3 compatible path_order = cycles[longest] else: path_order = cc[0] path_points = [tuple(pos[frozenset(p)]) for p in zip(path_order[:-1], path_order[1:])] # Store poly path = "M %f %f L" % tuple(path_points[0]) path += ", ".join(["%f %f"%p for p in path_points[1:]]) path += "Z " # Insert into SVG svgpath = etree.SubElement(roilayer, "path") svgpath.attrib["style"] = "fill:none;stroke:#000000;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opactiy:1" svgpath.attrib["d"] = path #svgpath.attrib["sodipodi:nodetypes"] = "c" * len(pts) with open(svgroipack.svgfile, "w") as xml: xml.write(etree.tostring(svg, pretty_print=True))
def sliding_window_conv_detect(warped_binary, window_width, window_height, margin, return_debug_img=False): if return_debug_img: debug_image = np.dstack( (warped_binary, warped_binary, warped_binary)) * 255 nonzero = np.nonzero(warped_binary) nonzero_x = nonzero[1] nonzero_y = nonzero[0] img_h, img_w = warped_binary.shape[0:2] # Create empty lists to receive left and right lane pixel indices left_lane_inds = [] right_lane_inds = [] # the 1-D convolutional weights, [1, 1, ..., 1] conv_weight = np.ones(window_width) # First find the two starting positions for the left and right lane by using np.sum # to get the vertical image slice and then np.convolve the vertical image slice with the window template # Sum quarter bottom of image to get slice, could use a different ratio l_histogram = np.sum(warped_binary[int(3 * img_h / 4):, :int(img_w / 2)], axis=0) l_center = np.argmax(np.convolve(conv_weight, l_histogram)) - window_width / 2 win_top = img_h - window_height win_bottom = img_h left_win_left = max(0, int(l_center - window_width / 2)) left_win_right = min(int(l_center + window_width / 2), img_w) good_left_inds = ((nonzero_y >= win_top) & (nonzero_y < win_bottom) & (nonzero_x >= left_win_left) & (nonzero_x < left_win_right)).nonzero()[0] left_lane_inds.append(good_left_inds) r_histogram = np.sum(warped_binary[int(3 * img_h / 4):, int(img_w / 2):], axis=0) r_center = np.argmax(np.convolve( conv_weight, r_histogram)) - window_width / 2 + int(img_w / 2) right_win_left = max(0, int(r_center - window_width / 2)) right_win_right = min(int(r_center + window_width / 2), img_w) good_right_inds = ((nonzero_y >= win_top) & (nonzero_y < win_bottom) & (nonzero_x >= right_win_left) & (nonzero_x < right_win_right)).nonzero()[0] right_lane_inds.append(good_right_inds) if return_debug_img: cv2.rectangle(debug_image, (left_win_left, win_top), (left_win_right, win_bottom), (0, 255, 0), 2) cv2.rectangle(debug_image, (right_win_left, win_top), (right_win_right, win_bottom), (0, 255, 0), 2) win_area = window_width * window_height # the max distance between current and previous center max_dist = 0.8 * window_width # Go through each layer looking for max pixel locations for level in range(1, int(img_h / window_height)): # convolve the window into the vertical slice of the image win_top = int(img_h - (level + 1) * window_height) win_bottom = int(img_h - level * window_height) layer_histogram = np.sum(warped_binary[win_top:win_bottom, :], axis=0) layer_conv_signal = np.convolve(conv_weight, layer_histogram) # Find the best left centroid by using past left center as a reference # Use window_width/2 as offset because convolution signal reference is at right side of window, # not center of window offset = window_width / 2 l_min_index = int(max(l_center + offset - margin, 0)) l_max_index = int( min(l_center + offset + margin, warped_binary.shape[1])) l_argmax = np.argmax(layer_conv_signal[l_min_index:l_max_index]) if layer_conv_signal[l_min_index + l_argmax] > (win_area / 50): l_center_candidate = l_min_index + l_argmax - offset # if the candidata center is far away from previous, adjust it to get close to previous one diff_with_prev = l_center_candidate - l_center if diff_with_prev > 0: if diff_with_prev < max_dist: l_center = l_center_candidate else: l_center = (l_center + min( l_center_candidate, l_center + max_dist * 2)) / 2 elif diff_with_prev < 0: if diff_with_prev > -max_dist: l_center = l_center_candidate else: l_center = (l_center + max( l_center_candidate, l_center - max_dist * 2)) / 2 left_win_left = max(0, int(l_center - offset)) left_win_right = min(int(l_center + offset), img_w) good_left_inds = ((nonzero_y >= win_top) & (nonzero_y < win_bottom) & (nonzero_x >= left_win_left) & (nonzero_x < left_win_right)).nonzero()[0] left_lane_inds.append(good_left_inds) # Find the best right centroid by using past right center as a reference r_min_index = int(max(r_center + offset - margin, 0)) r_max_index = int( min(r_center + offset + margin, warped_binary.shape[1])) r_argmax = np.argmax(layer_conv_signal[r_min_index:r_max_index]) if layer_conv_signal[r_min_index + r_argmax] > (win_area / 50): r_center_candidate = r_min_index + r_argmax - offset # if the candidata center is far away from previous, adjust it to get close to previous one diff_with_prev = r_center_candidate - r_center if diff_with_prev > 0: if diff_with_prev < max_dist: r_center = r_center_candidate else: r_center = (r_center + min( r_center_candidate, r_center + max_dist * 2)) / 2 elif diff_with_prev < 0: if diff_with_prev > -max_dist: r_center = r_center_candidate else: r_center = (r_center + max( r_center_candidate, r_center - max_dist * 2)) / 2 right_win_left = max(0, int(r_center - offset)) right_win_right = min(int(r_center + offset), img_w) good_right_inds = ((nonzero_y >= win_top) & (nonzero_y < win_bottom) & (nonzero_x >= right_win_left) & (nonzero_x < right_win_right)).nonzero()[0] right_lane_inds.append(good_right_inds) if return_debug_img: cv2.rectangle(debug_image, (left_win_left, win_top), (left_win_right, win_bottom), (0, 255, 0), 2) cv2.rectangle(debug_image, (right_win_left, win_top), (right_win_right, win_bottom), (0, 255, 0), 2) # Concatenate the arrays of indices left_lane_inds = np.concatenate(left_lane_inds) right_lane_inds = np.concatenate(right_lane_inds) # Extract left and right line pixel positions leftx = nonzero_x[left_lane_inds] lefty = nonzero_y[left_lane_inds] rightx = nonzero_x[right_lane_inds] righty = nonzero_y[right_lane_inds] # Fit a second order polynomial to each left_fit = np.polyfit(lefty, leftx, 2) right_fit = np.polyfit(righty, rightx, 2) # calculate the radius of curvature is in meters y_in_pixels = np.linspace(0, warped_binary.shape[0] - 1, warped_binary.shape[0] // 5) left_x_in_pixels = left_fit[0] * y_in_pixels**2 + left_fit[ 1] * y_in_pixels + left_fit[2] right_x_in_pixels = right_fit[0] * y_in_pixels**2 + right_fit[ 1] * y_in_pixels + right_fit[2] left_curvature, right_curvature = get_curvature( y_in_pixels, left_x_in_pixels, right_x_in_pixels, cfg.pixel_to_meter_config["x_meters_per_pixel"], cfg.pixel_to_meter_config["y_meters_per_pixel"]) # the center offset in meters lane_center_x_in_pixel = (left_x_in_pixels[-1] + right_x_in_pixels[-1]) / 2.0 image_center_x = warped_binary.shape[1] / 2.0 center_offset = get_center_offset( lane_center_x_in_pixel, image_center_x, cfg.pixel_to_meter_config["x_meters_per_pixel"]) result = Result() result.l_fit = left_fit result.r_fit = right_fit result.l_curvature = left_curvature result.r_curvature = right_curvature result.center_offset = center_offset if return_debug_img: debug_image[nonzero_y[left_lane_inds], nonzero_x[left_lane_inds]] = [255, 0, 0] debug_image[nonzero_y[right_lane_inds], nonzero_x[right_lane_inds]] = [0, 0, 255] result.debug_image = debug_image return result
def detect_based_prev_result(warped_binary, prev_left_fit, prev_right_fit, margin, return_debug_img=False): nonzero = warped_binary.nonzero() nonzero_y = np.array(nonzero[0]) nonzero_x = np.array(nonzero[1]) l_center_x = prev_left_fit[0] * ( nonzero_y**2) + prev_left_fit[1] * nonzero_y + prev_left_fit[2] l_left_margin = l_center_x - margin l_right_margin = l_center_x + margin left_lane_inds = ((nonzero_x > l_left_margin) & (nonzero_x < l_right_margin)) r_center_x = prev_right_fit[0] * ( nonzero_y**2) + prev_right_fit[1] * nonzero_y + prev_right_fit[2] r_left_margin = r_center_x - margin r_right_margin = r_center_x + margin right_lane_inds = ((nonzero_x > r_left_margin) & (nonzero_x < r_right_margin)) # min_pts = 10 # if len(left_lane_inds) < min_pts or len(right_lane_inds) < min_pts: # return None # extract left and right line pixel positions leftx = nonzero_x[left_lane_inds] lefty = nonzero_y[left_lane_inds] # Fit a second order polynomial to the left lane left_fit = np.polyfit(lefty, leftx, 2) # extract left and right line pixel positions rightx = nonzero_x[right_lane_inds] righty = nonzero_y[right_lane_inds] # Fit a second order polynomial to the right lane right_fit = np.polyfit(righty, rightx, 2) # calculate the radius of curvature is in meters y_in_pixels = np.linspace(0, warped_binary.shape[0] - 1, warped_binary.shape[0] // 5) left_x_in_pixels = left_fit[0] * y_in_pixels**2 + left_fit[ 1] * y_in_pixels + left_fit[2] right_x_in_pixels = right_fit[0] * y_in_pixels**2 + right_fit[ 1] * y_in_pixels + right_fit[2] left_curvature, right_curvature = get_curvature( y_in_pixels, left_x_in_pixels, right_x_in_pixels, cfg.pixel_to_meter_config["x_meters_per_pixel"], cfg.pixel_to_meter_config["y_meters_per_pixel"]) # the center offset in meters lane_center_x_in_pixel = (left_x_in_pixels[-1] + right_x_in_pixels[-1]) / 2.0 image_center_x = warped_binary.shape[1] / 2.0 center_offset = get_center_offset( lane_center_x_in_pixel, image_center_x, cfg.pixel_to_meter_config["x_meters_per_pixel"]) result = Result() result.l_fit = left_fit result.r_fit = right_fit result.l_curvature = left_curvature result.r_curvature = right_curvature result.center_offset = center_offset # Create an image to draw on and an image to show the selection window if return_debug_img: focused_region_img = np.dstack( (warped_binary, warped_binary, warped_binary)) * 255 # Color in left and right line pixels focused_region_img[nonzero_y[left_lane_inds], nonzero_x[left_lane_inds]] = [255, 0, 0] focused_region_img[nonzero_y[right_lane_inds], nonzero_x[right_lane_inds]] = [0, 0, 255] # # Generate x and y values for plotting ploty = np.linspace(0, warped_binary.shape[0] - 1, warped_binary.shape[0]) left_fitx = left_fit[0] * ploty**2 + left_fit[ 1] * ploty + left_fit[2] right_fitx = right_fit[0] * ploty**2 + right_fit[ 1] * ploty + right_fit[2] # Generate a polygon to illustrate the search window area # And recast the x and y points into usable format for cv2.fillPoly() l_left_pts = np.array( [np.transpose(np.vstack([left_fitx - margin, ploty]))]) l_right_pts = np.array([ np.flipud(np.transpose(np.vstack([left_fitx + margin, ploty]))) ]) left_line_pts = np.hstack((l_left_pts, l_right_pts)) r_left_pts = np.array( [np.transpose(np.vstack([right_fitx - margin, ploty]))]) r_right_pts = np.array([ np.flipud(np.transpose(np.vstack([right_fitx + margin, ploty]))) ]) right_line_pts = np.hstack((r_left_pts, r_right_pts)) search_area_img = np.zeros_like(focused_region_img) # Draw the lane onto the warped blank image cv2.fillPoly(search_area_img, np.int_([left_line_pts]), (0, 255, 0)) cv2.fillPoly(search_area_img, np.int_([right_line_pts]), (0, 255, 0)) debug_image = cv2.addWeighted(focused_region_img, 1, search_area_img, 0.3, 0) result.debug_image = debug_image return result
def sliding_window_detect(warped_binary, window_height, margin, min_num_pixels, return_debug_img=False): if return_debug_img: debug_image = np.dstack( (warped_binary, warped_binary, warped_binary)) * 255 img_h, img_w = warped_binary.shape[0:2] # Take a histogram of the bottom 2/3 of the image histogram = np.sum(warped_binary[int(img_h * 2 / 3):, :], axis=0) # Find the peak of the left and right halves of the histogram # These will be the starting point for the left and right lines mid_point = np.int(histogram.shape[0] / 2) leftx_base = np.argmax(histogram[:mid_point]) rightx_base = np.argmax(histogram[mid_point:]) + mid_point # Identify the x and y positions of all nonzero pixels in the image nonzero = warped_binary.nonzero() nonzero_y = np.array(nonzero[0]) nonzero_x = np.array(nonzero[1]) # Current positions to be updated for each window leftx_current = leftx_base rightx_current = rightx_base # Create empty lists to receive left and right lane pixel indices left_lane_inds = [] right_lane_inds = [] n_windows = int(img_h / window_height) # Step through the windows one by one for window in range(n_windows): # Identify window boundaries in x and y (and right and left) win_y_low = warped_binary.shape[0] - (window + 1) * window_height win_y_high = warped_binary.shape[0] - window * window_height win_xleft_low = leftx_current - margin win_xleft_high = leftx_current + margin win_xright_low = rightx_current - margin win_xright_high = rightx_current + margin if return_debug_img: cv2.rectangle(debug_image, (win_xleft_low, win_y_low), (win_xleft_high, win_y_high), (0, 255, 0), 2) cv2.rectangle(debug_image, (win_xright_low, win_y_low), (win_xright_high, win_y_high), (0, 255, 0), 2) # Identify the nonzero pixels in x and y within the window good_left_inds = ((nonzero_y >= win_y_low) & (nonzero_y < win_y_high) & (nonzero_x >= win_xleft_low) & (nonzero_x < win_xleft_high)).nonzero()[0] good_right_inds = ((nonzero_y >= win_y_low) & (nonzero_y < win_y_high) & (nonzero_x >= win_xright_low) & (nonzero_x < win_xright_high)).nonzero()[0] # Append these indices to the lists left_lane_inds.append(good_left_inds) right_lane_inds.append(good_right_inds) # If you found > minpix pixels, recenter next window on their mean position if len(good_left_inds) > min_num_pixels: leftx_current = np.int(np.mean(nonzero_x[good_left_inds])) if len(good_right_inds) > min_num_pixels: rightx_current = np.int(np.mean(nonzero_x[good_right_inds])) # Concatenate the arrays of indices left_lane_inds = np.concatenate(left_lane_inds) right_lane_inds = np.concatenate(right_lane_inds) # Extract left lane pixel positions leftx = nonzero_x[left_lane_inds] lefty = nonzero_y[left_lane_inds] # Fit a second order polynomial to the left lane left_fit = np.polyfit(lefty, leftx, 2) # Extract left lane pixel positions rightx = nonzero_x[right_lane_inds] righty = nonzero_y[right_lane_inds] # Fit a second order polynomial to the right lane right_fit = np.polyfit(righty, rightx, 2) # calculate the radius of curvature is in meters y_in_pixels = np.linspace(0, img_h - 1, img_h // 5) left_x_in_pixels = left_fit[0] * y_in_pixels**2 + left_fit[ 1] * y_in_pixels + left_fit[2] right_x_in_pixels = right_fit[0] * y_in_pixels**2 + right_fit[ 1] * y_in_pixels + right_fit[2] left_curvature, right_curvature = get_curvature( y_in_pixels, left_x_in_pixels, right_x_in_pixels, cfg.pixel_to_meter_config["x_meters_per_pixel"], cfg.pixel_to_meter_config["y_meters_per_pixel"]) # the center offset in meters lane_center_x_in_pixel = (left_x_in_pixels[-1] + right_x_in_pixels[-1]) / 2.0 image_center_x = img_w / 2.0 center_offset = get_center_offset( lane_center_x_in_pixel, image_center_x, cfg.pixel_to_meter_config["x_meters_per_pixel"]) result = Result() result.l_fit = left_fit result.r_fit = right_fit result.l_curvature = left_curvature result.r_curvature = right_curvature result.center_offset = center_offset if return_debug_img: debug_image[nonzero_y[left_lane_inds], nonzero_x[left_lane_inds]] = [255, 0, 0] debug_image[nonzero_y[right_lane_inds], nonzero_x[right_lane_inds]] = [0, 0, 255] result.debug_image = debug_image return result
input_timeframe='2H', output_timeframe='4H', smooth=True, smoothing_length=2) print data_slice_resampled.tail() # Convert timestamp to floats data_slice_resampled[ 'time_as_float'] = data_slice_resampled.timestamp.values.astype(float) # Create an intermediate numpy array. x = data_slice_resampled[['time_as_float', 'smooth_close']].values # Calculate Curvature b = u.get_curvature(x) # Use a temp data-frame to store the results of the curvature calculations. tmp_df = b # Normalize the results of curvature solution. tmp_df[:, [-1]] = normalize(tmp_df[:, -1, None], norm='max', axis=0) # Add the solution data to our main data-frame. data_slice_resampled['solution'] = tmp_df[:, 1] # ;------------------------------------------------------------------- # ; TRACE # ;--------------------------------------------------------------------------------- # Create the candlestick plotter. trace_candlestick = go.Candlestick(