def _process(self, image, cnt_3d): if len(cnt_3d) > 0: cnt = to_2D_contour(cnt_3d) # Determine the bounding rectangle of all contours x, y, w, h = cv2.boundingRect(np.concatenate(cnt)) image_height, image_width = image.shape[0], image.shape[1] # subtract the offset to move the bottom-left of the contour to [0,0] cnt_3d = [ np.subtract(c, [x, y, 0], dtype=np.int32) for c in cnt_3d ] preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) preview_cnt = contour_into_image(to_2D_contour(cnt_3d), preview_image) x, y, w, h = cv2.boundingRect(np.concatenate(preview_cnt)) # draw the coordinate axes # x-axis cv2.line(preview_image, (x, y + h), (x + w, y + h), (255, 0, 0), 1) # y-axis cv2.line(preview_image, (x, y), (x, y + h), (0, 0, 255), 1) # draw the contour itself cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) image = preview_image return image, cnt_3d
def _process(self, image, cnt_3d): if len(cnt_3d) > 0: cnt = to_2D_contour(cnt_3d) # Determine the bounding rectangle of all contours x, y, w, h = cv2.boundingRect(np.concatenate(cnt)) image_height, image_width = image.shape[0], image.shape[1] # the offset to move the center of the contour to [0,0] offset_x = int(w / 2 + x) offset_y = int(h / 2 + y) cnt_3d = [np.subtract(c, [offset_x, offset_y, 0], dtype=np.int32) for c in cnt_3d] preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # generate a preview contour # preview_cnt = contour_into_image(to_2D_contour(cnt_3d), preview_image) # draw the coordinate system of the centered drawing contour x, y, w, h = cv2.boundingRect(np.concatenate(preview_cnt)) cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) # horizontal cv2.line(preview_image, (x + int(w / 2), y + int(h / 2)), (x + w, y + int(h / 2)), (255, 0, 0), 1) # vertical cv2.line(preview_image, (x + int(w / 2), y), (x + int(w / 2), y + int(h / 2)), (0, 0, 255), 1) image = preview_image return image, cnt_3d
def _process(self, image, cnt): try: single_channel = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) generated_cnt = [] row_index = 0 # [micro m] normalized_depth = self.depth_in_micro_m / 255 for row in single_channel: rle_row = [rle for rle in self.rle(row) if rle[0] < 250] last_end = None last_contour = None for rle in rle_row: # gray => depth in [micro m] # 255 => 0 [micro m] # white means no cutting # 0 => self.depth_in_micro_m [micro m] # black is full depth depth = -(self.depth_in_micro_m - (normalized_depth * rle[0])) gray_start = rle[1] gray_end = rle[2] + gray_start if gray_start == last_end: last_contour = last_contour+[ [gray_start, row_index, depth], [gray_end, row_index, depth] ] else: if not last_contour is None: generated_cnt.append(np.array(last_contour, dtype=np.int32)) last_contour = [ [gray_start, row_index, depth], [gray_end, row_index, depth] ] last_end = gray_end if not last_contour is None: generated_cnt.append(np.array(last_contour, dtype=np.int32)) row_index += 1 # generate a preview image # preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # generate a preview contour # preview_cnt = contour_into_image(to_2D_contour(generated_cnt), preview_image) cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) # draw the carving depth cv2.putText(preview_image, "Carving Depth {:.2f} mm".format(self.depth_in_micro_m / 1000), (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 4) image = preview_image return image, generated_cnt except Exception as exc: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) print(type(self), exc)
def _process(self, image, cnt_3d): try: if len(cnt_3d) > 0: cnt = to_2D_contour(cnt_3d) x, y, w, h = cv2.boundingRect(np.concatenate(cnt)) image_height, image_width = image.shape[0], image.shape[1] # the offset to move the center of the contour to [0,0] cx = int(w / 2 + x) cy = int(h / 2 + y) a = math.radians(self.angle_in_degree) ca = math.cos(a) sa = math.sin(a) rotate_point = lambda p: [ int(((p[0] - cx) * ca) - ((p[1] - cy) * sa) + cx), int(((p[0] - cx) * sa) + ((p[1] - cy) * ca) + cy), p[2] ] rotate_cnt = lambda c: np.array([rotate_point(p) for p in c]) cnt_3d = [rotate_cnt(c) for c in cnt_3d] preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # generate a preview contour # preview_cnt = contour_into_image(to_2D_contour(cnt_3d), preview_image) # draw the coordinate system of the centered drawing contour x, y, w, h = cv2.boundingRect(np.concatenate(preview_cnt)) cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) # horizontal cv2.line(preview_image, (x + int(w / 2), y + int(h / 2)), (x + w, y + int(h / 2)), (255, 0, 0), 1) # vertical cv2.line(preview_image, (x + int(w / 2), y), (x + int(w / 2), y + int(h / 2)), (0, 0, 255), 1) image = newimage except Exception as exc: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) print(type(self), exc) return image, cnt_3d
def _process(self, image, cnt_3d): if len(cnt_3d) > 0: for c in cnt_3d: for p in c: p[2] = -self.depth_in_micro_m cnt = to_2D_contour(cnt_3d) preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # create a new cnt for the drawing on the image. The cnt should cover 4/5 of the overall image preview_cnt = contour_into_image(cnt, preview_image) cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) # draw the carving depth cv2.putText( preview_image, "Carving Depth {:.2f} mm".format(self.depth_in_micro_m / 1000), (20, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255, 0, 0), 4) image = preview_image return image, cnt_3d
def _process(self, image, cnt_3d): if len(cnt_3d) > 0: def gauss_kernel(kernlen=21, nsig=3): x = np.linspace(-nsig, nsig, kernlen) kern1d = np.diff(st.norm.cdf(x)) return kern1d / kern1d.sum() # repeat and insert the first and last value in the array n-times # (expand the array at the beginning and end). Required for convolve function. # add_padding = lambda array, n: np.insert( np.insert(array, 0, np.full(n, array[0])), -1, np.full(n, array[-1])) box = gauss_kernel(self.window) padding = int(self.window / 2) smoothed_cnt = [] for c in cnt_3d: x, y, z = c.T if len(z) > 0: if len(z) > self.window: x_new = np.convolve(add_padding(x, padding), box, mode="valid") y_new = np.convolve(add_padding(y, padding), box, mode="valid") z_new = np.convolve(add_padding(z, padding), box, mode="valid") # replace the starting points and end points of the CNC contour with the original points. # We don't want modify the start / end of an contour. The reason for that is, that this is normaly # cut-into movement into the stock without any tolerances. So - don't modify them. # # Replace the START Points with the original one x_new[0:padding] = x[0:padding] y_new[0:padding] = y[0:padding] z_new[0:padding] = z[0:padding] # Replace the END Points with the original one x_new[-padding:] = x[-padding:] y_new[-padding:] = y[-padding:] z_new[-padding:] = z[-padding:] smoothed_cnt.append( np.asarray([[int(i[0]), int(i[1]), int(i[2])] for i in zip(x_new, y_new, z_new)])) else: smoothed_cnt.append(c) # generate a preview image # preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # generate a preview contour # preview_cnt = contour_into_image(to_2D_contour(smoothed_cnt), preview_image) # draw the centered contour cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) image = preview_image cnt_3d = smoothed_cnt return image, cnt_3d
def _process(self, image, cnt): try: # generate an inverted, single channel image. Normally OpenCV detect on "white"....but we # want use "black" as contour foundation # single_channel = 255 - cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # determine the contour # https://docs.opencv.org/3.4.0/d9/d8b/tutorial_py_contours_hierarchy.html # We need the "two level" of hierarchy for the contour to perform clipping. # The hierarchy is stored in that pattern: [Next, Previous, First_Child, Parent] # cnt, hierarchy = cv2.findContours(single_channel, cv2.RETR_CCOMP, cv2.CHAIN_APPROX_NONE) hierarchy = hierarchy[0] print(hierarchy) validated_cnt = normalize_contour(cnt) # scale the contour to the user defined width if we have any # if len(validated_cnt) > 0: # Determine the bounding rectangle x, y, w, h = cv2.boundingRect(np.concatenate(validated_cnt)) # Ensure that width of the contour is the same as the width_in_mm. # Scale the contour to the required width. scale_factor = self.width_in_micro_m / w scaled_cnt = [ np.multiply(c.astype(np.float), [scale_factor, scale_factor]).astype(np.int32) for c in validated_cnt ] # generate a hatch pattern we want to apply pattern = self.build_hatch_pattern(scaled_cnt) hatch_cnt = [] for parent_i in range(len(hierarchy)): # [Next, Previous, First_Child, Parent] # Process the parents only. It is a Parent if "Parent" is "-1". parent_h = hierarchy[parent_i] if parent_h[3] == -1: clip_cnt = [scaled_cnt[parent_i].tolist()] # append all child contours of the parent for child_i in range(len(hierarchy)): child_h = hierarchy[child_i] if child_h[3] == parent_i: clip_cnt.append(scaled_cnt[child_i].tolist()) # clip the hatch with the calculated "clipping region" # try: pc = pyclipper.Pyclipper() pc.AddPaths(clip_cnt, pyclipper.PT_CLIP, True) pc.AddPaths(pattern, pyclipper.PT_SUBJECT, False) solution = pc.Execute2(pyclipper.CT_INTERSECTION, pyclipper.PFT_EVENODD, pyclipper.PFT_EVENODD) # add the clipped hatch to the overall contour result hatch = pyclipper.PolyTreeToPaths(solution) for c in hatch: hatch_cnt.append(np.array(c)) except: pass # h_cnt = scaled_cnt.copy() scaled_cnt = hatch_cnt + scaled_cnt # generate a preview image preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # draw some debug information #i = 0 #h_cnt = contour_into_image(h_cnt, preview_image) #for c in h_cnt: # cv2.putText(preview_image, str(i), (c[0][0], c[0][1]), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2) # i = i + 1 # place the contour into the center of the image preview_cnt = contour_into_image(scaled_cnt, preview_image) x, y, w, h = cv2.boundingRect(np.concatenate(preview_cnt)) # draw the outline contour in yellow cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) display_factor = 0.001 if self.display_unit == "mm" else 0.0001 # draw the width dimension cv2.line(preview_image, (x, y + int(h / 2)), (x + w, y + int(h / 2)), (255, 0, 0), 1) cv2.circle(preview_image, (x, y + int(h / 2)), 5, (255, 0, 0), -1) cv2.circle(preview_image, (x + w, y + int(h / 2)), 5, (255, 0, 0), -1) cv2.putText( preview_image, "{:.1f} {}".format(self.width_in_micro_m * display_factor, self.display_unit), (x + 20, y + int(h / 2) - 30), cv2.FONT_HERSHEY_SIMPLEX, 1.65, (255, 0, 0), 4) # draw the height dimension height_in_micro_m = self.width_in_micro_m / w * h cv2.line(preview_image, (x + int(w / 2), y), (x + int(w / 2), y + h), (255, 0, 0), 1) cv2.circle(preview_image, (x + int(w / 2), y), 5, (255, 0, 0), -1) cv2.circle(preview_image, (x + int(w / 2), y + h), 5, (255, 0, 0), -1) cv2.putText( preview_image, "{:.1f} {}".format(height_in_micro_m * display_factor, self.display_unit), (x + int(w / 2) + 20, y + 50), cv2.FONT_HERSHEY_SIMPLEX, 1.65, (255, 0, 0), 4) image = preview_image validated_cnt = scaled_cnt except Exception as exc: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) print(type(self), exc) return image, validated_cnt
def _process(self, image, cnt): try: # generate an inverted, single channel image. Normally OpenCV detect on "white"....but we # want use "black" as contour foundation # single_channel = 255 - cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # determine the contour # cnt, hierarchy = cv2.findContours(single_channel, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) i = 0 validated_cnt = [] while i < len(cnt): c = cnt[i] sq_cnt = np.squeeze(c) if len(sq_cnt.shape) == 2: sq_cnt = np.append(sq_cnt, [[sq_cnt[0][0], sq_cnt[0][1]]], axis=0) validated_cnt.append(sq_cnt) i += 1 # scale the contour to the user defined width # if len(validated_cnt) > 0: # Determine the origin bounding rectangle x, y, w, h = cv2.boundingRect(np.concatenate(validated_cnt)) # Ensure that width of the contour is the same as the width_in_mm. # Scale the contour to the required width. scale_factor = self.width_in_micro_m / w scaled_cnt = [ np.multiply(c.astype(np.float), [scale_factor, scale_factor]).astype(np.int32) for c in validated_cnt ] # generate a preview image # preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # generate a preview contour # preview_cnt = contour_into_image(scaled_cnt, preview_image) x, y, w, h = cv2.boundingRect(np.concatenate(preview_cnt)) # draw the preview contour cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) display_factor = 0.001 if self.display_unit == "mm" else 0.0001 # draw the width dimension cv2.line(preview_image, (x, y + int(h / 2)), (x + w, y + int(h / 2)), (255, 0, 0), 1) cv2.circle(preview_image, (x, y + int(h / 2)), 5, (255, 0, 0), -1) cv2.circle(preview_image, (x + w, y + int(h / 2)), 5, (255, 0, 0), -1) cv2.putText( preview_image, "{:.1f} {}".format(self.width_in_micro_m * display_factor, self.display_unit), (x + 20, y + int(h / 2) - 30), cv2.FONT_HERSHEY_SIMPLEX, 1.65, (255, 0, 0), 4) # draw the height dimension height_in_micro_m = self.width_in_micro_m / w * h cv2.line(preview_image, (x + int(w / 2), y), (x + int(w / 2), y + h), (255, 0, 0), 1) cv2.circle(preview_image, (x + int(w / 2), y), 5, (255, 0, 0), -1) cv2.circle(preview_image, (x + int(w / 2), y + h), 5, (255, 0, 0), -1) cv2.putText( preview_image, "{:.1f} {}".format(height_in_micro_m * display_factor, self.display_unit), (x + int(w / 2) + 20, y + 50), cv2.FONT_HERSHEY_SIMPLEX, 1.65, (255, 0, 0), 4) image = preview_image validated_cnt = scaled_cnt except Exception as exc: exc_type, exc_obj, exc_tb = sys.exc_info() fname = os.path.split(exc_tb.tb_frame.f_code.co_filename)[1] print(exc_type, fname, exc_tb.tb_lineno) print(type(self), exc) return image, validated_cnt
def _process(self, image, cnt_3d): if len(cnt_3d) > 0: cnt = to_2D_contour(cnt_3d) display_factor = 0.001 if self.display_unit == "mm" else 0.0001 # Determine the origin bounding rectangle x, y, w, h = cv2.boundingRect(np.concatenate(cnt)) # Ensure that width of the contour is the same as the width_in_mm. # Scale the contour to the required width. scaled_factor = self.width_in_micro_m / w scaled_cnt = [ np.multiply(c.astype(np.float), [scaled_factor, scaled_factor, 1]).astype(np.int32) for c in cnt_3d ] # generate a preview image # preview_image = np.zeros(image.shape, dtype="uint8") preview_image.fill(255) # generate a preview contour # preview_cnt = contour_into_image(to_2D_contour(scaled_cnt), preview_image) # determine the drawing bounding box x, y, w, h = cv2.boundingRect(np.concatenate(preview_cnt)) # draw the centered contour cv2.drawContours(preview_image, preview_cnt, -1, (60, 169, 242), 1) # draw the width dimension cv2.line(preview_image, (x, y + int(h / 2)), (x + w, y + int(h / 2)), (255, 0, 0), 1) cv2.circle(preview_image, (x, y + int(h / 2)), 5, (255, 0, 0), -1) cv2.circle(preview_image, (x + w, y + int(h / 2)), 5, (255, 0, 0), -1) cv2.putText( preview_image, "{:.1f} {}".format(self.width_in_micro_m * display_factor, self.display_unit), (x + 20, y + int(h / 2) - 30), cv2.FONT_HERSHEY_SIMPLEX, 1.65, (255, 0, 0), 4) # draw the height dimension height_in_micro_m = self.width_in_micro_m / w * h cv2.line(preview_image, (x + int(w / 2), y), (x + int(w / 2), y + h), (255, 0, 0), 1) cv2.circle(preview_image, (x + int(w / 2), y), 5, (255, 0, 0), -1) cv2.circle(preview_image, (x + int(w / 2), y + h), 5, (255, 0, 0), -1) cv2.putText( preview_image, "{:.1f} {}".format(height_in_micro_m * display_factor, self.display_unit), (x + int(w / 2) + 20, y + 50), cv2.FONT_HERSHEY_SIMPLEX, 1.65, (255, 0, 0), 4) image = preview_image cnt_3d = scaled_cnt return image, cnt_3d