Пример #1
0
    def add(self, item):
        if self.depth < 8 and self.ne == None:
            cx = self.bbox.center.x
            cy = self.bbox.center.y

            self.ne = QuadNode(
                self.depth + 1,
                BoundingBox(cx, cy, self.bbox.maxX, self.bbox.maxY))
            self.se = QuadNode(
                self.depth + 1,
                BoundingBox(cx, self.bbox.minY, self.bbox.maxX, cy))
            self.sw = QuadNode(
                self.depth + 1,
                BoundingBox(self.bbox.minX, self.bbox.minY, cx, cy))
            self.nw = QuadNode(
                self.depth + 1,
                BoundingBox(self.bbox.minX, cy, cx, self.bbox.maxY))

        if self.depth == 8:
            self.items.append(item)
            return

        if self.ne.bbox.overlaps(item.bbox):
            self.ne.add(item)

        if self.se.bbox.overlaps(item.bbox):
            self.se.add(item)

        if self.sw.bbox.overlaps(item.bbox):
            self.sw.add(item)

        if self.nw.bbox.overlaps(item.bbox):
            self.nw.add(item)
Пример #2
0
def get_bb(fp, frame, toolName):
    undo_pos = fp.tell()
    line = fp.readline()

    # create default invalid bbox (aibu format for no bbx frames)
    no_annot_bb = BoundingBox("", "", frame)

    # aibu
    if toolName == config.Tool.AIBU:
        # no need for line check
        return BoundingBox(line, toolName, frame)

    # darklabel
    if toolName == config.Tool.DARKLABEL:
        parts = line.split(",")
        if parts[0] == str(frame):
            return BoundingBox(line, toolName)

    # vatic
    if toolName == config.Tool.VATIC:
        if "<annotation>" in line:
            undo_pos = fp.tell()
            line = fp.readline()
        if "<object>" in line:
            undo_pos = fp.tell()
            line = fp.readline()
        if "<t>" + str(frame) + "</t>" in line:
            return BoundingBox(line, toolName)
    # wrong framenumber or other error
    fp.seek(undo_pos)  # undo last read
    return no_annot_bb
Пример #3
0
    def _extract_bounding_boxes(self, gt, ignore_grade: bool=False):
        boundingBoxes = BoundingBoxes()
        # gt boxes
        for image_id in gt.images:

            for x_min, y_min, x_max, y_max, class_id in gt.images[image_id].labels:
                if ignore_grade:
                    class_id = 1

                temp = BoundingBox(imageName=image_id, classId=class_id, x=max(x_min, 0),
                                   y=max(y_min, 0), w=min(256, x_max - x_min), h=min(256, y_max - y_min),
                                   typeCoordinates=CoordinatesType.Absolute,
                                   bbType=BBType.GroundTruth, format=BBFormat.XYWH,
                                   imgSize=(256, 256))
                boundingBoxes.addBoundingBox(temp)

            for x_min, y_min, x_max, y_max, class_id in self.images[image_id].labels:
                if ignore_grade:
                    class_id = 1

                temp = BoundingBox(imageName=image_id, classId=class_id, x=max(x_min, 0),
                                   y=max(y_min, 0), w=min(256, x_max - x_min), h=min(256, y_max - y_min),
                                   typeCoordinates=CoordinatesType.Absolute, classConfidence=1,
                                   bbType=BBType.Detected, format=BBFormat.XYWH, imgSize=(256, 256))
                boundingBoxes.addBoundingBox(temp)
        return boundingBoxes
Пример #4
0
def get_all_bbox(gt_json, det_json):
    allBoundingBoxes = BoundingBoxes()
    det = json.load(open(det_json,'r'))
    for k, v in det.items():
        nameOfImage = k
        for obj in v:
            idClass = obj[5]  # class
            confidence = float(obj[4])  # confidence
            x1 = float(obj[0])
            y1 = float(obj[1])
            x2 = float(obj[2])
            y2 = float(obj[3])
            bb = BoundingBox(nameOfImage, idClass, x1, y1, x2, y2, bbType=BBType.Detected,
                             classConfidence=confidence, format=BBFormat.XYX2Y2)
            allBoundingBoxes.addBoundingBox(bb)

    gt = pd.read_json(gt_json, lines=True)
    for i in range(gt.shape[0]):
        url = gt.loc[i]['url']
        filename = url.split('/')[-1]
        if filename in imgs:
            if len(gt.loc[i]['label'][0]['data']) > 0:
                    for ix, data in enumerate(gt.loc[i]['label'][0]['data']):
                        x1, y1 = data['bbox'][0]
                        x2, y2 = data['bbox'][2]
                        idClass = data['class']
                        bb = BoundingBox(filename, idClass,x1,y1,x2,y2, bbType=BBType.GroundTruth,
                                         classConfidence=confidence, format=BBFormat.XYX2Y2)
                        allBoundingBoxes.addBoundingBox(bb)

    return allBoundingBoxes
Пример #5
0
def default_value(nameOfImage, idClass, coordType, imgSize, isGT, bbFormat):
    idClass = '0'  # class
    x = 0  #x_topleft
    y = 0  #y_topleft
    w = 0
    h = 0
    confidence = 0
    if isGT:
        bb = BoundingBox(nameOfImage,
                         idClass,
                         x,
                         y,
                         w,
                         h,
                         coordType,
                         imgSize,
                         BBType.GroundTruth,
                         format=bbFormat)
    else:
        bb = BoundingBox(nameOfImage,
                         idClass,
                         x,
                         y,
                         w,
                         h,
                         coordType,
                         imgSize,
                         BBType.Detected,
                         confidence,
                         format=bbFormat)
    return bb
Пример #6
0
    def testGetBoundingBox(self):

        validFile = 'tests/WLBG-geog.csv'
        presPts = PresencePoints(validFile, 'WLBG')
        bbox = BoundingBox(presPts.points, presPts.epsg)

        expected = (-76.8933333297525, -76.8579444453814, 39.3381111142888,
                    39.4326111107889)
        self.assertEqual(expected, bbox.getBoundingBox())
Пример #7
0
def test_intersection():
    ridx = RTreeIndex()
    ridx.insert(Point(10, 10))
    ridx.insert(Point(100, 100))

    q = ridx.intersection(BoundingBox(0, 0, 100, 100))
    print(q)

    q = ridx.intersection(BoundingBox(0, 0, 90, 90))
    print(q)
Пример #8
0
    def __init__(self, depth=0, bbox=None):
        self.ne = self.se = self.sw = self.nw = None
        self.depth = depth

        if depth == 0:
            # Let's say our world can only be a third of the maximal float value
            max = 1500  #sys.float_info.max/3
            self.bbox = BoundingBox(-max, -max, max, max)
        else:
            if not bbox:
                raise ValueError("No bounding box given")
            self.bbox = bbox

        self.items = []
Пример #9
0
def getBoundingBoxes(dets,
                     isGT,
                     bbFormat,
                     coordType,
                     allBoundingBoxes=None,
                     allClasses=None,
                     imgSize=(0, 0)):
    """Read txt files containing bounding boxes (ground truth and detections)."""
    if allBoundingBoxes is None:
        allBoundingBoxes = BoundingBoxes()
    if allClasses is None:
        allClasses = []

    # Read GT detections from txt file
    # Each line of the files in the groundtruths folder represents a ground truth bounding box
    # (bounding boxes that a detector should detect)
    # Each value of each line is  "class_id, x, y, width, height" respectively
    # Class_id represents the class of the bounding box
    # x, y represents the most top-left coordinates of the bounding box
    # x2, y2 represents the most bottom-right coordinates of the bounding box
    #nameOfImage = dets[0]

    for i in range(len(dets)):
        dets1 = dets[i]
        nameOfImage = dets1[0]

        if isGT:
            # idClass = int(splitLine[0]) #class
            idClass = 'person'  # class
            x = dets1[1]
            y = dets1[2]
            w = dets1[3]
            h = dets1[4]
            bb = BoundingBox(nameOfImage, idClass, x, y, w, h, coordType, imgSize, BBType.GroundTruth, format=bbFormat)
        else:
            # idClass = int(splitLine[0]) #class
            idClass = 'person'  # class
            confidence = dets1[2]
            x = dets1[3]
            y = dets1[4]
            w = dets1[5]
            h = dets1[6]
            bb = BoundingBox(nameOfImage, idClass, x, y, w, h, coordType, imgSize, BBType.Detected, confidence, format=bbFormat)
        allBoundingBoxes.addBoundingBox(bb)
        if idClass not in allClasses:
            allClasses.append(idClass)

    return allBoundingBoxes, allClasses
Пример #10
0
def getBoundingBoxes(directory, isGT, bbFormat, allBoundingBoxes=None, allClasses=None):
    """Read txt files containing bounding boxes (ground truth and detections)."""
    if allBoundingBoxes == None:
        allBoundingBoxes = BoundingBoxes()
    if allClasses == None:
        allClasses = []
    # Read ground truths
    os.chdir(directory)
    files = glob.glob("*.txt")
    files.sort()
    # Read GT detections from txt file
    # Each line of the files in the groundtruths folder represents a ground truth bounding box (bounding boxes that a detector should detect)
    # Each value of each line is  "class_id, x, y, width, height" respectively
    # Class_id represents the class of the bounding box
    # x, y represents the most top-left coordinates of the bounding box
    # x2, y2 represents the most bottom-right coordinates of the bounding box
    for f in files:
        nameOfImage = f.replace(".txt", "")
        fh1 = open(f, "r")
        for line in fh1:
            line = line.replace("\n", "")
            if line.replace(' ', '') == '':
                continue
            splitLine = line.split(" ")
            if isGT:
                # idClass = int(splitLine[0]) #class
                idClass = (splitLine[0])  # class
                x = float(splitLine[1])
                y = float(splitLine[2])
                w = float(splitLine[3])
                h = float(splitLine[4])
                bb = BoundingBox(nameOfImage, idClass, x, y, w, h, CoordinatesType.Absolute, (0, 0), BBType.GroundTruth,
                                 format=bbFormat)
            else:
                # idClass = int(splitLine[0]) #class
                idClass = (splitLine[0])  # class
                confidence = float(splitLine[1])
                x = float(splitLine[2])
                y = float(splitLine[3])
                w = float(splitLine[4])
                h = float(splitLine[5])
                bb = BoundingBox(nameOfImage, idClass, x, y, w, h, CoordinatesType.Absolute, (0, 0), BBType.Detected,
                                 confidence, format=bbFormat)
            allBoundingBoxes.addBoundingBox(bb)
            if idClass not in allClasses:
                allClasses.append(idClass)
        fh1.close()
    return allBoundingBoxes, allClasses
Пример #11
0
class mAPTest(unittest.TestCase):

    def setUp(self):
        self.bb1 = BoundingBox(0, 50, 50, 100)
        self.bb2 = BoundingBox(30, 50, 40, 80)

    def test1(self):
        a = np.full_like(np.arange(10, dtype=np.double), 0.1)
        self.assertEqual({0.5: 0.1, 1: 0.1}, getThreshold(a, 10, 2))

    def test2(self):
        a = np.asarray(range(1,101)) / 100.0
        b = np.asarray((range(1,11))) / 10.0
        c = {(v+1)/10.0: k for v, k in enumerate(b)}
        self.assertEquals(c, getThreshold(a, 100, 10))

    def testOverlapX(self):
        bb1 = BoundingBox(0, 50, 0, 50)
        bb2 = BoundingBox(30, 80, 0, 50)
        self.assertEquals(20, bb1.x_overlap(bb2))


    def testOverlapY1(self):
        bb1 = BoundingBox(0, 50, 0, 50)
        bb2 = BoundingBox(30, 80, 0, 50)
        self.assertEquals(50, bb1.y_overlap(bb2))


    def testOverlapY2(self):
        bb1 = BoundingBox(0, 50, 50, 100)
        bb2 = BoundingBox(30, 80, 90, 105)
        self.assertEquals(10, bb1.y_overlap(bb2))

    def testOverlapX2(self):
        self.assertEqual(20, self.bb1.x_overlap(self.bb2))

    def testOverlapY3(self):
        self.assertEqual(30, self.bb1.y_overlap(self.bb2))

    def testIntersection(self):
        self.assertEqual(600, self.bb1.intersection(self.bb2))

    def testUnion(self):
        self.assertEqual(2700, self.bb1.union(self.bb2))


    def testIOU(self):
        self.assertEqual((600./2700), self.bb1.iou(self.bb2))
Пример #12
0
def computeCropPadImageLocation(bbox_tight, image):
    """TODO: Docstring for computeCropPadImageLocation.
	:returns: TODO
	"""

    # Center of the bounding box
    bbox_center_x = bbox_tight.get_center_x()
    bbox_center_y = bbox_tight.get_center_y()

    image_height = image.shape[0]
    image_width = image.shape[1]

    # Padded output width and height
    output_width = bbox_tight.compute_output_width()
    output_height = bbox_tight.compute_output_height()

    roi_left = max(0.0, bbox_center_x - (output_width / 2.))
    roi_bottom = max(0.0, bbox_center_y - (output_height / 2.))

    # Padded roi width
    left_half = min(output_width / 2., bbox_center_x)
    right_half = min(output_width / 2., image_width - bbox_center_x)
    roi_width = max(1.0, left_half + right_half)

    # Padded roi height
    top_half = min(output_height / 2., bbox_center_y)
    bottom_half = min(output_height / 2., image_height - bbox_center_y)
    roi_height = max(1.0, top_half + bottom_half)

    # Padded image location in the original image
    objPadImageLocation = BoundingBox(roi_left, roi_bottom,
                                      roi_left + roi_width,
                                      roi_bottom + roi_height)

    return objPadImageLocation
Пример #13
0
    def filter(self, frame):
        current = self.preprocess_frame(frame)

        if self.background_age > self.max_background_age:
            self.background_frame = current
            self.background_age = 0
            if self.last_frame is not None:
                current = self.last_frame  # prevent comparing background with itself

        self.last_frame = current
        self.background_age += 1

        frame_delta = cv2.absdiff(self.background_frame, current)
        # show(frame_delta, 'delta')
        result = cv2.threshold(frame_delta, 30, 255, cv2.THRESH_BINARY)[1]
        # show(result, 'threshold')

        result = cv2.dilate(result, None, iterations=20)
        # show(result, 'dilated')
        img, contours, _ = cv2.findContours(result.copy(), cv2.RETR_EXTERNAL,
                                            cv2.CHAIN_APPROX_SIMPLE)

        bounding_boxes = []
        for contour in contours:
            if self.min_contour_area < cv2.contourArea(
                    contour) < self.max_contour_area:
                bounding_boxes.append(BoundingBox(*cv2.boundingRect(contour)))
                # cv2.rectangle(frame, (x, y), (x + w, y + h), (0, 0, 255), 2)

        return result, bounding_boxes
Пример #14
0
 def subdivide(self):
     #print "self:",self.bbox
     l = self.bbox.ul.x
     r = self.bbox.lr.x
     t = self.bbox.ul.y
     b = self.bbox.lr.y
     mX = (l + r) / 2
     mY = (t + b) / 2
     self.northEast = QuadTree(BoundingBox(Point(mX, t), Point(r, mY)),
                               self.maxPoints)
     self.southEast = QuadTree(BoundingBox(Point(mX, mY), Point(r, b)),
                               self.maxPoints)
     self.southWest = QuadTree(BoundingBox(Point(l, mY), Point(mX, b)),
                               self.maxPoints)
     self.northWest = QuadTree(BoundingBox(Point(l, t), Point(mX, mY)),
                               self.maxPoints)
Пример #15
0
def createEvalBBoxes(imgNames):
    gtBBoxes = BoundingBoxes()
    for name in imgNames:
        path = os.path.join('../VOCdevkit/VOC2007/Annotations', name + '.xml')
        root = ET.parse(path).getroot()
        height = _findNode(root.find('size'), 'height', parse=int)
        width = _findNode(root.find('size'), 'width', parse=int)
        for element in root.iter('object'):
            box, class_name, difficult = parseBBoxes(element)
            if difficult:
                continue
            gtBBoxes.addBoundingBox(
                BoundingBox(imageName=name,
                            classId=class_name,
                            x=box[0, 0],
                            y=box[0, 1],
                            w=box[0, 2],
                            h=box[0, 3],
                            typeCoordinates=CoordinatesType.Absolute,
                            bbType=BBType.GroundTruth,
                            format=BBFormat.XYX2Y2,
                            imgSize=(width, height)))
    with open('validationBBoxes.pickle', 'wb') as f:
        pickle.dump(gtBBoxes, f)
    print('create EvalBBoxes over!')
Пример #16
0
    def testInit(self):

        print 'testInit ...'

        validFile = 'tests/WLBG-geog.csv'
        presPts = PresencePoints(validFile, 'WLBG')
        bbox = BoundingBox(presPts.points, presPts.epsg)
Пример #17
0
def get_bounding_boxes_from_xml(xml_path, coordType, bbFormat,
                                allBoundingBoxes, allClasses, isGT):
    assert coordType == CoordinatesType.Absolute
    assert bbFormat == BBFormat.XYX2Y2
    assert isGT is True

    tree = ET.parse(xml_path)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)
    nameOfImage = xml_path.replace(".xml", "")

    for obj in root.iter('object'):
        difficult = obj.find('difficult').text
        cls_name = obj.find('name').text
        if int(difficult) == 1:
            continue
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text),
             float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text))
        bb = BoundingBox(nameOfImage,
                         cls_name,
                         float(xmlbox.find('xmin').text),
                         float(xmlbox.find('ymin').text),
                         float(xmlbox.find('xmax').text),
                         float(xmlbox.find('ymax').text),
                         coordType, (w, h),
                         BBType.GroundTruth,
                         format=bbFormat)
        allBoundingBoxes.addBoundingBox(bb)
        if cls_name not in allClasses:
            allClasses.append(cls_name)
Пример #18
0
 def setup(self):
     self.q = QuadTree(
         BoundingBox(Point(0, 0), Point(self.width, self.height)), 3)
     self.points = []
     self.colors = [
         "#000", "#F00", "#0f0", "#00f", "#ff0", "#0ff", "#0f0"
     ]
Пример #19
0
    def get_videos(self):
        """Docstring for get_videos.

        :returns: returns video frames in each sub folder of vot directory

        """

        vot_folder = self.vot_folder
        sub_vot_dirs = self.find_subfolders(vot_folder)
        for vot_sub_dir in sub_vot_dirs:
            list_of_frames = sorted(
                glob.glob(os.path.join(vot_folder, vot_sub_dir, '*.jpg')))
            if not list_of_frames:
                logger.error('vot folders should contain only .jpg images')

            self.videos[vot_sub_dir] = list_of_frames

            bbox_gt_file = os.path.join(vot_folder, vot_sub_dir,
                                        'groundtruth.txt')
            bbox_gt_coords = []
            with open(bbox_gt_file, 'r') as f:
                for line in f:
                    co_ords = line.strip().split(',')
                    co_ords = [int(float(co_ord)) for co_ord in co_ords]
                    ax, ay, bx, by, cx, cy, dx, dy = co_ords
                    x1 = min(ax, min(bx, min(cx, dx))) - 1
                    y1 = min(ay, min(by, min(cy, dy))) - 1
                    x2 = max(ax, max(bx, max(cx, dx))) - 1
                    y2 = max(ay, max(by, max(cy, dy))) - 1
                    bbox = BoundingBox(x1, y1, x2, y2)
                    bbox_gt_coords.append(bbox)
            self.annotations[vot_sub_dir] = bbox_gt_coords

        return self.videos, self.annotations
Пример #20
0
 def view(self, window):
     fov = 0.14  # Hack: Roughly the factor needed to account for the FOV
     w = window.width * fov
     h = window.height * fov
     bbox = BoundingBox(self.location.x - w / 2, self.location.y - h / 2,
                        self.location.x + w / 2, self.location.y + h / 2)
     # bbox.draw()
     return bbox
Пример #21
0
    def __init__(self, presFile, startDate, endDate, species, inDir = '.', 
                 outDir = '.', numProcs = 10, numTrials = 10, logger = None):
        
		# Create the MmxConfig object.
        mmxConfig = MmxConfig()

        mmxConfig.initializeFromValues(presFile, startDate, endDate, species, 
                                       inDir, outDir, numProcs, numTrials)

		# Define the bounding box.
        presPts = PresencePoints(presFile, species)
        bbox = BoundingBox(presPts.points, presPts.epsg)
        
        mmxConfig.setUlx(bbox.getUlx())
        mmxConfig.setUly(bbox.getUly())
        mmxConfig.setLrx(bbox.getLrx())
        mmxConfig.setLry(bbox.getLry())
        mmxConfig.setEPSG(bbox.getEpsg())

        super(ConfigureMmxRun, self).__init__(mmxConfig, 
                                              'ConfigureMmxRun', 
                                              logger)
                                         
        # Log what we have so far.
        self.logHeader()
        self.logger.info(str(self.config))
Пример #22
0
def computeBBfromBB(bb_coords):
    """
    Method that converts np array of coords (xmin,xmax,ymin,ymax) into
    a bounding box.
    
    Args:
        bb_coords: the bounding box coordinates
    """
    return BoundingBox(bb_coords[0], bb_coords[1], bb_coords[2], bb_coords[3])
Пример #23
0
def boxRatioIsEqual(box: BoundingBox, aspectRatio: float):
    """Checks if a boxes aspect ratio is equal to the given aspect ratio.
    
    Args:
        box (BoundingBox): box to check aspect ratio for
        aspectRatio (Tuple[int,int]): aspect ratio to check box against (width, height)
    """

    diff = abs(box.aspectRatio() - aspectRatio)
    return diff < epsilon
Пример #24
0
 def updateLocation(self, newLocation):
     self.previousLocation = self.location
     self.location = newLocation
     self.bbox = BoundingBox(self.location.x - self.width / 2,
                             self.location.y - self.height / 2,
                             self.location.x + self.width / 2,
                             self.location.y + self.height / 2)
     self.polygon = Polygon.createBoundingBoxPolygon(
         Vector(self.location.x - self.width / 2,
                self.location.y - self.height / 2),
         Vector(self.location.x + self.width / 2,
                self.location.y + self.height / 2))
     self.polygon.convex = True
Пример #25
0
def locate_templates(img, templates, start, stop, threshold):
    #print('Locating Templates...')
    #print(f'templates in locate: {templates}')
    locations, scale = match(img, templates, start, stop, threshold)
    img_locations = []
    for i in range(len(templates)):
        w, h = templates[i].shape[::-1]
        w *= scale
        h *= scale
        img_locations.append([
            BoundingBox(pt[0], pt[1], w, h) for pt in zip(*locations[i][::-1])
        ])
    return img_locations
Пример #26
0
 def iou(self, x1, y1, x2, y2):
     bounding_box1 = BoundingBox(x1 * self.stride - self.half_box_length,
                                 x1 * self.stride + self.half_box_length,
                                 y1 * self.stride - self.half_box_length,
                                 y1 * self.stride + self.half_box_length)
     bounding_box2 = BoundingBox(x2 * self.stride - self.half_box_length,
                                 x2 * self.stride + self.half_box_length,
                                 y2 * self.stride - self.half_box_length,
                                 y2 * self.stride + self.half_box_length)
     assert (bounding_box1.iou(bounding_box2) == bounding_box2.iou(
         bounding_box1))
     return bounding_box1.iou(bounding_box2)
Пример #27
0
 def __init__(self, depth=0, bbox=None):
     self.ne = self.se = self.sw = self.nw = None
     self.depth = depth
     
     if depth == 0:
         # Let's say our world can only be a third of the maximal float value
         max = 1500#sys.float_info.max/3
         self.bbox = BoundingBox(-max, -max, max, max)
     else:
         if not bbox:
             raise ValueError("No bounding box given")
         self.bbox = bbox
     
     self.items = []
Пример #28
0
class PointSet(MyJSONEncoder.AbstractJSONEncoder):
    def __init__(self):
        self.points = []
        self.boundingBox = BoundingBox()

    def addPoint(self, p):
        if not isinstance(p, Point):
            Logger().error(
                "PointSet: il punto passato alla bounding box non e' un punto valido"
            )
            return False

        self.points.append(p)
        if not self.boundingBox.updateBoundingBox(p):
            Logger().error("PointSet: errore nell'update della bounding box")
            return False
        return True

    def getMin(self):
        return self.boundingBox.getMin()

    def getMax(self):
        return self.boundingBox.getMax()

    def size(self):
        return len(self.points)

    def decodeJson(self):
        ret = {
            "Points": self.size(),
            "BoundingBox": {
                "Min": self.getMin(),
                "Max": self.getMax(),
            },
            "Coordinate": self.points
        }
        return ret
Пример #29
0
def create_particles(num_particles: int, max_x: int, max_y: int, min_x: int,
                     min_y: int, width: int, height: int,
                     image) -> List[Particles]:
    particles = []

    for i in range(0, num_particles):
        x = random.randint(min_y, max_x)
        y = random.randint(min_y, max_y)
        bbox = BoundingBox(x, y, width, height, 640, 480, 0, 0)

        histogram = generate_histograms(bbox, image)
        particle = Particles(bbox, histogram, 1)
        particles.append(particle)

    return particles
Пример #30
0
def computeBBfromSM(segMask):
    """
        Method that computes bounding box from a binary segmentation mask with
        only a single object present
        
        Arguments:
            segMask: 2D binary image
        """
    if len(segMask.shape) != 2:
        raise ValueError('Cannot compute bounding box from non-2D image')

    x_coords, y_coords = np.where(segMask > 0)

    return BoundingBox(np.min(y_coords), np.max(y_coords), np.min(x_coords),
                       np.max(x_coords))
Пример #31
0
    def get_image_bounding_box(self) -> BoundingBox:
        mx, my = self._lat_lon_to_meters(self.center_lat, self.center_lng)
        pixel_x, pixel_y = self._meters_to_pixels(mx, my)
        w_pixel_x, e_pixel_x = pixel_x - self.image_w / 2, pixel_x + self.image_w / 2
        s_pixel_y, n_pixel_y = pixel_y - self.image_h / 2, pixel_y + self.image_h / 2

        w_meter_x = self._pixels_to_meters(w_pixel_x, self.zoom)
        e_meter_x = self._pixels_to_meters(e_pixel_x, self.zoom)
        s_meter_y = self._pixels_to_meters(s_pixel_y, self.zoom)
        n_meter_y = self._pixels_to_meters(n_pixel_y, self.zoom)

        s_lat, w_lng = self._meters_to_lat_lon(w_meter_x, s_meter_y)
        n_lat, e_lng = self._meters_to_lat_lon(e_meter_x, n_meter_y)

        return BoundingBox(s_lat, w_lng, n_lat, e_lng)
Пример #32
0
class QuadNode:
    def __init__(self, depth=0, bbox=None):
        self.ne = self.se = self.sw = self.nw = None
        self.depth = depth
        
        if depth == 0:
            # Let's say our world can only be a third of the maximal float value
            max = 1500#sys.float_info.max/3
            self.bbox = BoundingBox(-max, -max, max, max)
        else:
            if not bbox:
                raise ValueError("No bounding box given")
            self.bbox = bbox
        
        self.items = []
        
    #
    # Add an item to the quad tree
    # item needs to have a member bbox containing it's bounding box
    #
    def add(self, item):
        if self.depth < 8 and self.ne == None:
            cx = self.bbox.center.x
            cy = self.bbox.center.y
        
            self.ne = QuadNode(self.depth + 1, BoundingBox(cx, cy, self.bbox.maxX, self.bbox.maxY))
            self.se = QuadNode(self.depth + 1, BoundingBox(cx, self.bbox.minY, self.bbox.maxX, cy))
            self.sw = QuadNode(self.depth + 1, BoundingBox(self.bbox.minX, self.bbox.minY, cx, cy))
            self.nw = QuadNode(self.depth + 1, BoundingBox(self.bbox.minX, cy, cx, self.bbox.maxY))
            
        if self.depth == 8:
            self.items.append(item)
            return
        
        if self.ne.bbox.overlaps(item.bbox):
            self.ne.add(item)
        
        if self.se.bbox.overlaps(item.bbox):
            self.se.add(item)
        
        if self.sw.bbox.overlaps(item.bbox):
            self.sw.add(item)
        
        if self.nw.bbox.overlaps(item.bbox):
            self.nw.add(item)
            
    #
    # Remove an item from the quad tree
    # This only works if the item still has the same bounding box as it had at the time of adding
    #
    def remove(self, item):
        if self.depth == 8:
            self.items.remove(item)
            return
        
        if self.ne.bbox.overlaps(item.bbox):
            self.ne.remove(item)
        
        if self.se.bbox.overlaps(item.bbox):
            self.se.remove(item)
        
        if self.sw.bbox.overlaps(item.bbox):
            self.sw.remove(item)
        
        if self.nw.bbox.overlaps(item.bbox):
            self.nw.remove(item)
        
            
    def drawCollision(self, item):
        self.bbox.draw()
        
        if self.depth == 8:
            return
        
        if self.ne.bbox.overlaps(item.bbox):
            self.ne.drawCollision(item)
        
        if self.se.bbox.overlaps(item.bbox):
            self.se.drawCollision(item)
        
        if self.sw.bbox.overlaps(item.bbox):
            self.sw.drawCollision(item)
        
        if self.nw.bbox.overlaps(item.bbox):
            self.nw.drawCollision(item)
        
    def collision(self, object):
        offsets = []
        objects = []
        
        self.__collision__(object, offsets, objects)
        
        if len(offsets) == 0:
            return False, None
        
        max = -1
        result = None
        for offset in offsets:
            if offset.length() > max:
                result = offset
                max = offset.length()
                
        test = result
        if Vector(0, 1).dot(result) < 0:
            test = test.multiply(-1)
                
        p = Vector( (object.bbox.minX+object.bbox.maxX)/2, object.bbox.minY)
        t = p.add(test)
        glPointSize(10)
        glBegin(GL_POINTS)
        glVertex2f(p.x, p.y)
        glEnd()
        glPointSize(1)
        
        glLineWidth(10)
        glBegin(GL_LINES)
        glVertex2f(p.x, p.y)
        glVertex2f(t.x, t.y)
        glEnd()
        glLineWidth(1)
                
        return True, result
            
    
    def __collision__(self, object, offsets, objects):
    
        if self.depth == 8:
            for item in self.items:
                if objects.count(item) == 0:
                    collides, offset = item.collision(object)
                    if collides:
                        offsets.append(offset)
                    objects.append(item)
        
        if self.ne and self.ne.bbox.overlaps(object.bbox):
            self.ne.__collision__(object, offsets, objects)
        
        if self.se and self.se.bbox.overlaps(object.bbox):
            self.se.__collision__(object, offsets, objects)
        
        if self.sw and self.sw.bbox.overlaps(object.bbox):
            self.sw.__collision__(object, offsets, objects)
        
        if self.nw and self.nw.bbox.overlaps(object.bbox):
            self.nw.__collision__(object, offsets, objects)
            
            
    def draw(self, view):
        drawn = []
        self.__draw__(view, drawn)
        # print len(drawn)
        
    def __draw__(self, view, drawn):
        if self.depth == 8:
            for item in self.items:
                if drawn.count(item) == 0:
                    item.draw()
                    drawn.append(item)
                
        if self.ne and self.ne.bbox.overlaps(view.bbox):
            self.ne.__draw__(view, drawn)
        
        if self.se and self.se.bbox.overlaps(view.bbox):
            self.se.__draw__(view, drawn)
        
        if self.sw and self.sw.bbox.overlaps(view.bbox):
            self.sw.__draw__(view, drawn)
        
        if self.nw and self.nw.bbox.overlaps(view.bbox):
            self.nw.__draw__(view, drawn)
Пример #33
0
 def updateBoundingBox(self):
     size = len(self.vertices)
     self.bbox = BoundingBox(self.vertices[0].x,self.vertices[0].y,self.vertices[0].x,self.vertices[0].y)
     for vertex in range(1, size):
         self.bbox.add(self.vertices[vertex].x, self.vertices[vertex].y)
Пример #34
0
class Polygon:
    def __init__(self):
        self.vertices = []
        self.monotones = None
        self.triangles = None
        self.bbox = None
        
        # If this is False, this does not mean it is no convex, but if it is True, it is!
        self.convex = False
    
    def addVertex(self, p):
        self.vertices.append(p)
        self.updateBoundingBox()
        
        if len(self.vertices) == 3:
            self.convex = True
        else:
            self.convex = False
    
    #
    # Draw this polygon
    #
    def draw(self):
        self.__draw__(0)
    
    def __draw__(self, level):
        if len(self.vertices) < 3:
            raise ValueError("This ain't no polygon! A duogon, at best!")
            
        if len(self.vertices) == 3:
            glPolygonMode(GL_FRONT_AND_BACK,GL_FILL);
            glColor3f (.1, .1, .1)
            glBegin(GL_TRIANGLES)
            glVertex2f(self.vertices[0].x, self.vertices[0].y)
            glVertex2f(self.vertices[1].x, self.vertices[1].y)
            glVertex2f(self.vertices[2].x, self.vertices[2].y)
            glEnd()
            
        if self.monotones and self.triangles:
            raise ValueError("A polygon containing both monotones and triangles? Something went wrong")
            
        if self.monotones:
            for m in self.monotones:
                m.__draw__(level + 1)
                
        if self.triangles:
            for t in self.triangles:
                t.__draw__(level + 1)
         
        if level == 0:
            glColor3f(.9, .9, .9)
            glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
            glEnable(GL_POLYGON_OFFSET_LINE);
            glPolygonOffset(-1.,-1.);
            self.__drawPolygon__()

    def __drawPolygon__(self):
        glBegin(GL_POLYGON)
        size = len(self.vertices)
        for vertex in range(size):
            glVertex2f(self.vertices[vertex].x, self.vertices[vertex].y)
            glVertex2f(self.vertices[(vertex+1)%size].x, self.vertices[(vertex+1)%size].y)
        glEnd()
    
    # Line segment 1: p + t*r
    # Line segment 2: q + u*s
    @staticmethod
    def __LineSegmentIntersection__(p, r, q, s):
        rCrossS = r.cross(s)
        if rCrossS == 0:
            return -1, -1
        qMinusP = q.subtract(p)
        rhsT = qMinusP.cross(s)
        t = rhsT/rCrossS
        rhsR = qMinusP.cross(r)
        u = rhsR/rCrossS
        
        return t, u
    
    #
    # Check for collisions
    # \return True|False, CollisionVector
    #
    def collisionActor(self, actor):
    
        if not actor.bbox.overlaps(self.bbox):
            return False, Vector(0,0)
        # Line segment 1: p + t*r
        # Line segment 2: q + u*s
        p = Vector(actor.location.x, actor.location.y-actor.height/2)
        r = Vector(0, actor.height-1)

        size = len(self.vertices)
        for vertex in range(size):
            # t = (q - p) x s /(r x s)
            # u = (q - p) x r /(r x s)
            q = self.vertices[vertex]
            qPlusS = self.vertices[(vertex+1)%size]            
            s = qPlusS.subtract(q)
            
            t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)
            
            if t >= 0 and t < 1 and u >= 0 and u < 1:
                # return True, p.add(r.multiply(t)).add(r.multiply(0.5))
                return True, r.multiply(t)#.add(Vector(0, -.001))
        return False, Vector(0,0)
              
    #
    # Check if this polygon collides with another polygon
    # \return True|False, CollisionVector
    #
    def collision(self, other):        
        collides, axis, dist = self.__privateCollision__(other)
        
        if collides:
            return True, axis.multiply(dist)
        
        return False, None
            
    def __privateCollision__(self, other):
        if not self.bbox.overlaps(other.bbox):
            return False, None, None
            
        if self.convex:
            minAxis = None
            minDist = sys.float_info.max
            collides, minAxis, minDist = self.__privateCollisionConvex__(other, minAxis, minDist)
            if not collides:
                return False, minAxis, minDist
                
            return other.__privateCollisionConvex__(self, minAxis, minDist)
            
        if self.monotones:
            collides = False
            newCollides = False
            newAxis = None
            newDist = None
            axis = None
            dist = -1
            for m in self.monotones:
                newCollides, newAxis, newDist = m.__privateCollision__(other)
                if newCollides:
                    collides = True
                    if newDist > dist:
                        dist = newDist
                        axis = newAxis
                        
            return collides, axis, dist        
            
        if self.triangles:
            collides = False
            newCollides = False
            newAxis = None
            newDist = None
            axis = None
            dist = -1
            for t in self.triangles:
                newCollides, newAxis, newDist = t.__privateCollision__(other)
                if newCollides:
                    collides = True
                    if newDist > dist:
                        dist = newDist
                        axis = newAxis
                        
            return collides, axis, dist    

            
    # Internal collision method
    def __privateCollisionConvex__(self, other, minAxis, minDist):
        size = len(self.vertices)
        for i in range(size):
            v1 = self.vertices[i]
            v2 = self.vertices[(i+1)%size]
            
            perp = Polygon.__perpendicularVector__(self, v1, v2)
            
            minSelf = maxSelf = minOther = maxOther = None
            
            minSelf, maxSelf = self.__projectOnAxis__(perp)
            minOther, maxOther = other.__projectOnAxis__(perp)
            
            dist = Polygon.__getIntervalDistance__(minSelf, maxSelf, minOther, maxOther)
            
            if dist > 0.:
                return False, minAxis, minDist
            elif abs(dist) < minDist:
                minDist = abs(dist)
                minAxis = perp
        return True, minAxis, minDist
            
            
    #
    # Project all vertices of this polygon onto an axis
    # Get the min and max values
    #
    def __projectOnAxis__(self, axis):
        min = max = axis.dot(self.vertices[0])
        
        for i in range(1, len(self.vertices)):
            product = axis.dot(self.vertices[i])
            
            if product < min:
                min = product
            
            if product > max:
                max = product
                
        return min, max
    
    #
    # Get the distance beween two 1-dimensional intervals
    #
    @staticmethod
    def __getIntervalDistance__(min1, max1, min2, max2):
        if min1 < min2:
            return min2 - max1
        else:
            return min1 - max2
            
    #
    # Get the perpendicular vector on a line segment defined by 2 poins
    #
    @staticmethod
    def __perpendicularVector__(self, v1, v2):
        dy = (v1.y - v2.y)
        dx = (v1.x - v2.x)
        d = sqrt(dy*dy + dx*dx)
        if d == 0:
            return Vector(1, 0)
        return Vector(-dy / d, dx / d)
            
    
    def __selfIntersects__(self):
        size = len(self.vertices)
        for vertex1 in range(size):
            p = self.vertices[vertex1]
            pPlusR = self.vertices[(vertex1+1)%size]
            r = pPlusR.subtract(p)
            for vertex2 in range(vertex1, size):
                if vertex1 == vertex2:
                    continue
                q = self.vertices[vertex2]
                qPlusS = self.vertices[(vertex2+1)%size]
                s = qPlusS.subtract(q)
                
                t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)
            
                if t >= 0 and t < 1 and u >= 0 and u < 1:
                    return True
        return False

    # TODO
    def cut(self, other):
        sizeSelf = len(self.vertices)
        sizeOther = len(other.vertices)
        # Line segment 1: p + t*r
        # Line segment 2: q + u*s
        for vertexSelf in range(sizeSelf):
            p = self.vertices[vertexSelf]
            pPlusR = self.vertices[(vertexSelf+1)%sizeSelf]
            r = pPlusR.subtract(p)
            for vertexOther in range(sizeOther):
                # t = (q - p) x s /(r x s)
                # u = (q - p) x r /(r x s)
                q = other.vertices[vertexOther]
                qPlusS = other.vertices[(vertexOther+1)%sizeOther]
                
                s = qPlusS.subtract(q)
                rCrossS = r.cross(s)
                qMinusP = q.subtract(p)
                rhsT = qMinusP.cross(s)
                t = rhsT/rCrossS
                rhsR = qMinusP.cross(r)
                u = rhsR/rCrossS
                
                if t >= 0 and t < 1 and u >= 0 and u < 1:
                    intersect = p.add(r.multiply(t))
                    glColor3f(0.8, 0.8, 0.2)
                    glPointSize(6)
                    intersect.draw()
                    
    #
    # Check if the given point is inside the polygon
    #
    def inside(self, p):
        size = len(self.vertices)
        count = 0
        for i in range(size):
            v1 = self.vertices[i]
            v2 = self.vertices[(i+1)%size]
            
            if (v1.y > p.y and v2.y <= p.y) or \
                (v1.y <= p.y and v2.y > p.y):
                            
                div = (v1.x - v2.x)
                if div == 0:
                    if p.x - v1.x > 0:
                        count += 1
                m = (v1.y - v2.y) / div
                if p.x - (v1.x + (p.y - v1.y)/m) > 0:
                    count += 1
        return count%2 == 1
    
    @staticmethod
    def __interiorAngleGreaterThanPi__(v1, v2, v3):
        cross = v1.subtract(v2).cross(v3.subtract(v2))
        if cross < 0:
            return True
        return False
      
    # Doubly Connected edge list: a data structure which can be used in monotone decomposition and triangulation of a polygon
    class DCELHalfEdge:
        def __init__(self, origin):
            self.origin = origin
            self.twin = None
            self.next = None
            self.prev = None
            self.face = None
            self.helper = None
            
        def start(self):
            return self.origin.v
        
        def end(self):
            return self.next.origin.v
            
        def xDistTo(self, v):
            # y - y1 = m(x - x1)
            # x = x1 + (y - y1)/m
            div = (self.start().x - self.end().x)
            if div == 0:
                return v.x - self.start().x
            m = (self.start().y - self.end().y) / div
            
            return v.x - (self.start().x + (v.y - self.start().y)/m)
            
        def mark(self):
            glLineWidth(10)
            glBegin(GL_LINES)
            glVertex2f(self.start().x, self.start().y)
            glVertex2f(self.end().x, self.end().y)
            glEnd()
            glLineWidth(1)
            
    class DCELVertex:
        def __init__(self, v):
            self.v = v
            self.leavingEdge = None
            
    class DCELFace:
        def __init__(self, edge):
            self.edge = edge
            self.visited = False
            
    class DCELNextEdgeIterator:
        def __init__(self, edge):
            self.start = edge
            self.current = edge
            
        def next(self):
            self.current = self.current.next
            if self.current == self.start:
                return None
            return self.current
            
    class DCELLeavingEdgeIterator:
        def __init__(self, edge):
            self.start = edge
            self.current = edge
            
        def next(self):
            self.current = self.current.prev.twin
            if self.current == self.start:
                return None
            return self.current
            

    # Search tree, implemented initially as a flat array (Laziness)
    class Tree:
        def __init__(self):
            self.edges = []
            # self.count = 0
        
        def add(self, edge):
            self.edges.append(edge)
         
        def remove(self, edge):
            self.edges.remove(edge)
            
        def edgeLeftOf(self, v):
            d = sys.float_info.max
            e = None
            for edge in self.edges:
                if (edge.start().y >= v.y and edge.end().y < v.y) or \
                    (edge.start().y < v.y and edge.end().y >= v.y):
                        distToEdge = edge.xDistTo(v)                        
                        if distToEdge > 0 and d > distToEdge:
                            d = distToEdge
                            e = edge
            return e
            
        def draw(self):
            print "hmpf"
            for edge in self.edges:
                glLineWidth(6)
                glBegin(GL_LINES)
                glVertex2f(edge.start().x, edge.start().y)
                glVertex2f(edge.end().x, edge.end().y)
                glEnd()
                glLineWidth(1)
                
    # Is it possible to add a diagonal between two vertices
    def __canAddDiagonal__(self, vi1, vi2):
        size = len(self.vertices)
        p = self.vertices[vi1]
        pPlusR = self.vertices[vi2]
        r = pPlusR.subtract(p)
        for v in range(size):
            # if (vi1==v and vi2==(v+1)%size) or (vi2==v and vi1==(v+1)%size):
                # return False
            q = self.vertices[v]
            qPlusS = self.vertices[(v+1)%size]
            s = qPlusS.subtract(q)
            
            t, u = Polygon.__LineSegmentIntersection__(p, r, q, s)
            
            if t > 0 and t < 1 and u > 0 and u < 1:
                return False
                
        midDiagonal = Vector((p.x+pPlusR.x)/2, (p.y+pPlusR.y)/2)
        return self.inside(midDiagonal)
            
       
    # Add a diagonal to the DCEL of this polygon
    def __addDiagonal__(self, vi1, vi2, dcelVertices):
        v1 = dcelVertices[vi1]
        v2 = dcelVertices[vi2]
        
        # Find the common face for v1 and v2
        face = None
        e1 = v1.leavingEdge 
        it1 = Polygon.DCELLeavingEdgeIterator(e1)
        while e1:
            face1 = e1.face
            e2 = v2.leavingEdge
            it2 = Polygon.DCELLeavingEdgeIterator(e2)
            while e2:
                if face1 == e2.face and face1 != None:
                    face = face1
                    break
                e2 = it2.next()
            e1 = it1.next()
        
        # Find the next and previous edges for both diagonals
        prevEdge1 = None
        nextEdge1 = None
        prevEdge2 = None
        nextEdge2 = None

        e = face.edge
        it = Polygon.DCELNextEdgeIterator(e)
        while e:
            if e.origin == v1:
                prevEdge1 = e.prev
                nextEdge1 = e
            if e.origin == v2:
                prevEdge2 = e.prev
                nextEdge2 = e
            e = it.next()
        
        # Connect 2 new half edges
        diagonal1 = Polygon.DCELHalfEdge(v1)
        diagonal1.prev = prevEdge1
        prevEdge1.next = diagonal1
        diagonal1.next = nextEdge2
        nextEdge2.prev = diagonal1
        
        diagonal2 = Polygon.DCELHalfEdge(v2)
        diagonal2.prev = prevEdge2
        prevEdge2.next = diagonal2
        diagonal2.next = nextEdge1
        nextEdge1.prev = diagonal2

        diagonal1.twin = diagonal2
        diagonal2.twin = diagonal1
        
        # Connect new faces
        face1 = Polygon.DCELFace(diagonal1)
        face2 = Polygon.DCELFace(diagonal2)
        e1 = diagonal1
        it1 = Polygon.DCELNextEdgeIterator(e1)
        while e1:
            e1.face = face1
            e1 = it1.next()
        
        e2 = diagonal2
        it2 = Polygon.DCELNextEdgeIterator(e2)
        while e2:
            e2.face = face2
            e2 = it2.next()

    
    # Construct the Doubly connected edge list
    # \param dcelVertices A list of DCELVertex objects, must be empty
    def __constructDCEL__(self, dcelVertices):
        size = len(self.vertices)
        currentVertex = None
        # Create all DCEL Vertices with a leaving edge
        for i in range(size):
            # Create the current vertex or use the previously stored last one, if this is the last vertex
            v = self.vertices[i]
            currentVertex = Polygon.DCELVertex(v)
            # Create the DCEL Half Edge
            currentEdge = Polygon.DCELHalfEdge(currentVertex)
            # Connect
            currentVertex.leavingEdge = currentEdge
            dcelVertices.append(currentVertex)
        
        # Create all twin half edges
        for i in range(size):
            currentVertex = dcelVertices[i]
            nextVertex = dcelVertices[(i+1)%size]
            currentEdge = currentVertex.leavingEdge
            twinEdge = Polygon.DCELHalfEdge(nextVertex)
            currentEdge.twin = twinEdge
            twinEdge.twin = currentEdge
            
        # Connect all edges
        for i in range(size):
            prevVertex = dcelVertices[i-1]
            currentVertex = dcelVertices[i]
            nextVertex = dcelVertices[(i+1)%size]
            
            currentEdge = currentVertex.leavingEdge
            currentEdge.prev = prevVertex.leavingEdge
            currentEdge.next = nextVertex.leavingEdge
            
            twinEdge = currentEdge.twin
            twinEdge.prev = nextVertex.leavingEdge.twin
            twinEdge.next = prevVertex.leavingEdge.twin

        # Connect the single face
        startVertex = dcelVertices[0]
        startEdge = startVertex.leavingEdge
        face = Polygon.DCELFace(startEdge)
        
        e = startEdge
        it = Polygon.DCELNextEdgeIterator(e)
        while e:
            e.face = face
            e = it.next()
        
                
    # Sort the vertices of this polygon according to y coordinate
    def __ySortVertices__(self, sortedVertices):
        size = len(self.vertices)
        for vertex in range(size):
            v = self.vertices[vertex]
            sizeSorted = len(sortedVertices)
            found = False
            for index in range(sizeSorted):
                if v.above(self.vertices[sortedVertices[index]]):
                    sortedVertices.insert(index, vertex)
                    found = True
                    break
            if not found:
                sortedVertices.append(vertex)
    
    @staticmethod
    def __constructPolygonsFromDCEL__(dcelVertices, polygons):
        # Iterator all leaving edges on all vertices to find all faces
        for vertex in dcelVertices:
            e1 = vertex.leavingEdge
            it1 = Polygon.DCELLeavingEdgeIterator(e1)
            polygon = Polygon()
            while e1:
                if e1.face and not e1.face.visited:
                    e1.face.visited = True
                    polygon = Polygon()
                    e2 = e1.face.edge
                    it2 = Polygon.DCELNextEdgeIterator(e2)
                    while e2:
                        polygon.addVertex(e2.origin.v)
                        e2 = it2.next()
                    polygons.append(polygon)
                e1 = it1.next()
            
    def triangulate(self):
        size = len(self.vertices)
        if size <= 3:
            return
        # Monotone decomposition
        # Computation Geometry Algorithms and Applications, page 50
        startVertex = 1
        endVertex = 2
        regularVertex = 3
        mergeVertex = 4
        splitVertex = 5
        
        vertexType = []
        # Determine the type for each vertex
        for vertex in range(size):
            v = self.vertices[vertex]
            vPrev = self.vertices[vertex-1]
            vNext = self.vertices[(vertex+1)%size]
            if v.above(vPrev) and v.above(vNext):
                if Polygon.__interiorAngleGreaterThanPi__(vNext, v, vPrev):
                    vertexType.append(splitVertex)
                else:
                    vertexType.append(startVertex)
            elif vPrev.above(v) and vNext.above(v):
                if Polygon.__interiorAngleGreaterThanPi__(vNext, v, vPrev):
                    vertexType.append(mergeVertex)
                else:
                    vertexType.append(endVertex)
            else:
                vertexType.append(regularVertex)

        # glColor3f(1., 1., 1.)
        # glPolygonMode(GL_FRONT_AND_BACK,GL_LINE)
        # if False:
            # self.__privateDraw__()
            # for vertex in range(size):
                # v = self.vertices[vertex]
                # if vertexType[vertex] == startVertex:
                    # glPointSize(12)
                # elif vertexType[vertex] == endVertex:
                    # glPointSize(3)
                # elif vertexType[vertex] == splitVertex:
                    # glPointSize(6)
                # elif vertexType[vertex] == mergeVertex:
                    # glPointSize(9)
                # else:
                    # glPointSize(1)
                
                # glColor3f(1., 2., 2.)
                # glBegin(GL_POINTS)
                # glVertex2f(v.x, v.y)
                # glEnd()
        
        # Construct doubly-connected edge list
        dcelVertices = []
        self.__constructDCEL__(dcelVertices)
        
       
        #
        # TEST CODE : Check DCEL
        #
        # polys = []
        # Polygon.__constructPolygonsFromDCEL__(dcelVertices, polys)
        
        # c = int(time.clock()*2)%(len(polys)+1)
        # if c == len(polys):
            # glColor3f(1., 1., .2)
            # self.__privateDraw__()
            # return
        # glColor3f(.2, .2, 1.)
        # polys[c].__privateDraw__()
        
        #
        # TEST CODE : Check prev, next, twin edges
        #
        # c = int(time.clock()*2)%(len(dcelVertices))
        # v = dcelVertices[c]
        # glPointSize(30)
        # glBegin(GL_POINTS)
        # glVertex2f(v.v.x, v.v.y)
        # glEnd()
        # glPointSize(1)
        # v.leavingEdge.twin.mark()
        # glColor3f(1., 1., .2)
        # v.leavingEdge.twin.next.mark()
        # return
        
        #
        # TEST CODE : Check edgeLeftOf
        #
        # tree = Polygon.Tree()
        # e1 = Polygon.DCELHalfEdge(Polygon.DCELVertex(Vector(-1, 1)))
        # e2 = Polygon.DCELHalfEdge(Polygon.DCELVertex(Vector(-1, -1)))
        # e1.next = e2
        # e2.prev = e1
        # tree.add(e1)
        # tree.edgeLeftOf(Vector(0, 0)).mark()
        # return
                
        # Sort vertices top to bottom
        # Insertion sort
        sortedVertices = []        
        self.__ySortVertices__(sortedVertices)
        
        # Initialize the partition with the edges of the (possibly) not-monotone polygon
        tree = Polygon.Tree()

        # Handle each vertex, from top to bottom the polygon
        # Handling each vertex depends on the type of the vertex
        for vertex in sortedVertices:
            v = self.vertices[vertex]
            if vertexType[vertex] == startVertex:
                ei = dcelVertices[vertex].leavingEdge
                ei.helper = vertex
                # Add e_i to searchtree
                tree.add(ei)
            elif vertexType[vertex] == endVertex or vertexType[vertex] == mergeVertex:
                # Perform actions which are the same of end or merge vertices
                eiMinus1 = dcelVertices[vertex-1].leavingEdge
                # Add a diagonal if helper(e_i+1) is a merge vertex
                if vertexType[eiMinus1.helper] == mergeVertex:
                    self.__addDiagonal__(vertex, eiMinus1.helper, dcelVertices)
                # Delete e_i from searchtree
                tree.remove(eiMinus1)
                # Perform actions which are unique to a merge vertex
                if vertexType[vertex] == mergeVertex:
                    # Find edge directly to the right of vertex
                    ej = tree.edgeLeftOf(v)
                    if vertexType[ej.helper] == mergeVertex:
                        self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                    # Set vertex as new helper
                    ej.helper = vertex
            elif vertexType[vertex] == splitVertex:
                # Find edge directly to the left of vertex
                ej = tree.edgeLeftOf(v)
                self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                # Set vertex as new helper
                ej.helper = vertex
                ei = dcelVertices[vertex].leavingEdge
                # Add e_i to tree and set vertex as helper
                ei.helper = vertex
                tree.add(ei)
            elif vertexType[vertex] == regularVertex:
                #If the interior of the polygon lies right of this vertex
                if self.vertices[(vertex+1)%size].y <= v.y and \
                   self.vertices[vertex-1].y > v.y:
                    eiMinus1 = dcelVertices[vertex-1].leavingEdge
                    helper = eiMinus1.helper
                    # Add a diagonal if helper(e_i-1) is a merge vertex
                    if vertexType[helper] == mergeVertex:
                        self.__addDiagonal__(vertex, helper, dcelVertices)
                    # Delete e_i-1 from searchtree
                    tree.remove(eiMinus1)
                    # Add e_i to tree and set vertex as helper
                    ei = dcelVertices[vertex].leavingEdge
                    ei.helper = vertex
                    tree.add(ei)
                else:
                    # Find edge directly to the left of vertex
                    ej = tree.edgeLeftOf(v)
                    if not ej:
                        glPointSize(5)
                        v.draw()
                        glPointSize(1)
                        return
                    if vertexType[ej.helper] == mergeVertex:
                        self.__addDiagonal__(vertex, ej.helper, dcelVertices)
                    # Set vertex as new helper
                    ej.helper = vertex

        # Construct the monotones
        self.monotones = []
        Polygon.__constructPolygonsFromDCEL__(dcelVertices, self.monotones)
        
        # print "Monotones: " + str(len(self.monotones))
        # if self.debug == 2:
            # c = int(time.clock()*2)%(len(self.monotones)+1)
            # if c == len(self.monotones):
                # glColor3f(1., 1., .2)
                # self.__privateDraw__()
                # return
            # glColor3f(.2, .2, 1.)
            # self.monotones[c].__privateDraw__()
            # for polygon in self.monotones:
                # polygon.__privateDraw__()
            # return
        
        # for m in self.monotones:
            # glColor3f(1., 1., 1.)
            # m.__privateDraw__()

        for m in self.monotones:
        
            if len(m.vertices) == 3:
                # if self.debug == 1 or self.debug == 3:
                    # m.draw(1., .6, .0)
                continue
                       
            monoDcelVertices = []
            m.__constructDCEL__(monoDcelVertices)

            monotoneSortedVertices = []
            m.__ySortVertices__(monotoneSortedVertices)
          
            stack = []
            stack.append(monotoneSortedVertices[0])
            stack.append(monotoneSortedVertices[1])

            for j in range(2, len(monotoneSortedVertices)-1):
            
                if m.__onSameEdge__(stack[-1], monotoneSortedVertices[j]):
                
                    poppedLast = stack.pop()
                    vStack = stack[-1]
                    
                    while m.__canAddDiagonal__(vStack, monotoneSortedVertices[j]):
                        m.__addDiagonal__(vStack, monotoneSortedVertices[j], monoDcelVertices)
                        poppedLast = stack.pop()
                        if len(stack) == 0:
                            break;
                        vStack = stack[-1]
                    
                    stack.append(poppedLast)
                    stack.append(monotoneSortedVertices[j])
                else:
                    while len(stack) > 0:
                        vi = stack.pop()
                        if len(stack) > 0:
                            m.__addDiagonal__(vi, monotoneSortedVertices[j], monoDcelVertices)
                    stack.append(monotoneSortedVertices[j-1])
                    stack.append(monotoneSortedVertices[j])

            stack.pop()
            while len(stack) > 1:
                m.__addDiagonal__(stack[-1], monotoneSortedVertices[-1], monoDcelVertices)
                stack.pop()

            m.triangles = []
            Polygon.__constructPolygonsFromDCEL__(monoDcelVertices, m.triangles)
            
            # if self.debug == 1:
                # m.triangles[int(time.clock()*2)%len(m.triangles)].draw(1., .2, .2)
            # if self.debug == 3:
                # for t in m.triangles:
                    # t.draw(1., .2, .2)

    # check if two vertices are on the same edge, given their indices
    def __onSameEdge__(self, i1, i2):
        size = len(self.vertices)
        return abs(i1-i2) == 1 or abs(i1-i2) == size-1

    # Apply midpoint displacement to this polygon
    # The midpoint of each line segment will be displaced randomly
    # perpendicular to the line segment, distance between -factor and +factor
    def midpointDisplacement(self, factor):
        seed = random.randint(0, 10000)
        # if factor == 8:
            # seed = 8977
            # seed = 9539
            # seed = 1909
            # seed = 5021
            # seed = 6131 # Triangulation issue
            # seed = 6198 # Triangulation issue
            # seed = 3403 # Triangulation issue
        print factor
        print seed
        random.seed(seed)
        size = len(self.vertices)
        oldVertices = list(self.vertices)
        vertices = []
        for vertex in range(size):
            p1 = self.vertices[vertex]
            p2 = self.vertices[(vertex+1)%size]
            vertices.append(p1)
            p = p1.subtract(p2).perpendicularVector()
            r = factor - random.random()*factor*2
            x = (p1.x + p2.x)/2 + r*p.x
            y = (p1.y + p2.y)/2 + r*p.y
            vertices.append(Vector(x, y))
        self.vertices = vertices
        if self.__selfIntersects__():
            print "uh oh"
            self.vertices = oldVertices
            self.midpointDisplacement(factor/2)
        self.updateBoundingBox()

    def updateBoundingBox(self):
        size = len(self.vertices)
        self.bbox = BoundingBox(self.vertices[0].x,self.vertices[0].y,self.vertices[0].x,self.vertices[0].y)
        for vertex in range(1, size):
            self.bbox.add(self.vertices[vertex].x, self.vertices[vertex].y)
    
    @staticmethod
    def createBoundingBoxPolygon(pMin, pMax):
        p = Polygon()
        p.addVertex(Vector(pMin.x, pMax.y))
        p.addVertex(Vector(pMin.x, pMin.y))
        p.addVertex(Vector(pMax.x, pMin.y))
        p.addVertex(Vector(pMax.x, pMax.y))
        return p