def get_contours(frame, approx=True): """Get contours from an image :: iplimage -> CvSeq """ # A workaround for OpenCV 2.0 crash on receiving a (nearly) black image nonzero = cv.CountNonZero(frame) logging.debug("Segmentation got an image with %d nonzero pixels", nonzero) if nonzero < 20 or nonzero > 10000: return [] storage = cv.CreateMemStorage(0) # find the contours contours = cv.FindContours(frame, storage, cv.CV_RETR_LIST, cv.CV_CHAIN_APPROX_SIMPLE) if contours is None: return [] res = [] while contours: if not approx: result = contours else: result = cv.ApproxPoly(contours, storage, cv.CV_POLY_APPROX_DP, cv.ArcLength(contours) * 0.02, 1) res.append(result) contours = contours.h_next() return res
def __init__(self, contours, index, depth): self.index = index # used from other thread, so it knows to clear drawing surface on zero. self.depth = depth # kinect depth level self.mem1 = cv.CreateMemStorage(0) self.mem2 = cv.CreateMemStorage(0) sizeof_contour = ctypes.sizeof(cv.Contour.CSTRUCT) self.poly1 = cv.ApproxPoly( # pass1 contours, sizeof_contour, self.mem1, cv.CV_POLY_APPROX_DP, 3.0, 1) #print('POLY1') self.poly2 = cv.ApproxPoly( # pass2 self.poly1, sizeof_contour, self.mem2, cv.CV_POLY_APPROX_DP, 20.0, 1) #print('POLY2') self.total = self.poly2.total
def get_mask_with_contour(img, ret_img=False, ret_cont=False, with_init_mask=False, cont_color=cv.RGB(255, 50, 50), normalize=True, skin_version=1, strong=False): if normalize: img = normalize_rgb(img, aggressive=0.005) mask = skin_mask(img) if skin_version == 1 else skin_mask2(img) di_mask = image_empty_clone(mask) cv.Dilate(mask, di_mask) seqs = cv.FindContours(cv.CloneImage(di_mask), memory(), cv.CV_RETR_EXTERNAL) c_img = image_empty_clone(mask) cv.DrawContours(c_img, seqs, 255, 255, 10, -1) er_img = image_empty_clone(c_img) cv.Erode(c_img, er_img, iterations=2) seqs = cv.FindContours(cv.CloneImage(er_img), memory(), cv.CV_RETR_EXTERNAL) if not seqs: print "no areas" return img, None seqs = cv.ApproxPoly(seqs, memory(), cv.CV_POLY_APPROX_DP, parameter=3, parameter2=1) result = [] if ret_img: # er_seq_img = cv.CreateImage(sizeOf(er_img), 8, 3) # cv.Zero(er_seq_img) er_seq_img = cv.CloneImage(img) if with_init_mask: cv.Merge(mask, mask, mask, None, er_seq_img) if strong: cv.DrawContours(er_seq_img, seqs, cont_color, 0, 10, thickness=3) cv.DrawContours(er_seq_img, seqs, cv.RGB(0, 0, 0), 0, 10, thickness=1) else: cv.DrawContours(er_seq_img, seqs, cont_color, 0, 10, thickness=1) result.append(er_seq_img) if ret_cont: result.append(seqs) return result
def _get_approx(self, conts): ''' Returns contur aproximation @param conts: conturs ''' per = cv.ArcLength(conts) conts = cv.ApproxPoly(conts, cv.CreateMemStorage(), cv.CV_POLY_APPROX_DP, per * 0.05) #cv.DrawContours(self.img, conts, (0,0,255), (0,255,0), 4) return list(conts)
def detect_outline(self, image, threshold=THRESHOLD): img_size = cv.GetSize(image) grayscale = cv.CreateImage(img_size, 8, 1) cv.CvtColor(image, grayscale, cv.CV_BGR2GRAY) cv.EqualizeHist(grayscale, grayscale) storage = cv.CreateMemStorage(0) cv.Threshold(grayscale, grayscale, threshold, 255, cv.CV_THRESH_BINARY) contours = cv.FindContours(grayscale, cv.CreateMemStorage(), cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE) if len(contours) > 0: return cv.ApproxPoly(contours, storage, cv.CV_POLY_APPROX_DP, 1.5, 1) return contours
def find_contours(im): """ @param im IplImage: an input gray image @return cvseq contours using cv.FindContours """ storage = cv.CreateMemStorage(0) try: contours = cv.FindContours(im, storage, cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE) contours = cv.ApproxPoly(contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) except cv.error, e: print e return None
def motion_bbox(output, motion): #global muestra, prom global bbox_list #INICIO = time.time() contour = cv.FindContours(motion, mem_storage, cv.CV_RETR_CCOMP, cv.CV_CHAIN_APPROX_SIMPLE) bbox_list = [] #lista para almacenar los bounding box de las manchas average_box_area = 0 #variable para obtener el area en promedio de las bounding box while contour: #recorrido de los contornos/manchas bbox = cv.BoundingRect( list(contour)) #obtencion del bounding box del contorno actual pt1 = (bbox[0], bbox[1]) #punto 1 del bounding box pt2 = (bbox[0] + bbox[2], bbox[1] + bbox[3]) #punto 2 del bounding box w, h = abs(pt1[0] - pt2[0]), abs( pt1[1] - pt2[1]) #ancho y largo del bounding box #obtencion de puntos del contorno para crear un wire-frame polygon_points = cv.ApproxPoly(list(contour), mem_storage, cv.CV_POLY_APPROX_DP) #mostrar o las manchas de movimiento if SHOW_MOVEMENT_AREA: cv.FillPoly(output, [ list(polygon_points), ], cv.CV_RGB(255, 255, 255), 0, 0) #mostrar o no los contornos de las manchas de movimiento if SHOW_MOVEMENT_CONTOUR and w * h > AREA * MIN_PERCENT: cv.PolyLine(output, [ polygon_points, ], 0, cv.CV_RGB(255, 255, 255), 1, 0, 0) average_box_area += w * h #acumulacion de totales de areas bbox_list.append((pt1, pt2)) #lista con todos los bounding box contour = contour.h_next() #lectura del siguiente contorno, si hay if len(bbox_list) > 0: #si hubo movimiento average_box_area = average_box_area / float( len(bbox_list)) #area promedio de bounding box new_bbox_list = [ ] #nueva lista de bounding box, eliminando los menores al area promedio for i in range(len(bbox_list)): #recorrido de los bounding box pt1, pt2 = bbox_list[i] #separacion en dos puntos del bounding box w, h = abs(pt1[0] - pt2[0]), abs( pt1[1] - pt2[1]) #obtencion del ancho y largo if w * h >= average_box_area and w * h > AREA * MIN_PERCENT: #comparacion del area del bounding box con el promedio new_bbox_list.append( (pt1, pt2)) #si es mayor o igual, se queda en la nueva lista bbox_list = get_collided_bboxes( new_bbox_list ) #combinacion de varios bounding box en uno si estan en contacto
def __refresh_poly(self): self.polys_out = cv.ApproxPoly(self.contours, self.a_storage, cv.CV_POLY_APPROX_DP, self.__poly_acc / 100.0, -1) # Prints a count of the number of polygons and points in the picture thingy con = self.polys_out self.pointc = 0 self.polyc = 0 while not con == None: self.pointc += len(con) self.polyc += 1 con = con.h_next() print '\n%d polygons'%self.polyc print '%d points'%self.pointc cv.Set(self.contour_out, cv.ScalarAll(255)) cv.DrawContours(self.contour_out, self.polys_out, cv.Scalar(0, 0, 0), cv.Scalar(0, 0, 0), 99) cv.ShowImage('Contours', self.contour_out)
def find_squares_from_binary( gray ): """ use contour search to find squares in binary image returns list of numpy arrays containing 4 points """ squares = [] storage = cv.CreateMemStorage(0) contours = cv.FindContours(gray, storage, cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE, (0,0)) storage = cv.CreateMemStorage(0) while contours: #approximate contour with accuracy proportional to the contour perimeter arclength = cv.ArcLength(contours) polygon = cv.ApproxPoly( contours, storage, cv.CV_POLY_APPROX_DP, arclength * 0.02, 0) if is_square(polygon): squares.append(polygon[0:4]) contours = contours.h_next() return squares
def get_candidates(self, m_d): ''' Get candidates for this corner from new image @param m_d: marker_detector ''' # if this corner is wider then MAX_CORNER_ANGLE, we probably won't # find it anyway. Instead lets find narrow corners and calculate its # position if self.angle > MAX_CORNER_ANGLE: return [] cr = self.get_rectangle(m_d) cr = correct_rectangle(cr, m_d.size) if cr is None: return [] m_d.set_ROI(cr) tmp_img = m_d.tmp_img gray_img = m_d.gray_img bw_img = m_d.bw_img canny = m_d.canny_img cv.Copy(gray_img, tmp_img) cv.Threshold(gray_img, bw_img, 125, 255, cv.CV_THRESH_OTSU) if self.black_inside > 0: cv.Not(bw_img, bw_img) cv.Canny(gray_img, canny, 300, 500) cv.Or(bw_img, canny, bw_img) tmpim = m_d.canny_img cv.Copy(bw_img, tmpim) cv.Set2D(tmpim, 1, 1, 255) conts = cv.FindContours(tmpim, cv.CreateMemStorage(), cv.CV_RETR_EXTERNAL) cv.Zero(tmpim) m_d.set_ROI() cv.SetImageROI(tmpim, cr) result = [] while conts: aconts = cv.ApproxPoly(conts, cv.CreateMemStorage(), cv.CV_POLY_APPROX_DP, 2) nconts = list(aconts) cv.PolyLine(tmpim, [nconts], True, (255, 255, 255)) self._append_candidates_from_conts(cr, result, nconts, m_d) conts = conts.h_next() # print result # db.show([tmpim,m_d.draw_img], 'tmpim', 0, 0, 0) return result
def get_contours(frame): """Get contours from an image :: iplimage -> CvSeq """ storage = cv.CreateMemStorage(0) # find the contours contours = cv.FindContours( frame, storage, cv.CV_RETR_LIST, cv.CV_CHAIN_APPROX_SIMPLE ) if contours is None: return contours = cv.ApproxPoly( contours, storage, cv.CV_POLY_APPROX_DP, cv.ArcLength(contours)*0.05, 1 ) return contours
def contours(mask): """ Return clockwise contours (anticlockwise around holes) """ import cv storage = cv.CreateMemStorage() cont = cv.FindContours(cv.fromarray(mask.astype('uint8')),storage) if not len(cont): return [ ] approx = cv.ApproxPoly(cont,storage,cv.CV_POLY_APPROX_DP,1.0, 1) item = approx result = [ ] while item: assert item.v_next() is None and item.v_prev() is None result.append( list(item) ) item = item.h_next() return result
boundingBoxList = [] # Use the white pixels as the motion and find the contours contour = cv.FindContours(greyImage, memStorage, cv.CV_RETR_CCOMP, cv.CV_CHAIN_APPROX_SIMPLE) while contour: boundingRect = cv.BoundingRect(list(contour)) p1 = (boundingRect[0], boundingRect[1]) p2 = (boundingRect[0] + boundingRect[2], boundingRect[1] + boundingRect[3]) boundingBoxList.append((p1, p2)) polygonPoints = cv.ApproxPoly(list(contour), memStorage, cv.CV_POLY_APPROX_DP) #Show the contours cv.FillPoly(greyImage, [ list(polygonPoints), ], cv.CV_RGB(255, 255, 255), 0, 0) cv.PolyLine(displayImage, [ polygonPoints, ], 0, cv.CV_RGB(255, 255, 255), 1, 0, 0) contour = contour.h_next() # Find the average size of the bounding box targets and remove ones that are 5% or less than the average as noise boxAreas = [] for box in boundingBoxList: boxWidth = box[right][0] - box[left][0]
def shapeAnalysis(mask): height, width = mask.shape pixels = height * width # spatial and central moments moments = cv.Moments(mask, binary=1) huMoments = cv.GetHuMoments(moments) print "Shape hu moments", huMoments # distances from the gravity point contour_seq = cv.FindContours(np.array(mask), cv.CreateMemStorage(), cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE) gravity_center = (int(moments.m10 / moments.m00), int(moments.m01 / moments.m00)) # (x, y) gx, gy = gravity_center distances = np.array( [math.sqrt((gx - x)**2 + (gy - y)**2) for (x, y) in contour_seq]) dist_distri, bin_dist = np.histogram(distances, bins=10, range=None, normed=True) print "dist distribution", dist_distri dist_max = np.max(distances) dist_min = np.min(distances) dist_ratio_min_max = dist_min / dist_max print "dist ratio min max", dist_ratio_min_max dist_mean = np.mean(distances) dist_std = np.std(distances) # normalize distance min and max dist_max = dist_max / pixels dist_min = dist_min / pixels dist_mean = dist_mean / pixels dist_std = dist_std / pixels print "dist max", dist_max print "dist min", dist_min print "dist mean", dist_mean print "dist std", dist_std # number of petals nbPetals = np.sum([ min(x1, x2) < dist_mean < max(x1, x2) for x1, x2 in zip(distances[:-1], distances[1:]) ]) / 2 print "petals", nbPetals poly_seq = cv.ApproxPoly(contour_seq, cv.CreateMemStorage(), cv.CV_POLY_APPROX_DP, 2.8) ppimg = np.zeros(mask.shape) for (x, y) in poly_seq: ppimg[y, x] = 255 imsave('/home/cplab/workspace/imageex/src/imageex/static/POLYYYAAAAA.png', ppimg) convex_hull = cv.ConvexHull2(poly_seq, cv.CreateMemStorage()) convexity_defects = cv.ConvexityDefects(poly_seq, convex_hull, cv.CreateMemStorage()) # number of defects nbDefects = len(convexity_defects) print "defects", nbDefects convexity_seq = sum([[cd[0], cd[2], cd[1]] for cd in convexity_defects], []) ppimg = np.zeros(mask.shape) for (x, y) in convexity_seq: ppimg[y, x] = 255 imsave('/home/cplab/workspace/imageex/src/imageex/static/CONVEXXAAAAA.png', ppimg) convexity_depths = np.array([cd[3] for cd in convexity_defects]) convexity_depth_max = np.max(convexity_depths) convexity_depth_min = np.min(convexity_depths) convexity_depth_ratio_min_max = convexity_depth_min / convexity_depth_max print "convexity depth ratio min max", convexity_depth_ratio_min_max #normalize convexity_depth_max = convexity_depth_max / pixels print "convexity depth max", convexity_depth_max area = cv.ContourArea(contour_seq) perimeter = cv.ArcLength(contour_seq) perimeterOarea = perimeter / area print "perimeter over area", perimeterOarea features = [] features += list(huMoments) features += dist_distri, dist_ratio_min_max, dist_max, dist_min, dist_mean, dist_std features += nbPetals, nbDefects features += convexity_depth_ratio_min_max, convexity_depth_max, perimeterOarea return features
def find_better_point(self, from_, direction, predicted_length, range=20): ''' Tries to find better corner arm - goes from from_ using vector direction on a line to find last visible point on this line @param from_: @param tpredicted_length: predicted length of this side @param direction: ''' img = self.bw_img timg = self.tmp_img L1 = predicted_length * 1.2 vec = direction L2 = length(vec) vec = add((0, 0), vec, L1 / L2) #vector towards direction of length of old side vec1 = rotateVec(vec, d2r(range)) vec2 = rotateVec(vec, d2r(-range)) x, y = from_ cv.ResetImageROI(img) size = cv.GetSize(img) border_points = [add(from_, vec1), add(from_, vec2), (x - 5, y - 5), (x + 5, y + 5)] (x, y, wx, wy) = cv.BoundingRect(border_points) crect = correct_rectangle((x - 3, y - 3, wx + 6, wy + 6), size) [cv.SetImageROI(i, crect) for i in [img, timg, self.gray_img]] self.bounds.extend(cvrect(crect)) cv.Threshold(self.gray_img, img, 125, 255, cv.CV_THRESH_OTSU) cv.Not(img, timg) cv.Canny(self.gray_img, img, 300, 500) cv.Or(img, timg,timg) rect = cvrect(crect) cv.Set2D(timg, 1, 1, (30, 30, 30)) conts = cv.FindContours(timg, cv.CreateMemStorage(), cv.CV_RETR_EXTERNAL) db.DrawContours(timg, conts, (255, 255, 255), (128, 128, 128), 10) cv.Zero(timg) fr = add(from_, rect[0], -1) ans = [] while conts: cont = cv.ApproxPoly(conts, cv.CreateMemStorage(), cv.CV_POLY_APPROX_DP, parameter=2, parameter2=0) cv.DrawContours(timg, cont, (255, 255, 255), (128, 128, 128), 10) cont = list(cont) L = len(cont) for i, p in enumerate(cont): if length(vector(fr, p)) < 5: prev = cont[(i - 1 + L) % L] next = cont[(i + 1) % L] ans.append(vector(fr, prev)) ans.append(vector(fr, next)) conts = conts.h_next() [cv.ResetImageROI(i) for i in [self.gray_img, timg, img]] if len(ans) == 0: # we didn't find corresponding corner, # that means it wasn't really a corner return None if len(ans) == 2 and ans[0] == ans[1]: return add(from_, direction) min = math.pi min_index = 0 for i, v in enumerate(ans): tmp = vectorAngle(vec, v) if tmp < min: min = tmp min_index = i ans = ans[min_index] if length(ans)+1< L2: # the point we just found is closer then the previous one return add(from_,direction) abs_point = add(from_, ans) if point_on_edge(abs_point, crect): if not point_on_edge(abs_point, (0, 0, size[0], size[1])): if range < 20: # this is recurence call. When we are here it means that # side is longer then expected by over 2 times - it is not # the side we are looking for- corner is not valid return None else: return self.find_better_point(from_, abs_point, predicted_length * 2, 5) return abs_point
def run(self): global centroid frame = cv.QueryFrame(self.capture) while not frame: cv.WaitKey(10) frame = cv.QueryFrame(self.capture) frame_size = cv.GetSize(frame) # Capture the first frame from webcam for image properties display_image = cv.QueryFrame(self.capture) while not display_image: cv.WaitKey(10) display_image = cv.QueryFrame(self.capture) # Greyscale image, thresholded to create the motion mask: grey_image = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_8U, 1) # The RunningAvg() function requires a 32-bit or 64-bit image... running_average_image = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_32F, 3) # ...but the AbsDiff() function requires matching image depths: running_average_in_display_color_depth = cv.CloneImage(display_image) # RAM used by FindContours(): mem_storage = cv.CreateMemStorage(0) # The difference between the running average and the current frame: difference = cv.CloneImage(display_image) target_count = 1 last_target_count = 1 last_target_change_t = 0.0 k_or_guess = 1 codebook = [] frame_count = 0 last_frame_entity_list = [] t0 = time.time() # For toggling display: image_list = ["camera", "difference", "threshold", "display", "faces"] image_index = 0 # Index into image_list # Prep for text drawing: text_font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, .5, .5, 0.0, 1, cv.CV_AA) text_coord = (5, 15) text_color = cv.CV_RGB(255, 255, 255) ############################### ### Face detection stuff haar_cascade = cv.Load('haarcascades/haarcascade_frontalface_alt.xml') # Set this to the max number of targets to look for (passed to k-means): max_targets = 3 while True: # Capture frame from webcam camera_image = cv.QueryFrame(self.capture) while not camera_image: cv.WaitKey(10) camera_image = cv.QueryFrame(self.capture) frame_count += 1 print 'frame_count = ', frame_count frame_t0 = time.time() # Create an image with interactive feedback: display_image = cv.CloneImage(camera_image) # Create a working "color image" to modify / blur color_image = cv.CloneImage(display_image) # Smooth to get rid of false positives cv.Smooth(color_image, color_image, cv.CV_GAUSSIAN, 19, 0) # Use the Running Average as the static background # a = 0.020 leaves artifacts lingering way too long. # a = 0.320 works well at 320x240, 15fps. (1/a is roughly num frames.) cv.RunningAvg(color_image, running_average_image, 0.320, None) # Convert the scale of the moving average. cv.ConvertScale(running_average_image, running_average_in_display_color_depth, 1.0, 0.0) # Subtract the current frame from the moving average. cv.AbsDiff(color_image, running_average_in_display_color_depth, difference) # Convert the image to greyscale. cv.CvtColor(difference, grey_image, cv.CV_RGB2GRAY) # Threshold the image to a black and white motion mask: cv.Threshold(grey_image, grey_image, 2, 255, cv.CV_THRESH_BINARY) # Smooth and threshold again to eliminate "sparkles" cv.Smooth(grey_image, grey_image, cv.CV_GAUSSIAN, 19, 0) cv.Threshold(grey_image, grey_image, 240, 255, cv.CV_THRESH_BINARY) grey_image_as_array = numpy.asarray(cv.GetMat(grey_image)) non_black_coords_array = numpy.where(grey_image_as_array > 3) # Convert from numpy.where()'s two separate lists to one list of (x, y) tuples: non_black_coords_array = zip(non_black_coords_array[1], non_black_coords_array[0]) points = [ ] # Was using this to hold either pixel coords or polygon coords. bounding_box_list = [] # Now calculate movements using the white pixels as "motion" data contour = cv.FindContours(grey_image, mem_storage, cv.CV_RETR_CCOMP, cv.CV_CHAIN_APPROX_SIMPLE) while contour: bounding_rect = cv.BoundingRect(list(contour)) point1 = (bounding_rect[0], bounding_rect[1]) point2 = (bounding_rect[0] + bounding_rect[2], bounding_rect[1] + bounding_rect[3]) bounding_box_list.append((point1, point2)) polygon_points = cv.ApproxPoly(list(contour), mem_storage, cv.CV_POLY_APPROX_DP) # To track polygon points only (instead of every pixel): #points += list(polygon_points) # Draw the contours: ###cv.DrawContours(color_image, contour, cv.CV_RGB(255,0,0), cv.CV_RGB(0,255,0), levels, 3, 0, (0,0) ) cv.FillPoly(grey_image, [ list(polygon_points), ], cv.CV_RGB(255, 255, 255), 0, 0) cv.PolyLine(display_image, [ polygon_points, ], 0, cv.CV_RGB(255, 255, 255), 1, 0, 0) #cv.Rectangle( display_image, point1, point2, cv.CV_RGB(120,120,120), 1) contour = contour.h_next() # Find the average size of the bbox (targets), then # remove any tiny bboxes (which are prolly just noise). # "Tiny" is defined as any box with 1/10th the area of the average box. # This reduces false positives on tiny "sparkles" noise. box_areas = [] for box in bounding_box_list: box_width = box[right][0] - box[left][0] box_height = box[bottom][0] - box[top][0] box_areas.append(box_width * box_height) #cv.Rectangle( display_image, box[0], box[1], cv.CV_RGB(255,0,0), 1) average_box_area = 0.0 if len(box_areas): average_box_area = float(sum(box_areas)) / len(box_areas) trimmed_box_list = [] for box in bounding_box_list: box_width = box[right][0] - box[left][0] box_height = box[bottom][0] - box[top][0] # Only keep the box if it's not a tiny noise box: if (box_width * box_height) > average_box_area * 0.1: trimmed_box_list.append(box) # Draw the trimmed box list: bounding_box_list = merge_collided_bboxes(trimmed_box_list) # Draw the merged box list: for box in bounding_box_list: cv.Rectangle(display_image, box[0], box[1], cv.CV_RGB(0, 255, 0), 1) # Here are our estimate points to track, based on merged & trimmed boxes: estimated_target_count = len(bounding_box_list) # Don't allow target "jumps" from few to many or many to few. # Only change the number of targets up to one target per n seconds. # This fixes the "exploding number of targets" when something stops moving # and the motion erodes to disparate little puddles all over the place. if frame_t0 - last_target_change_t < .350: # 1 change per 0.35 secs estimated_target_count = last_target_count else: if last_target_count - estimated_target_count > 1: estimated_target_count = last_target_count - 1 if estimated_target_count - last_target_count > 1: estimated_target_count = last_target_count + 1 last_target_change_t = frame_t0 # Clip to the user-supplied maximum: estimated_target_count = min(estimated_target_count, max_targets) # The estimated_target_count at this point is the maximum number of targets # we want to look for. If kmeans decides that one of our candidate # bboxes is not actually a target, we remove it from the target list below. # Using the numpy values directly (treating all pixels as points): points = non_black_coords_array center_points = [] if len(points): # If we have all the "target_count" targets from last frame, # use the previously known targets (for greater accuracy). k_or_guess = max(estimated_target_count, 1) # Need at least one target to look for. if len(codebook) == estimated_target_count: k_or_guess = codebook #points = vq.whiten(array( points )) # Don't do this! Ruins everything. codebook, distortion = vq.kmeans(array(points), k_or_guess) # Convert to tuples (and draw it to screen) for center_point in codebook: center_point = (int(center_point[0]), int(center_point[1])) center_points.append(center_point) #cv.Circle(display_image, center_point, 10, cv.CV_RGB(255, 0, 0), 2) #cv.Circle(display_image, center_point, 5, cv.CV_RGB(255, 0, 0), 3) # Now we have targets that are NOT computed from bboxes -- just # movement weights (according to kmeans). If any two targets are # within the same "bbox count", average them into a single target. # # (Any kmeans targets not within a bbox are also kept.) trimmed_center_points = [] removed_center_points = [] for box in bounding_box_list: # Find the centers within this box: center_points_in_box = [] for center_point in center_points: if center_point[0] < box[right][0] and center_point[0] > box[left][0] and \ center_point[1] < box[bottom][1] and center_point[1] > box[top][1] : # This point is within the box. center_points_in_box.append(center_point) # Now see if there are more than one. If so, merge them. if len(center_points_in_box) > 1: # Merge them: x_list = y_list = [] for point in center_points_in_box: x_list.append(point[0]) y_list.append(point[1]) average_x = int(float(sum(x_list)) / len(x_list)) average_y = int(float(sum(y_list)) / len(y_list)) trimmed_center_points.append((average_x, average_y)) # Record that they were removed: removed_center_points += center_points_in_box if len(center_points_in_box) == 1: trimmed_center_points.append( center_points_in_box[0]) # Just use it. # If there are any center_points not within a bbox, just use them. # (It's probably a cluster comprised of a bunch of small bboxes.) for center_point in center_points: if (not center_point in trimmed_center_points) and ( not center_point in removed_center_points): trimmed_center_points.append(center_point) # Determine if there are any new (or lost) targets: actual_target_count = len(trimmed_center_points) last_target_count = actual_target_count # Now build the list of physical entities (objects) this_frame_entity_list = [] # An entity is list: [ name, color, last_time_seen, last_known_coords ] for target in trimmed_center_points: # Is this a target near a prior entity (same physical entity)? entity_found = False entity_distance_dict = {} for entity in last_frame_entity_list: entity_coords = entity[3] delta_x = entity_coords[0] - target[0] delta_y = entity_coords[1] - target[1] distance = sqrt(pow(delta_x, 2) + pow(delta_y, 2)) entity_distance_dict[distance] = entity # Did we find any non-claimed entities (nearest to furthest): distance_list = entity_distance_dict.keys() distance_list.sort() for distance in distance_list: # Yes; see if we can claim the nearest one: nearest_possible_entity = entity_distance_dict[distance] # Don't consider entities that are already claimed: if nearest_possible_entity in this_frame_entity_list: #print "Target %s: Skipping the one iwth distance: %d at %s, C:%s" % (target, distance, nearest_possible_entity[3], nearest_possible_entity[1] ) continue #print "Target %s: USING the one iwth distance: %d at %s, C:%s" % (target, distance, nearest_possible_entity[3] , nearest_possible_entity[1]) # Found the nearest entity to claim: entity_found = True nearest_possible_entity[ 2] = frame_t0 # Update last_time_seen nearest_possible_entity[ 3] = target # Update the new location this_frame_entity_list.append(nearest_possible_entity) #log_file.write( "%.3f MOVED %s %d %d\n" % ( frame_t0, nearest_possible_entity[0], nearest_possible_entity[3][0], nearest_possible_entity[3][1] ) ) break if entity_found == False: # It's a new entity. color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) name = hashlib.md5(str(frame_t0) + str(color)).hexdigest()[:6] last_time_seen = frame_t0 new_entity = [name, color, last_time_seen, target] this_frame_entity_list.append(new_entity) #log_file.write( "%.3f FOUND %s %d %d\n" % ( frame_t0, new_entity[0], new_entity[3][0], new_entity[3][1] ) ) # Now "delete" any not-found entities which have expired: entity_ttl = 1.0 # 1 sec. for entity in last_frame_entity_list: last_time_seen = entity[2] if frame_t0 - last_time_seen > entity_ttl: # It's gone. #log_file.write( "%.3f STOPD %s %d %d\n" % ( frame_t0, entity[0], entity[3][0], entity[3][1] ) ) pass else: # Save it for next time... not expired yet: this_frame_entity_list.append(entity) # For next frame: last_frame_entity_list = this_frame_entity_list # Draw the found entities to screen: for entity in this_frame_entity_list: center_point = entity[3] c = entity[1] # RGB color tuple cv.Circle(display_image, center_point, 20, cv.CV_RGB(c[0], c[1], c[2]), 1) cv.Circle(display_image, center_point, 15, cv.CV_RGB(c[0], c[1], c[2]), 1) cv.Circle(display_image, center_point, 10, cv.CV_RGB(c[0], c[1], c[2]), 2) cv.Circle(display_image, center_point, 5, cv.CV_RGB(c[0], c[1], c[2]), 3) #print "min_size is: " + str(min_size) # Listen for ESC or ENTER key c = cv.WaitKey(7) % 0x100 if c == 27 or c == 10: break # Toggle which image to show if chr(c) == 'd': image_index = (image_index + 1) % len(image_list) image_name = image_list[image_index] image_name = "display" # for black and white: Threshold #for colored = display # Display frame to user if image_name == "camera": image = camera_image cv.PutText(image, "Camera (Normal)", text_coord, text_font, text_color) elif image_name == "difference": image = difference cv.PutText(image, "Difference Image", text_coord, text_font, text_color) elif image_name == "display": image = display_image cv.PutText(image, "Targets (w/AABBs and contours)", text_coord, text_font, text_color) elif image_name == "threshold": # Convert the image to color. cv.CvtColor(grey_image, display_image, cv.CV_GRAY2RGB) image = display_image # Re-use display image here cv.PutText(image, "Motion Mask", text_coord, text_font, text_color) elif image_name == "faces": # Do face detection detect_faces(camera_image, haar_cascade, mem_storage) image = camera_image # Re-use camera image here cv.PutText(image, "Face Detection", text_coord, text_font, text_color) cv.ShowImage("Target", image) if self.writer: cv.WriteFrame(self.writer, image) arr = numpy.asarray(image[:, :]) move_thresh = 100 counter = 0 # If only using a camera, then there is no time.sleep() needed, # because the camera clips us to 15 fps. But if reading from a file, # we need this to keep the time-based target clipping correct: frame_t1 = time.time() centroid = get_centroid(trimmed_center_points) print 'centroid = ', centroid # If reading from a file, put in a forced delay: if not self.writer: delta_t = frame_t1 - frame_t0 if delta_t < (1.0 / 15.0): time.sleep((1.0 / 15.0) - delta_t) t1 = time.time() time_delta = t1 - t0 processed_fps = float(frame_count) / time_delta print "Got %d frames. %.1f s. %f fps." % (frame_count, time_delta, processed_fps)
def run(self): frame = cv.QueryFrame(self.capture) frame_size = cv.GetSize(frame) # Capture the first frame from webcam for image properties display_image = cv.QueryFrame(self.capture) # Greyscale image, thresholded to create the motion mask: grey_image = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_8U, 1) # The RunningAvg() function requires a 32-bit or 64-bit image... running_average_image = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_32F, 3) # ...but the AbsDiff() function requires matching image depths: running_average_in_display_color_depth = cv.CloneImage(display_image) # RAM used by FindContours(): mem_storage = cv.CreateMemStorage(0) # The difference between the running average and the current frame: difference = cv.CloneImage(display_image) target_count = 1 last_target_count = 1 last_target_change_t = 0.0 k_or_guess = 1 codebook = [] frame_count = 0 last_frame_entity_list = [] t0 = time.time() # For toggling display: image_list = ["display", "difference", "threshold", "camera", "faces"] image_index = 0 # Index into image_list # Prep for text drawing: text_font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, .5, .5, 0.0, 1, cv.CV_AA) text_coord = (5, 15) text_color = cv.CV_RGB(255, 255, 255) haar_cascade = cv.Load( '/usr/local/share/OpenCV/haarcascades/haarcascade_frontalface_alt.xml' ) max_targets = 3 while True: camera_image = cv.QueryFrame(self.capture) frame_count += 1 frame_t0 = time.time() # Create an image with interactive feedback: display_image = cv.CloneImage(camera_image) # Create a working "color image" to modify / blur color_image = cv.CloneImage(display_image) # Smooth to get rid of false positives cv.Smooth(color_image, color_image, cv.CV_GAUSSIAN, 19, 0) # Use the Running Average as the static background # a = 0.020 leaves artifacts lingering way too long. # a = 0.320 works well at 320x240, 15fps. (1/a is roughly num frames.) cv.RunningAvg(color_image, running_average_image, 0.420, None) # Convert the scale of the moving average. cv.ConvertScale(running_average_image, running_average_in_display_color_depth, 1.0, 0.0) # Subtract the current frame from the moving average. cv.AbsDiff(color_image, running_average_in_display_color_depth, difference) # Convert the image to greyscale. cv.CvtColor(difference, grey_image, cv.CV_RGB2GRAY) # Threshold the image to a black and white motion mask: cv.Threshold(grey_image, grey_image, 2, 255, cv.CV_THRESH_BINARY) # Smooth and threshold again to eliminate "sparkles" cv.Smooth(grey_image, grey_image, cv.CV_GAUSSIAN, 19, 0) cv.Threshold(grey_image, grey_image, 240, 255, cv.CV_THRESH_BINARY) cv.Dilate(grey_image, grey_image, None, 18) cv.Erode(grey_image, grey_image, None, 20) grey_image_as_array = numpy.asarray(cv.GetMat(grey_image)) non_black_coords_array = numpy.where(grey_image_as_array > 3) # Convert from numpy.where()'s two separate lists to one list of (x, y) tuples: non_black_coords_array = zip(non_black_coords_array[1], non_black_coords_array[0]) points = [ ] # Was using this to hold either pixel coords or polygon coords. bounding_box_list = [] # Now calculate movements using the white pixels as "motion" data contour = cv.FindContours(grey_image, mem_storage, cv.CV_RETR_CCOMP, cv.CV_CHAIN_APPROX_SIMPLE) while contour: bounding_rect = cv.BoundingRect(list(contour)) point1 = (bounding_rect[0], bounding_rect[1]) point2 = (bounding_rect[0] + bounding_rect[2], bounding_rect[1] + bounding_rect[3]) bounding_box_list.append((point1, point2)) polygon_points = cv.ApproxPoly(list(contour), mem_storage, cv.CV_POLY_APPROX_DP) # To track polygon points only (instead of every pixel): #points += list(polygon_points) # Draw the contours: levels = 0 cv.DrawContours(color_image, contour, cv.CV_RGB(255, 0, 0), cv.CV_RGB(0, 255, 0), levels, 3, 0, (0, 0)) cv.FillPoly(grey_image, [ list(polygon_points), ], cv.CV_RGB(255, 255, 255), 0, 0) cv.PolyLine(display_image, [ polygon_points, ], 0, cv.CV_RGB(255, 255, 255), 1, 0, 0) #cv.Rectangle( display_image, point1, point2, cv.CV_RGB(120,120,120), 1) contour = contour.h_next() # Find the average size of the bbox (targets), then # remove any tiny bboxes (which are prolly just noise). # "Tiny" is defined as any box with 1/10th the area of the average box. # This reduces false positives on tiny "sparkles" noise. box_areas = [] for box in bounding_box_list: box_width = box[right][0] - box[left][0] box_height = box[bottom][0] - box[top][0] box_areas.append(box_width * box_height) #cv.Rectangle( display_image, box[0], box[1], cv.CV_RGB(255,0,0), 1) average_box_area = 0.0 if len(box_areas): average_box_area = float(sum(box_areas)) / len(box_areas) trimmed_box_list = [] for box in bounding_box_list: box_width = box[right][0] - box[left][0] box_height = box[bottom][0] - box[top][0] # Only keep the box if it's not a tiny noise box: if (box_width * box_height) > average_box_area * 0.1: trimmed_box_list.append(box) # Draw the trimmed box list: #for box in trimmed_box_list: # cv.Rectangle( display_image, box[0], box[1], cv.CV_RGB(0,255,0), 2 ) bounding_box_list = merge_collided_bboxes(trimmed_box_list) # Draw the merged box list: for box in bounding_box_list: cv.Rectangle(display_image, box[0], box[1], cv.CV_RGB(0, 255, 0), 1) # Here are our estimate points to track, based on merged & trimmed boxes: estimated_target_count = len(bounding_box_list) if frame_t0 - last_target_change_t < .650: # 1 change per 0.35 secs estimated_target_count = last_target_count else: if last_target_count - estimated_target_count > 1: estimated_target_count = last_target_count - 1 if estimated_target_count - last_target_count > 1: estimated_target_count = last_target_count + 1 last_target_change_t = frame_t0 # Clip to the user-supplied maximum: estimated_target_count = min(estimated_target_count, max_targets) points = non_black_coords_array center_points = [] if len(points): k_or_guess = max(estimated_target_count, 1) # Need at least one target to look for. if len(codebook) == estimated_target_count: k_or_guess = codebook #points = vq.whiten(array( points )) # Don't do this! Ruins everything. codebook, distortion = vq.kmeans(array(points), k_or_guess) # Convert to tuples (and draw it to screen) for center_point in codebook: center_point = (int(center_point[0]), int(center_point[1])) center_points.append(center_point) trimmed_center_points = [] removed_center_points = [] for box in bounding_box_list: # Find the centers within this box: center_points_in_box = [] for center_point in center_points: if center_point[0] < box[right][0] and center_point[0] > box[left][0] and \ center_point[1] < box[bottom][1] and center_point[1] > box[top][1] : # This point is within the box. center_points_in_box.append(center_point) # Now see if there are more than one. If so, merge them. if len(center_points_in_box) > 1: # Merge them: x_list = y_list = [] for point in center_points_in_box: x_list.append(point[0]) y_list.append(point[1]) average_x = int(float(sum(x_list)) / len(x_list)) average_y = int(float(sum(y_list)) / len(y_list)) trimmed_center_points.append((average_x, average_y)) # Record that they were removed: removed_center_points += center_points_in_box if len(center_points_in_box) == 1: trimmed_center_points.append( center_points_in_box[0]) # Just use it. # If there are any center_points not within a bbox, just use them. # (It's probably a cluster comprised of a bunch of small bboxes.) for center_point in center_points: if (not center_point in trimmed_center_points) and ( not center_point in removed_center_points): trimmed_center_points.append(center_point) # Determine if there are any new (or lost) targets: actual_target_count = len(trimmed_center_points) last_target_count = actual_target_count # Now build the list of physical entities (objects) this_frame_entity_list = [] # An entity is list: [ name, color, last_time_seen, last_known_coords ] for target in trimmed_center_points: # Is this a target near a prior entity (same physical entity)? entity_found = False entity_distance_dict = {} for entity in last_frame_entity_list: entity_coords = entity[3] delta_x = entity_coords[0] - target[0] delta_y = entity_coords[1] - target[1] distance = sqrt(pow(delta_x, 2) + pow(delta_y, 2)) entity_distance_dict[distance] = entity # Did we find any non-claimed entities (nearest to furthest): distance_list = entity_distance_dict.keys() distance_list.sort() for distance in distance_list: # Yes; see if we can claim the nearest one: nearest_possible_entity = entity_distance_dict[distance] if nearest_possible_entity in this_frame_entity_list: continue # Found the nearest entity to claim: entity_found = True nearest_possible_entity[ 2] = frame_t0 # Update last_time_seen nearest_possible_entity[ 3] = target # Update the new location this_frame_entity_list.append(nearest_possible_entity) break if entity_found == False: # It's a new entity. color = (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) name = hashlib.md5(str(frame_t0) + str(color)).hexdigest()[:6] last_time_seen = frame_t0 new_entity = [name, color, last_time_seen, target] this_frame_entity_list.append(new_entity) # Now "delete" any not-found entities which have expired: entity_ttl = 1.0 # 1 sec. ent_count = 0 for entity in last_frame_entity_list: last_time_seen = entity[2] if frame_t0 - last_time_seen > entity_ttl: pass else: # Save it for next time... not expired yet: this_frame_entity_list.append(entity) ent_count += 1 # For next frame: last_frame_entity_list = this_frame_entity_list # Draw the found entities to screen: count = 0 if ent_count != 0: entity = this_frame_entity_list[0] center_point = entity[3] c = entity[1] # RGB color tuple # print '%s %d %d %d' % (entity[0], count, center_point[0], center_point[1]) cv.Circle(display_image, center_point, 20, cv.CV_RGB(c[0], c[1], c[2]), 1) cv.Circle(display_image, center_point, 15, cv.CV_RGB(c[0], c[1], c[2]), 1) cv.Circle(display_image, center_point, 10, cv.CV_RGB(c[0], c[1], c[2]), 2) cv.Circle(display_image, center_point, 5, cv.CV_RGB(c[0], c[1], c[2]), 3) text_font = cv.InitFont(cv.CV_FONT_HERSHEY_COMPLEX, .5, .5, 0.0, 1, cv.CV_AA) text_coord = (5, 15) text_color = cv.CV_RGB(255, 255, 255) x = 50 + (center_point[0] * 80 / 320) y = 20 + (center_point[1] * 80 / 240) if self.have_eye: self.ServoMove(0, int(x)) self.ServoMove(1, int(y)) s = '%3.0d %3.0d' % (x, y) cv.PutText(display_image, str(s), text_coord, text_font, text_color) #print "min_size is: " + str(min_size) # Listen for ESC or ENTER key c = cv.WaitKey(7) % 0x100 if c == 27 or c == 10: break # Toggle which image to show if chr(c) == 'd': image_index = (image_index + 1) % len(image_list) image_name = image_list[image_index] # Display frame to user if image_name == "display": image = display_image # cv.PutText( image, "AABBs and contours", text_coord, text_font, text_color ) elif image_name == "camera": image = camera_image cv.PutText(image, "No overlay", text_coord, text_font, text_color) elif image_name == "difference": image = difference cv.PutText(image, "Difference Image", text_coord, text_font, text_color) elif image_name == "faces": # Do face detection detect_faces(camera_image, haar_cascade, mem_storage) image = camera_image # Re-use camera image here cv.PutText(image, "Face Detection", text_coord, text_font, text_color) elif image_name == "threshold": # Convert the image to color. cv.CvtColor(grey_image, display_image, cv.CV_GRAY2RGB) image = display_image # Re-use display image here cv.PutText(image, "Motion Mask", text_coord, text_font, text_color) cv.ShowImage("Target", image) if self.writer: cv.WriteFrame(self.writer, image) frame_t1 = time.time() t1 = time.time() time_delta = t1 - t0 processed_fps = float(frame_count) / time_delta print "Got %d frames. %.1f s. %f fps." % (frame_count, time_delta, processed_fps)
def approx_poly(self): self.contours = cv.ApproxPoly(self.contours, self.storage, cv.CV_POLY_APPROX_DP, 3, 1) return self
def detect_motion(self, sensitivity='medium'): #Finding Video Size from the first frame frame = cv.QueryFrame(self.video_handle) frame_size = cv.GetSize(frame) '''Initializing Image Variables(to be used in motion detection) with required types and sizes''' # Image containg instantaneous moving rectangles color_image = cv.CreateImage(frame_size, 8, 3) # Resizing to window size color_output = cv.CreateImage(self.window_size, 8, 3) # Grey Image used for contour detection grey_image = cv.CreateImage(frame_size, cv.IPL_DEPTH_8U, 1) # Image storing background (moving pixels are averaged over small time window) moving_average = cv.CreateImage(frame_size, cv.IPL_DEPTH_32F, 3) # Image for storing tracks resized to window size track_output = cv.CreateImage(self.window_size, cv.IPL_DEPTH_8U, 3) track_image, track_win = self.init_track_window(frame) def totuple(a): try: return tuple(totuple(i) for i in a) except TypeError: return a first = True # Infinite loop for continuous detection of motion while True: '''########## Pixelwise Detection of Motion in a frame ###########''' # Capturing Frame color_image = cv.QueryFrame(self.video_handle) ##### Sensitivity Control 1 ##### if (sensitivity == 'medium') or (sensitivity == 'low'): # Gaussian Smoothing cv.Smooth(color_image, color_image, cv.CV_GAUSSIAN, 3, 0) if first: difference = cv.CloneImage(color_image) temp = cv.CloneImage(color_image) cv.ConvertScale(color_image, moving_average, 1.0, 0.0) first = False else: cv.RunningAvg(color_image, moving_average, .020, None) # Convert the scale of the moving average. cv.ConvertScale(moving_average, temp, 1, 0.0) # Minus the current frame from the moving average. cv.AbsDiff(color_image, temp, difference) #cv.ShowImage("BG",difference) # Convert the image to grayscale. cv.CvtColor(difference, grey_image, cv.CV_RGB2GRAY) ##### Sensitivity Control 2 ##### sens_thres = 90 if (sensitivity == 'low') or (self.opt == 'cam') else 40 # Convert the image to black and white. cv.Threshold(grey_image, grey_image, sens_thres, 255, cv.CV_THRESH_BINARY) '''### Blobing moved adjacent pixels, finding closed contours and bounding rectangles ###''' ##### Sensitivity Control 3 ##### if (sensitivity == 'medium') or (sensitivity == 'low'): # Dilate and erode to get people blobs ker_size = 20 if self.opt == 'file' else 50 cv.Dilate(grey_image, grey_image, None, ker_size) cv.Erode(grey_image, grey_image, None, 3) storage = cv.CreateMemStorage(0) contour = cv.FindContours(grey_image, storage, cv.CV_RETR_CCOMP, cv.CV_CHAIN_APPROX_SIMPLE) points = [] while contour: bound_rect = cv.BoundingRect(list(contour)) polygon_points = cv.ApproxPoly(list(contour), storage, cv.CV_POLY_APPROX_DP) pt1 = (bound_rect[0], bound_rect[1]) pt2 = (bound_rect[0] + bound_rect[2], bound_rect[1] + bound_rect[3]) if (self.opt == 'file'): points.append(pt1) points.append(pt2) elif (bound_rect[0] - bound_rect[2] > 20) and (bound_rect[1] - bound_rect[3] > 20): points.append(pt1) points.append(pt2) box = cv.MinAreaRect2(polygon_points) box2 = cv.BoxPoints(box) box3 = np.int0(np.around(box2)) box4 = totuple(box3) box5 = box4 + (box4[0], ) # Filling the contours in the greyscale image (visual blobs instead of just contours) cv.FillPoly(grey_image, [ list(polygon_points), ], cv.CV_RGB(255, 255, 255), 0, 0) # Following line to draw detected contours as well #cv.PolyLine( color_image, [ polygon_points, ], 0, cv.CV_RGB(255,0,0), 1, 0, 0 ) # Drawing Rectangle around the detected contour cv.PolyLine(color_image, [list(box5)], 0, (0, 255, 255), 2) if len(points): # (self.opt == 'file') and center1 = (pt1[0] + pt2[0]) / 2 center2 = (pt1[1] + pt2[1]) / 2 cv.Circle(color_image, (center1, center2), 5, cv.CV_RGB(0, 255, 0), -1) rad = 3 if self.opt == 'file' else 5 cv.Circle(track_image, (center1, center2), rad, cv.CV_RGB(255, 128, 0), -1) contour = contour.h_next() # Uncomment to track centroid of all the moved boxes (only for WebCam) ''' if (self.opt == 'cam') and len(points): center_point = reduce(lambda a, b: ((a[0] + b[0]) / 2, (a[1] + b[1]) / 2), points) cv.Circle(track_image, center_point, 15, cv.CV_RGB(255, 128, 0), -1) ''' cv.Resize(color_image, color_output, cv.CV_INTER_AREA) cv.ShowImage("Original", color_output) cv.Resize(track_image, track_output, cv.CV_INTER_AREA) cv.ShowImage(track_win, track_output) # Listen for ESC key c = cv.WaitKey(7) % 0x100 if (0xFF & c == 27): cv.SaveImage('Tracks_img_042_' + sensitivity + '.jpeg', track_output) break
cv.Ellipse(image, (dx + 27, dy + 100), (20, 35), 0, 0, 360, _white, -1, 8, 0) cv.Ellipse(image, (dx + 273, dy + 100), (20, 35), 0, 0, 360, _white, -1, 8, 0) # create window and display the original picture in it cv.NamedWindow("image", 1) cv.ShowImage("image", image) # create the storage area storage = cv.CreateMemStorage(0) # find the contours contours = cv.FindContours(image, storage, cv.CV_RETR_TREE, cv.CV_CHAIN_APPROX_SIMPLE, (0, 0)) # comment this out if you do not want approximation contours = cv.ApproxPoly(contours, storage, cv.CV_POLY_APPROX_DP, 3, 1) # create the window for the contours cv.NamedWindow("contours", 1) # create the trackbar, to enable the change of the displayed level cv.CreateTrackbar("levels+3", "contours", 3, 7, on_trackbar) # call one time the callback, so we will have the 1st display done on_trackbar(_DEFAULT_LEVEL) # wait a key pressed to end cv.WaitKey(0)
def findSquares4(img, storage): N = 11 sz = (img.width & -2, img.height & -2) timg = cv.CloneImage(img) # make a copy of input image gray = cv.CreateImage(sz, 8, 1) pyr = cv.CreateImage((sz.width / 2, sz.height / 2), 8, 3) # create empty sequence that will contain points - # 4 points per square (the square's vertices) squares = cv.CreateSeq(0, sizeof_CvSeq, sizeof_CvPoint, storage) squares = CvSeq_CvPoint.cast(squares) # select the maximum ROI in the image # with the width and height divisible by 2 subimage = cv.GetSubRect(timg, cv.Rect(0, 0, sz.width, sz.height)) # down-scale and upscale the image to filter out the noise cv.PyrDown(subimage, pyr, 7) cv.PyrUp(pyr, subimage, 7) tgray = cv.CreateImage(sz, 8, 1) # find squares in every color plane of the image for c in range(3): # extract the c-th color plane channels = [None, None, None] channels[c] = tgray cv.Split(subimage, channels[0], channels[1], channels[2], None) for l in range(N): # hack: use Canny instead of zero threshold level. # Canny helps to catch squares with gradient shading if (l == 0): # apply Canny. Take the upper threshold from slider # and set the lower to 0 (which forces edges merging) cv.Canny(tgray, gray, 0, thresh, 5) # dilate canny output to remove potential # holes between edge segments cv.Dilate(gray, gray, None, 1) else: # apply threshold if l!=0: # tgray(x, y) = gray(x, y) < (l+1)*255/N ? 255 : 0 cv.Threshold(tgray, gray, (l + 1) * 255 / N, 255, cv.CV_THRESH_BINARY) # find contours and store them all as a list count, contours = cv.FindContours(gray, storage, sizeof_CvContour, cv.CV_RETR_LIST, cv.CV_CHAIN_APPROX_SIMPLE, (0, 0)) if not contours: continue # test each contour for contour in contours.hrange(): # approximate contour with accuracy proportional # to the contour perimeter result = cv.ApproxPoly(contour, sizeof_CvContour, storage, cv.CV_POLY_APPROX_DP, cv.ContourPerimeter(contours) * 0.02, 0) # square contours should have 4 vertices after approximation # relatively large area (to filter out noisy contours) # and be convex. # Note: absolute value of an area is used because # area may be positive or negative - in accordance with the # contour orientation if (result.total == 4 and abs(cv.ContourArea(result)) > 1000 and cv.CheckContourConvexity(result)): s = 0 for i in range(5): # find minimum angle between joint # edges (maximum of cosine) if (i >= 2): t = abs( angle(result[i], result[i - 2], result[i - 1])) if s < t: s = t # if cosines of all angles are small # (all angles are ~90 degree) then write quandrange # vertices to resultant sequence if (s < 0.3): for i in range(4): squares.append(result[i]) return squares