def find_nematode(self, frame): gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) ret, gray_frame = cv2.threshold(gray_frame, self.threshold, 255, cv2.THRESH_BINARY_INV) display_frame = frame.copy() centers = [] eccentricities = [] _, contours, _ = cv2.findContours(gray_frame, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) if contours: for idx, contour in enumerate(contours): if self.min_area * self.ppa < cv2.contourArea( contour) < self.max_area * self.ppa: cv2.drawContours(display_frame, contours, idx, (0, 0, 255), int(max(1, self.elements_resize_ratio))) m = cv2.moments(contour) cx = int(m['m10'] / m['m00']) cy = int(m['m01'] / m['m00']) cv2.circle(display_frame, (cx, cy), 2, (0, 255, 0), -1) centers.append((cx, cy)) ellipse = cv2.fitEllipseDirect(contour) display_frame = cv2.ellipse(display_frame, ellipse, (0, 255, 0), 2) eccentricities.append(self.get_eccentricity(ellipse)) return display_frame, centers, eccentricities
def opencv_fitEllipse(img, method="Direct"): #assert binary_mask.min() >= 0.0 and binary_mask.max() <= 1.0 # points = np.argwhere(binary_mask == 1) # TODO: tune threshold contours, hierarchy = cv2.findContours(img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE) # c_img = cv2.drawContours(img, contours, -1, (0, 255, 0), 1) # plt.figure() # plt.imshow(c_img) # plt.show() for c in contours: ellipse = cv2.fitEllipse(c) if method == "AMS": for c in contours: ellipse = cv2.fitEllipseAMS(c) elif method == "Direct": for c in contours: ellipse = cv2.fitEllipseDirect(c) elif method == "Simple": for c in contours: ellipse = cv2.fitEllipse(c) else: raise ValueError("Wrong method") return ellipse
def test_distances(): img_ellipse = np.zeros((300, 300), dtype=np.uint8) center, axes, angle = (150.6, 150.34), (100.66, 50.77), 16.58 cv2.ellipse(img_ellipse, intt(center), intt(axes), angle, 0, 360, 255, thickness=2) contour = cv2.findContours(img_ellipse, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[1][0] fitted_ellipse = box_to_ellipse(*cv2.fitEllipseDirect(contour)) contour = utils.normalize_contour(contour) poly_cv = cv2.ellipse2Poly(intt(center), intt(axes), round(angle), 0, 360, 1) print(utils.polygon_polygon_test(poly_cv, contour, -1)) print(utils.polygon_polygon_test(contour, poly_cv, -1)) poly_my = utils.ellipseF2Poly_numpy(center, axes, angle, 0, 360, 1) poly_my = np.round(poly_my, 0, out=poly_my).astype(np.int32) print(utils.polygon_polygon_test(poly_my, contour, -1)) print(utils.polygon_polygon_test(contour, poly_my, -1))
def fromContour(cls, contour): if contour.len() < 5: return None center, axes, angle = cv2.fitEllipseDirect(contour.points()) if math.isnan(axes[0]) or math.isnan( axes[1]) or axes[0] == 0 or axes[1] == 0: return None return cls(center, axes, angle)
def ellipse_fit(points, method="Direct"): if method == "AMS": (xx, yy), (MA, ma), angle = cv2.fitEllipseAMS(points) elif method == "Direct": (xx, yy), (MA, ma), angle = cv2.fitEllipseDirect(points) elif method == "Simple": (xx, yy), (MA, ma), angle = cv2.fitEllipse(points) return (xx, yy), (MA, ma), angle
def __init__(self, points): fit_ellipse = cv2.fitEllipseDirect(points) self.valid = self.validate_ellipse(fit_ellipse) if not self.valid: return self.center, self.axes, self.angle = utils.box_to_ellipse( *fit_ellipse) self.axis_a, self.axis_b = self.axes self.area = self.axis_a * self.axis_b * np.pi self.aspect_ratio = min(self.axes[0], self.axes[1]) / max( self.axes[0], self.axes[1]) self.__poly = self.__min_rect = self.__main_axes_pts = None
def test_EllipseDirect(img): imgray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) ret, binary = cv2.threshold(src=imgray, thresh=180, maxval=255, type=cv2.THRESH_BINARY) find = np.where(binary == 255) # return tuple cnt = np.vstack(find).T cnt = cnt[:, np.newaxis, :][:, :, ::-1] # (6000, 1, 2) # 并不是把所有点都包裹进去 ellipse = cv2.fitEllipseDirect(cnt) # cvt binary to 3-channels for i in range(img.shape[0]): for j in range(img.shape[1]): img[i][j] = [binary[i][j], binary[i][j], binary[i][j]] cv2.ellipse(img, ellipse, color=(0, 255, 0), thickness=2) cv2.imshow('fitEllipseDirect', img)
def __get_ellipse(self, contour, img): ''' IN: pupil contours and image frame OUT: fitted ellipse around pupil and its contours ''' #TODO: if there are multiple contours, return multiple ellipses mask = np.zeros(img.shape, np.uint8) ellipse = None for c in [contour]: if len(c) >= 5: ellipse = cv2.fitEllipseDirect(c) break if ellipse is not None: mask = cv2.ellipse(mask, ellipse, 255, 2) cnt, hiq = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) return cnt, ellipse return None, None
def compute_ellipticalSF(thresh, cnt): # fit minimum bounded rectangle rect = cv2.minAreaRect(cnt) (rectCoord1, rectCoord2, rotate_angle) = rect # fit minimum bounded ellipse # ret,thresh = cv2.threshold(img,127,255,0) ellipse = cv2.fitEllipseDirect(cnt) #(x, y), (MA, ma), angle ell = cv2.ellipse(thresh,ellipse,(169,169,169),3) (ellpCtr_x, ellpCtr_y), (shortAxis, longAxis), angle = ellipse # perimeter and area of ellipse a = longAxis / 2 b = shortAxis / 2 e = np.sqrt(1 - b**2 / a**2) # eccentricity perimt = 4 * a * ellipe(e*e) area = np.pi * a * b return rectCoord1, rectCoord2, rotate_angle, (ellpCtr_x, ellpCtr_y), shortAxis, longAxis, perimt, area
def update_model(self, centroids): ''' Update the area of pupil displacement IN: list of projected pupil centroids OUT: ellipse of the area ''' mean = np.mean(centroids, axis=0) ellipse_centers = centroids - mean polar = self.__to_polar_batch(ellipse_centers) extremes = self.__find_extremes(polar, 18) cartesian = self.__to_cartesian_batch(extremes) + mean self.extremes = cartesian cnt = [np.array(cartesian, np.int32)] ellipse = cv2.fitEllipseDirect(cnt[0]) if ellipse is not None: self.angle = ellipse[2] self.major_axis = ellipse[1][1] / 2 self.minor_axis = ellipse[1][0] / 2 self.center = ellipse[0] return ellipse
def filter_good_ellipses(self, contours): """ Filter out all bad ellipses and return the good ones. :param contours: The contours in the image :return: List of good ellipses :rtype: list """ good = [] for i, c in enumerate(contours): if len(c) < 5: # cant find ellipse with less self.logger.warning("Contour is too small: %d", i) continue ellipse = cv2.fitEllipseDirect(c) cv2.ellipse(self.processed, ellipse, (255 - i * (255 / len(contours)), 0, 0), 1) if self.check_ellipse(ellipse, c): good.append(copy(ellipse)) return good
def test_fitting_condition(arcs, lines=None): arc_points = [] for arc in arcs: for line in arc: arc_points.append(line[0]) arc_points.append(line[1]) arc_points = np.unique(arc_points, axis=0).astype(np.float64) line_points = [] if lines is not None: for line in lines: line_points.append(line[0]) line_points.append(line[1]) if len(line_points) > 0: line_points = np.unique(line_points, axis=0) points = np.vstack((arc_points, line_points)) points = np.unique(points, axis=0) else: points = arc_points if len(points) < 6: return True, None points = np.array(points).astype(dtype=np.int32) ellipse = Ellipse(cv2.fitEllipseDirect(points)) score_arc = ellipse.fitting_score(arc_points) if len(line_points) > 0: score_line = ellipse.fitting_score(line_points) else: score_line = 1.0 threshold_fit = 0.8 if score_arc < threshold_fit or score_line < threshold_fit: return False, ellipse return True, ellipse
def approximate_electrode_outline(img_bw, iterations=20, center=None): """ Iteratively approximate the electrode area in the given binary image. :param img_bw: The binary image :param iterations: The number of iterations :param center: The approximate center of the electrode (if None, center of the image will be assumed) :return: an ellipse mask outlining the approximate shape of the electrode, the ellipse description ((cx, cy), (ra, rb), angle) the outline of the electrodes masked by the returned mask """ # TODO: check if copy is redundant img_bw_orig = np.copy(img_bw) mask = None area = np.nan new_area = np.nan i = 0 while not ( area - new_area ) / area < 0.01: # continue until area change per iteration is less than 1 % # get contours cont = find_contour(img_bw, center) # fit ellipse ellipse = cv.fitEllipseDirect(cont) # calculate area to determine convergence area = new_area new_area = ellipse[1][0] * ellipse[1][ 1] # pseudo area (no need to bother multiplying by pi) # update center based on fit center = ellipse[0] # create ellipse mask to use in next iteration mask = get_ellipse_mask(ellipse, img_bw.shape, 25) img_bw = cv.bitwise_and(img_bw_orig, mask) i += 1 if i >= iterations: break return mask
def vrniKroglo(slika, elipsa, izpisujOpozorila=False): """ slika - rabi sliko na kateri isce elipsa - kje naj isce izpisujOpozorila - ali izpisuje opozorila v cmd Vrne tocko kje se nahaja krogla """ slika = slika.copy() if debug: slikaDebug = slika.copy() # Maskiram po plosci maska = cv.ellipse(np.zeros(slika.shape, dtype=np.uint8), elipsa[0], elipsa[1], elipsa[2], 0, 360, (1, 1, 1), -1) slika = np.where(maska == (0, 0, 0), (255, 255, 255), slika).astype(np.uint8) # Pretvorim v hsv prostor slika = cv.cvtColor(slika, cv.COLOR_BGR2HSV) # Maskiram po barvi zogice slika = cv.inRange(slika, (0, 0, 0), (180, 60, 110)) if debug: cv.imshow("krogla maska1", slika) # Odstrani majhne tocke slika = cv.medianBlur(slika, 3) # Zapolni zogo kernel = np.ones((3, 3), np.uint8) slika = cv.dilate(slika, kernel) slika = cv.erode(slika, kernel) # Odstrani majhne tocke slika = cv.medianBlur(slika, 5) if debug: cv.imshow("krogla maska2", slika) # Najde obrobe obroba = cv.findContours(slika, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) # Ce ni nasel obrob if obroba is None: if izpisujOpozorila: print("Ne najdem robov") return None, None # Razvrstim obrobe po povrsini obrobe = [] for o in obroba[0]: obrobe.append({"o": o, "povrsina": cv.contourArea(o)}) obrobe = sorted(obrobe, key=lambda o: o["povrsina"], reverse=True) for o in obrobe: # Povrsina obrobe more vstrezat """ Te meje bi lahko določil z razmerjem vrhov.. če so roboti dvignjeni so blizje kameri in zato je zogica vecja """ if o["povrsina"] < 270 or o["povrsina"] > 970: continue # Ce imam manj kot 5 tock ne morem dolocit elipse if o["o"].shape[0] < 5: continue # Dolocim elipso na dane tocke obrobe tocka, (MA, ma), kot = cv.fitEllipseDirect(o["o"]) (MA, ma) = (MA / 2, ma / 2) if debug: cv.drawContours(slikaDebug, o["o"], -1, (0, 100, 255)) if debug: cv.ellipse(slikaDebug, (int(tocka[0]), int(tocka[1])), (int(MA), int(ma)), int(kot), 0, 360, (255, 255, 255)) if debug: cv.imshow("krogla slika", slikaDebug) # Ali je priblizno krog avg = np.average((MA, ma)) vecji = np.max((MA, ma)) manjsi = np.max((MA, ma)) if manjsi / vecji < 0.5: continue # Ali je primerno velika razmerje = np.max(elipsa[1]) / np.max((MA, ma)) razmerjeRef = 30 / 2.3 toleranca = 0.2 if not (razmerje > razmerjeRef * (1 - toleranca) and razmerje < razmerjeRef * (1 + toleranca)): continue # Vrne zaokrozeno return (int(tocka[0]), int(tocka[1])), int(avg) return None, None
def assembled_area(parts): pts = np.vstack([p.points() for p in parts]) center, axes, angle = utils.box_to_ellipse(*cv2.fitEllipseDirect(pts)) return axes[0] * axes[1] * np.pi
print(len(contours)) for cnt in contours: M = cv2.moments(cnt) if M["m00"] != 0: cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) else: continue area = cv2.contourArea(cnt) if area > max_area or area < min_area: continue x, y, w, h = cv2.boundingRect(cnt) rectArea = w * h degOfCompactness = float(area) / rectArea perimeter = cv2.arcLength(cnt, True) (x, y), (MA, ma), angle = cv2.fitEllipseDirect(cnt) a = ma / 2 b = MA / 2 ratioOfAxis = float(ma) / MA ellipseArea = math.pi * a * b eccentricity = math.sqrt(pow(a, 2) - pow(b, 2)) eccentricity = round(eccentricity / a, 2) formFactor = pow(perimeter, 2) / 4 * math.pi * area roundness = 4 * math.pi * area / pow(perimeter, 2) formFactor = round(formFactor, 2) roundness = round(roundness, 2) areaRatio = float(area) / ellipseArea candidates.append((cX, cY, area, perimeter)) # cv2.ellipse(currFrame,ellipse,(0,255,0),2) cv2.drawContours(currFrame, [cnt], -1, (0, 0, 255), 1) cv2.putText(currFrame, str(ratioOfAxis), (cX, cY),
def refine_electrode_outline(img_bw, mask): # get contour of the ellipse mask ell_cont, hierarchy = cv.findContours(mask, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_NONE) ell_cont = ell_cont[0] # there can be only one contour # get centroid of the contour m = cv.moments(ell_cont) cx = int(m["m10"] / m["m00"]) cy = int(m["m01"] / m["m00"]) center = (cx, cy) # get apply mask to input image and get contour img_bw = cv.bitwise_and(img_bw, mask) cont = find_contour(img_bw, center) # visualize result # img_col = cv.cvtColor(img_bw, cv.COLOR_GRAY2BGR) # cv.drawContours(img_col, cont, -1, (255, 0, 0), 3) # cv.drawContours(img_col, ell_cont, -1, (0, 255, 0), 3) # cv.imshow("Image", img_col) # cv.waitKey() # check contour against ellipse contour contour = np.squeeze(cont) exclude_contour = np.squeeze(ell_cont) is_on_ellipse = np.zeros((contour.shape[0]), dtype=bool) for i1 in range(0, contour.shape[0]): if (exclude_contour == contour[i1, :]).all(axis=1).any(): is_on_ellipse[i1] = True is_included = np.logical_not(is_on_ellipse) # post-process excluded region (convert to BW image and use morphological operations) is_included = np.array(is_included * 255, dtype=np.uint8) # to bw image is_included = np.reshape(is_included, (-1, 1)) # close small gaps in the contour (random peaks that got excluded) strel = cv.getStructuringElement(cv.MORPH_RECT, (1, 51)) is_included = cv.morphologyEx(is_included, cv.MORPH_CLOSE, strel) strel = cv.getStructuringElement(cv.MORPH_RECT, (1, 51)) is_included = cv.morphologyEx(is_included, cv.MORPH_OPEN, strel) # extend excluded regions strel = cv.getStructuringElement(cv.MORPH_RECT, (1, 201)) is_included = cv.morphologyEx(is_included, cv.MORPH_ERODE, strel) is_included = is_included > 0 is_included = np.reshape(is_included, (-1)) # find islands incl_diff = np.diff(np.array(is_included, dtype=np.int8)) i_start = np.nonzero(incl_diff > 0)[0] i_end = np.nonzero(incl_diff < 0)[0] # fix length mismatch (in case a transition is at the beginning/end of the data and no caught by diff) if len(i_start) < len(i_end): # fewer starts than ends i_start = np.insert(i_start, 0, 0) # first block starts at index 0 elif len(i_start) > len(i_end): # fewer ends than starts i_end = np.append(i_end, len(is_included) - 1) # last block ends at last index # if lengths still don't match, we have a problem! if len(i_start) != len(i_end): raise ValueError("can't match start and end of included regions") if len(i_start) != 3: logger.warning( "Expected 3 contour regions to include. Found: {}".format( len(i_start))) # TODO: solve this problem properly! Not just catch the error if i_start[0] > i_end[ 0]: # start and end don't match -> shift order to match them i_end = np.roll(i_end, -1) # include_regions = np.concatenate(i_start, i_end, axis=1) # combine matched start and end indices contour_split = [] for i in range(len(i_start)): if i_start[i] < i_end[ i]: # the normal case, where start and end are in order section = cont[i_start[i] + 1:i_end[i] + 1, :] else: s1 = cont[i_start[i] + 1:, :] s2 = cont[:i_end[i] + 1, :] section = np.append(s1, s2, axis=1) contour_split.append(np.reshape(section, (-1, 1, 2))) # stitch together to get ellipse fit cont_stitched = np.concatenate(contour_split) ellipse = cv.fitEllipseDirect(cont_stitched) return ellipse
def dea_fit_ellipse(self, img, exclude_mask=None): if img is None: logger.warning("No binary image specified!") return None, None # binarize image img_bw = get_binary_image(img, closing_radius=10) # If no mask is specified, create one with no exclusions if exclude_mask is None: # If there is no mask, the algorithm is run once with a blank mask to generate a proper mask. # We then run fit ellipse normally to generate the actual fit. # This step is crucial to ensure that subsequent calls to dea_fit_ellipse return consistent results. # The values returned without a mask differ from those with a mask. # TODO: maybe fix this mask issue at some point but for now we'll just call fit ellipse twice. self.logging.debug( "No mask provided for ellipse fitting. Running ellipse fit once to generate mask." ) exclude_mask = np.zeros(img_bw.shape, dtype=np.uint8) ellipse, exclude_mask = self.dea_fit_ellipse(img, exclude_mask) # get contours cont = find_contour(img_bw) ellipse = None new_area = np.nan converged = False i = 0 while not converged: # continue until area change per iteration is less than 1 % # exclude regions of contour cont_in = exclude_contour_points(cont, exclude_mask) # fit ellipse if cont_in is None or len(cont_in) < 5: logger.warning( "Unable to fit ellipse because no outline was found.") if np.any(exclude_mask > 0): # if any exclusions were applied exclude_mask = np.zeros( img_bw.shape, dtype=np.uint8) # reset mask and try again logger.warning("Trying again without any exclusions.") continue return None, None # if we can't fit an ellipse and there was no mask applied, we have to abort ellipse = cv.fitEllipseDirect(cont_in) # calculate area to determine convergence old_area = new_area new_area = ellipse[1][0] * ellipse[1][ 1] # pseudo area (no need to bother multiplying by pi) # create ellipse mask and apply inverse to image to create exclude mask ellipse_mask = get_ellipse_mask(ellipse, img_bw.shape, 50) exclude_mask = cv.bitwise_and(img_bw, cv.bitwise_not(ellipse_mask)) # dilate exclude mask to overlap ellipse contour operation = cv.MORPH_DILATE n = 61 kernel = cv.getStructuringElement(cv.MORPH_ELLIPSE, (n, n)) exclude_mask = cv.morphologyEx(exclude_mask, operation, kernel) # count iterations to abort after a while if the result does not converge for some reason i += 1 if i >= self.fit_max_iterations: break if old_area == 0: converged = False else: converged = (old_area - new_area ) / old_area < self.fit_convergence_threshold if i < self.fit_max_iterations: logger.debug( "Electrode outline detection converged after {} iterations". format(i)) else: logger.warning( "Electrode outline detection did not converge. Aborted after {} iterations" .format(i)) return ellipse, exclude_mask
def add_ellipses_csv(): """Add ellipses parameters to training_set_pixel_size_and_HC.csv""" import numpy as np import pandas as pd import os import cv2 from PIL import Image import matplotlib.pyplot as plt df = pd.read_csv("../../data/raw/training_set_pixel_size_and_HC.csv") centers_x = list() centers_y = list() axes_a = list() axes_b = list() angles = list() for i, row in df.iterrows(): filename = row["filename"] print("i: ", i, end="\r") img = Image.open( os.path.join( "../../data/raw/training_set/", filename.replace(".png", "_Annotation.png"), )) img = np.array(img) points = np.argwhere(img > 127) (xx, yy), (MA, ma), angle = cv2.fitEllipseDirect(points) factor = row["pixel size(mm)"] center_x_mm = factor * yy center_y_mm = factor * xx semi_axes_a_mm = factor * ma / 2 semi_axes_b_mm = factor * MA / 2 angle_rad = (-angle * np.pi / 180) % np.pi # print(center_x_mm, center_y_mm, semi_axes_a_mm, semi_axes_b_mm, angle_rad) centers_x.append(center_x_mm) centers_y.append(center_y_mm) axes_a.append(semi_axes_a_mm) axes_b.append(semi_axes_b_mm) angles.append(angle_rad) h = (semi_axes_a_mm - semi_axes_b_mm)**2 / (semi_axes_a_mm + semi_axes_b_mm)**2 circ = (np.pi * (semi_axes_a_mm + semi_axes_b_mm) * (1 + (3 * h) / (10 + np.sqrt(4 - 3 * h)))) assert np.abs(circ - row["head circumference (mm)"]) < 0.1 # print("circ: ", circ) # print("true circ: ", row["head circumference (mm)"]) # plt.imshow(img) # plt.show() df["center_x_mm"] = centers_x df["center_y_mm"] = centers_y df["semi_axes_a_mm"] = axes_a df["semi_axes_b_mm"] = axes_b df["angle_rad"] = angles print(df) df.to_csv( "../../data/processed/training_set_pixel_size_and_HC_and_ellipses.csv", index=False, )
def draw_fitted_ellipse(img, contours, color): pts = np.vstack([c.points() for c in contours]) center, axes, angle = utils.box_to_ellipse(*cv2.fitEllipseDirect(pts)) cv2.ellipse(img, utils.intt(center), utils.intt(axes), angle, 0, 360, color, 1)
def dist_from_fitted_ellipse(contours): points = np.vstack([c.points() for c in contours]) center, exes, angle = utils.box_to_ellipse(*(cv2.fitEllipseDirect(points))) ellipse_poly = utils.ellipseF2Poly(center, exes, angle, 0, 360, 1) return utils.polygon_polygon_test(points, ellipse_poly, -1)
def elipsa(tocke, slika, izpisujOpozorila=False): """ Tocke je vektor iz treh tock za elipso Vrne parametre elipse """ slika = slika.copy() c = np.average(tocke, axis=0) nove = list(tocke) for t in tocke: nove.append(list(c - np.array(t) + c)) #cv.drawMarker(slika, tuple(np.array((c - np.array(t) + c), dtype = np.int)), (255, 200, 200), cv.MARKER_TILTED_CROSS, 15) (x, y), (MA, ma), kot = cv.fitEllipseDirect(np.array(nove).astype(np.int32)) #MA, ma = int(MA * 1.05), int(ma * 1.05) # Da malo poveča elipso #E1, E2, E3 = (int(x), int(y)), (int(MA / 2), int(ma / 2)), int(kot) E1, E2, E3 = np.array([x, y]), np.array([MA / 2, ma / 2]), np.array(kot) # region Boljše prileganje 2 hsv = cv.cvtColor(slika, cv.COLOR_BGR2HSV) nove = [] preveriNajvec = 20 # Koliko tock naj najvec preveri predno obupa kotZacetni = kotMedVektorji(np.array([1, 0]), tocke[0] - E1) kotPremika = 2 * np.pi / preveriNajvec # Zmesa seznam (zmesa vrsti red po katerem bo iskal robove) vsi = np.arange(preveriNajvec) np.random.shuffle(vsi) for i in vsi: # Kot pod katerem trenutno iscem kot = kotPremika * i + kotZacetni # Enotski vektor v kateri smeri trenutno iscem v = np.array([np.cos(kot), np.sin(kot)]) # Preveri linijo v katero kaze vektor v for s in range(int(np.min(E2) * 0.9), int(np.max(E2) * 1.1), 1): # Pixel, ki ga pregleduje pix = np.round(v * s + E1).astype(np.uint) ## Ce je pixel preblizu tocke vrha odnehaj #for t in tocke: # if np.linalg.norm(pix - t) < 10: # break #else: # Ce je pixel izven meja slike odnehaj if pix[0] < 0 or pix[0] > hsv.shape[1] - 1 or pix[1] < 0 or pix[ 1] > hsv.shape[0] - 1: break if debug: cv.drawMarker(slika, tuple(pix), (0, 100, 50), cv.MARKER_CROSS, 1) # Preveri ali je pixel dovolj temen if hsv[pix[1], pix[0], 2] < 50: if debug: cv.drawMarker(slika, tuple(pix), (0, 255, 150), cv.MARKER_SQUARE, 10) nove.append(pix) break # Ce imam vsaj toliko tock lahko neha z iskanjem if len(nove) > 12: if debug: cv.imshow("Vrhovi prileganje", slika) (x, y), (MA, ma), kot = cv.fitEllipseDirect(np.array(nove, dtype=np.int)) E1, E2, E3 = (int(x), int(y)), (int(MA / 2), int(ma / 2)), int(kot) return E1, E2, E3 if debug: cv.imshow("Vrhovi prileganje", slika) if izpisujOpozorila: print("Slabo prileganje") return (int(E1[0]), int(E1[1])), (int(E2[0]), int(E2[1])), int(E3)
imgCp = img.copy() (x, y), radius = cv2.minEnclosingCircle(contours[i]) (x, y, radius) = np.int0((x, y, radius)) # 圆心和半径取整 cv2.circle(imgCp, (x, y), radius, (0, 0, 255), 2) # 拟合椭圆,方法返回的是一个RotatedRectangle,拟合的椭圆即为其内切椭圆 # 青色 ellipse = cv2.fitEllipse(contours[i]) cv2.ellipse(imgCp, ellipse, (255, 255, 0), 2) # 拟合椭圆的另外两种方法 # 蓝色 ellipse = cv2.fitEllipseAMS(contours[i]) cv2.ellipse(imgCp, ellipse, (255, 0, 0), 2) # 绿色 ellipse = cv2.fitEllipseDirect(contours[i]) cv2.ellipse(imgCp, ellipse, (0, 255, 0), 2) totImg = cv2.hconcat((totImg, imgCp)) cv2.imshow('Bounding Circle', totImg) # 考察轮廓的外接多边形 totImg = img.copy() for i in range(len(contours)): # 多边形逼近,得到多边形的角点。使用Douglas-Peucker算法 imgCp = img.copy() # epsilon表示近似多边形周长和源轮廓周长之间的差值,越小则多边形与源轮廓越相似 e = 0.01 * cv2.arcLength(contours[i], True) # 取源轮廓周长的百分比作为差值 approx = cv2.approxPolyDP(contours[i], epsilon=e, closed=True) cv2.polylines(imgCp, [approx], True, (0, 255, 0), 2)