def put(self, crop_id): # confirm that the X-Manual header was specified manual = checkXManual(request) content = request.get_json() if content is None: abort(400, 'Must specify values to update') if 'image_id' in content: abort(400, 'Updating image_id is forbidden!') if 'crop_id' in content: abort(400, "Updating the crop_id is forbidden") dao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) result = dao.updateImage(crop_id, content) if result is None: return { 'message': 'No image with id {} found to update (or was there a server error?)' .format(crop_id) }, 404 else: return jsonify(result.toJsonResponse())
def get(self): manual = checkXManual(request) dao = SubmittedTargetDAO(defaultConfigPath()) resultingClassifications = dao.getAllTargets(not manual) if resultingClassifications is None or not resultingClassifications: return { 'message': 'Failed to retrieve any submitted targets (Have any been submitted?)' }, 404 resultingClassifications = [ classification.toDict() for classification in resultingClassifications ] croppedDao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) for target in resultingClassifications: # for each of these targets we need to try and get the crop_id for it from the crop_path # crop_id is much more useful client-side than crop_path croppedImg = croppedDao.getImageWithCropPath(target['crop_path']) if croppedImg is not None: target['crop_id'] = croppedImg.crop_id return jsonify(resultingClassifications)
def post(self): # confirm that the X-Manual header was specified manual = checkXManual(request) prevId = -1 if 'X-Crop-Id' in request.headers: prevId = request.headers.get('X-Crop-Id') else: abort(400, "Need to specify header 'X-Crop-Id'!") dao = OutgoingManualDAO( defaultConfigPath()) if manual else OutgoingAutonomousDAO( defaultConfigPath()) outgoingIn = outgoing_manual( json=request.get_json()) if manual else outgoing_autonomous( json=request.get_json()) outgoingIn.crop_id = prevId resultingId = dao.upsertClassification(outgoingIn) if resultingId == -1: return { 'message': 'Failed to insert classification into outgoing table' }, 500 response = make_response( jsonify({ 'message': 'success!', 'id': resultingId })) response.headers['X-Class-Id'] = resultingId return response
def run(self): # wait until we get our first gps coordinate # remember - the ros_handler code does not insert a gps record # into the table until the gps has a fix haveGps = False print("waiting for gps") while not haveGps and not self._should_shutdown: # check for any entries by just getting all dao = IncomingGpsDAO(defaultConfigPath()) results = dao.getAll() dao.close() if results is None: time.sleep(1) else: haveGps = True # we've got something! print( "Have gps! Using the first coordinate as our ground station. Starting geolocation..." ) # get the coordinates from the first recorded gps measurement # this will be the coordinates we use for the groundstation # in geolocation dao = IncomingGpsDAO(defaultConfigPath()) groundstationGps = dao.getFirst() dao.close() # now we can run stuff geo = targetGeolocation(groundstationGps.lat, groundstationGps.lon) while not self._should_shutdown: # lets deal with manual classifications in the queue dao = OutgoingManualDAO(defaultConfigPath()) classifications = dao.getUnlocatedClassifications() dao.close() if classifications is not None: for classification in classifications: self.processManualGeolocation(geo, classification) # now lets do autonomous dao = OutgoingAutonomousDAO(defaultConfigPath()) classifications = dao.getUnlocatedClassifications() dao.close() if classifications is not None: for classification in classifications: self.processAutonomousGeolocation(geo, classification) time.sleep(1)
def get(self, crop_id): # confirm that the X-Manual header was specified manual = checkXManual(request) dao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) image = dao.getImage(crop_id) if image is None: return { 'message': 'Failed to locate cropped id {}'.format(crop_id) }, 404 return jsonify(image.toJsonResponse())
def get(self): # confirm that the X-Manual header was specified manual = checkXManual(request) dao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) croppedList = dao.getAll() if croppedList == None or not croppedList: return {'message': 'Cropped table is empty!'}, 404 exportable = [img.toJsonResponse() for img in croppedList] return jsonify(exportable)
def get(self, class_id): # confirm that the X-Manual header was specified manual = checkXManual(request) dao = OutgoingManualDAO( defaultConfigPath()) if manual else OutgoingAutonomousDAO( defaultConfigPath()) result = dao.getClassification(class_id) if result is None: return { 'message': 'Failed to locate classification with id {}'.format(class_id) }, 404 return jsonify(result.toDict())
def get(self): # confirm that the X-Manual header was specified manual = checkXManual(request) # Get content: dao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) image = dao.getNextImage() # response validation if image is None: return {'message': 'Failed to locate untapped image'}, 404 # success! return cropImageSender(image, image.cropped_path)
def test(self): truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) # see how it works on an empty table self.assertIsNone(dao.submitAllPendingTargets()) # this will insert records for 2 different targets from 3 classifications: testIns = outgoing_manual() testIns.crop_id = 42 testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'black' dao.addClassification(testIns) testIns.crop_id = 43 dao.addClassification(testIns) testIns.crop_id = 44 testIns.alphanumeric = 'C' testIns.submitted = 'submitted' # this wont work -> it should still be unsubmitted dao.addClassification(testIns) result = dao.submitAllPendingTargets() self.assertIsNotNone(result) self.assertEqual(len(result), 2) # should have 2 targets
def test(self): testIns = outgoing_manual() testIns.crop_id = 42 testIns.type = 'standard' testIns.orientation = 'NE' testIns.shape = 'star' testIns.background_color = 'blue' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'purple' testIns.latitude = 40.11111 testIns.longitude = -111.222222 truncateTable('outgoing_manual') dao = OutgoingManualDAO(defaultConfigPath()) id = dao.addClassification(testIns) self.assertNotEqual(id, -1) model = dao.getClassification(id) self.assertIsNotNone(model) classOut = dao.submitPendingTarget(model.target) self.assertIsNotNone(classOut) imgPath = os.path.dirname(os.path.realpath(__file__)) + '/assets/star.png' targetOut = submitted_target(outgoingManualOrAutonomous=classOut, autonomous_in=False) targetOut.crop_path = imgPath auvsiDao = AuvsiOdlcDao() auvsiDao.addTarget(targetOut)
def test(self): model = incoming_image() model.time_stamp = 1547453775.2 model.focal_length = 16.0 model.image_path = '/im/a/totally/real/path/i/swear.jpg' model.manual_tap = False model.autonomous_tap = False truncateTable('incoming_image') dao = IncomingImageDAO(defaultConfigPath()) self.assertIsNotNone(dao) # test with empty table self.assertIsNone(dao.getImage(1)) resultingId = dao.addImage(model) self.assertNotEqual(resultingId, -1) resultingModel = dao.getImage(resultingId) self.assertIsNotNone(resultingModel) self.assertAlmostEqual(resultingModel.time_stamp, model.time_stamp) self.assertEqual(resultingModel.focal_length, model.focal_length) self.assertEqual(resultingModel.image_path, model.image_path) self.assertEqual(resultingModel.manual_tap, model.manual_tap) self.assertEqual(resultingModel.autonomous_tap, model.autonomous_tap)
def test(self): truncateTable('submitted_target') dao = SubmittedTargetDAO(defaultConfigPath()) # none should fail to insert result = dao.upsertTarget(None) self.assertIsNotNone(result) self.assertEqual(result, -1) testIns = submitted_target() testIns.shape = 'square' testIns.type = 'standard' testIns.crop_path = '/another/totally/real/crop/path/ikr.jpg' testIns.background_color = 'white' testIns.alphanumeric = "T" testIns.alphanumeric_color = "orange" # should fail when we try and upsert an image that doesn't have a target or autonomous field self.assertEqual(dao.upsertTarget(testIns), -1) testIns.autonomous = False # should fail when we try and upsert an image that doesn't have a target field self.assertEqual(dao.upsertTarget(testIns), -1) testIns.target = 10 self.assertNotEqual(dao.upsertTarget(testIns), -1)
def test(self): truncateTable('submitted_target') dao = SubmittedTargetDAO(defaultConfigPath()) testIns = submitted_target() testIns.shape = 'square' testIns.type = 'standard' testIns.crop_path = '/another/totally/real/crop/path/ikr.jpg' testIns.background_color = 'white' testIns.alphanumeric = "T" testIns.alphanumeric_color = "orange" testIns.autonomous = False testIns.target = 10 self.assertNotEqual(dao.upsertTarget(testIns), -1) resultingModel = dao.getTarget(10, False) self.assertIsNotNone(resultingModel) self.assertEqual(resultingModel.target, 10) self.assertFalse(resultingModel.autonomous) self.assertEqual(resultingModel.type, 'standard') self.assertEqual(resultingModel.crop_path, '/another/totally/real/crop/path/ikr.jpg') self.assertEqual(resultingModel.shape, 'square') self.assertEqual(resultingModel.background_color, 'white') self.assertEqual(resultingModel.alphanumeric, 'T') self.assertEqual(resultingModel.alphanumeric_color, 'orange') self.assertEqual(resultingModel.submitted, 'pending') self.assertIsNone(resultingModel.latitude) self.assertIsNone(resultingModel.longitude) self.assertIsNone(resultingModel.orientation)
def put(self, class_id): # confirm that the X-Manual header was specified manual = checkXManual(request) dao = OutgoingManualDAO( defaultConfigPath()) if manual else OutgoingAutonomousDAO( defaultConfigPath()) result = dao.updateClassification(class_id, request.get_json()) if result is None: return { 'message': 'No image with id {} found with a classification to update or your input was invalid (or was there a server error?)' .format(class_id) }, 404 else: return jsonify(result.toDict())
def get(self): # confirm that the X-Manual header was specified manual = checkXManual(request) outgoingList = [] dao = OutgoingManualDAO( defaultConfigPath()) if manual else OutgoingAutonomousDAO( defaultConfigPath()) outgoingList = dao.getAll() if not outgoingList: return {'message': 'Outgoing table is empty!'}, 404 exportable = [ classification.toDict() for classification in outgoingList ] return jsonify(exportable)
def get(self, image_id): dao = IncomingImageDAO(defaultConfigPath()) image = dao.getImage(image_id) if image is None: return { 'message': 'Failed to locate raw id {}'.format(image_id) }, 404 return jsonify(image.toDict())
def get(self, id): dao = IncomingStateDAO(defaultConfigPath()) state = dao.getStateById(id) if state is None: return { 'message': 'Failed to locate state entry with id {}'.format(id) }, 404 return jsonify(state.toDict())
def get(self, image_id): dao = IncomingImageDAO(defaultConfigPath()) image = dao.getImage(image_id) if image is None: return { 'message': 'Failed to locate raw id {}'.format(image_id) }, 404 # otherwise lets send the image:: return rawImageSender(image.image_id, image.image_path)
def get(self, id): dao = IncomingGpsDAO(defaultConfigPath()) gps = dao.getGpsById(id) # response validation if gps is None: return {'message': 'Failed to locate gps id {}'.format(id)}, 404 # success! return jsonify(gps.toDict())
def processGeolocation(self, geolocator, classification, croppedImg): """ Geolocation processing stuff common to both manual and autonomous. Called only by the processManualGeolocation and processAutonomousGeolocation methods """ dao = IncomingImageDAO(defaultConfigPath()) rawImg = dao.getImage(croppedImg.image_id) dao.close() if rawImg is None: print( "Failed to find raw image {} for autonomous cropped image {}!". format(croppedImg.image_id, croppedImg.crop_id)) return None dao = IncomingGpsDAO(defaultConfigPath()) gpsRaw = dao.getGpsByClosestTS(rawImg.time_stamp) dao.close() if gpsRaw is None: print("Failed to find gps measurement close to raw timestamp {}!". format(rawImg.time_stamp)) return None dao = IncomingStateDAO(defaultConfigPath()) stateRaw = dao.getStateByClosestTS(rawImg.time_stamp) dao.close() if stateRaw is None: print( "Failed to find state measurement close to raw timestamp {}!". format(rawImg.time_stamp)) return None # lat, lon = geolocator.calculate_geolocation(gpsRaw.lat, gpsRaw.lon, gpsRaw.alt, stateRaw.roll, stateRaw.pitch, stateRaw.yaw, croppedImg.crop_coordinate_tl.x, croppedImg.crop_coordinate_tl.y, croppedImg.crop_coordinate_br.x, croppedImg.crop_coordinate_br.y) lat, lon = geolocator.calculate_geolocation( gpsRaw.lat, gpsRaw.lon, -stateRaw.position[2], stateRaw.roll, stateRaw.pitch, stateRaw.yaw, croppedImg.crop_coordinate_tl.x, croppedImg.crop_coordinate_tl.y, croppedImg.crop_coordinate_br.x, croppedImg.crop_coordinate_br.y) return {'latitude': lat, 'longitude': lon}
def get(self, ts): dao = IncomingStateDAO(defaultConfigPath()) state = dao.getStateByClosestTS(ts) if state is None: return { 'message': 'Failed to get closest state measurement by time_stamp. Either: a) the table is empty or b) the time_stampe is < all timestamps in the table' }, 404 return jsonify(state.toDict())
def processAutonomousGeolocation(self, geolocator, classification): """ See documentation for processManualGeolocation function. This is the same thing but autonomous. """ dao = CroppedAutonomousDAO(defaultConfigPath()) croppedImg = dao.getImage(classification.crop_id) dao.close() if croppedImg is None: print( "Failed to find cropped image {} for autonomous classification {}!" .format(classification.crop_id, classification.class_id)) return updateDict = self.processGeolocation(geolocator, classification, croppedImg) dao = OutgoingAutonomousDAO(defaultConfigPath()) dao.updateClassification(classification.class_id, updateDict) dao.close()
def processManualGeolocation(self, geolocator, classification): """ Process an unlocated manual classification waiting in the queue. Basically this involves getting a bunch of information from a lot of the other database tables, and then using it as input to the actual geolocation algorithm. If successful, this will update the given classification with the calculated coordinates. Here's what we use: cropped_manual -> image_id, crop_coordinates incoming_image -> image timestamp incoming_gps -> mav gps coordinates at incoming_image timestamp incoming_gps -> mav state info at incoming_image timestamp @type geolocator: targetGeolocation @param geolocator: An instance of the targetGeolocation class, already initialized @type classification: outgoing_manual @param classification: A manual classification that has no geolocation coordinates yet. """ # now get all the info about gps, state and crop # as we do this we need to check that we actually get # data back from our queries. if we dont, then just # skip this classification dao = CroppedManualDAO(defaultConfigPath()) croppedImg = dao.getImage(classification.crop_id) dao.close() if croppedImg is None: print( "Failed to find cropped image {} for manual classification {}!" .format(classification.crop_id, classification.class_id)) return updateDict = self.processGeolocation(geolocator, classification, croppedImg) dao = OutgoingManualDAO(defaultConfigPath()) dao.updateClassification(classification.class_id, updateDict) dao.close()
def writeTargetToODLCFile(target, manual): imagePath = None # we have a crop_id from the classification generated for submission # now we need to take that and grab the cropped path croppedDao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) croppedInfo = croppedDao.getImage(target.crop_id) if croppedInfo is None: return None imagePath = croppedInfo.cropped_path prettyTargetOut = submitted_target(outgoingManualOrAutonomous=target, autonomous_in=(not manual)) prettyTargetOut.crop_path = imagePath auvsiDao = AuvsiOdlcDao() auvsiDao.addTarget(prettyTargetOut) return prettyTargetOut
def test(self): # insert some targets dao = OutgoingManualDAO(defaultConfigPath()) truncateTable('outgoing_manual') # see what it does with an empty table: self.assertIsNone(dao.submitPendingTarget(1)) testIns = outgoing_manual() testIns.crop_id = 42 testIns.latitude = 40.111 testIns.longitude = -111.111 testIns.orientation = 'N' testIns.shape = 'circle' testIns.background_color = 'white' testIns.alphanumeric = 'A' testIns.alphanumeric_color = 'black' self.assertIsNot(dao.addClassification(testIns), -1) testIns.crop_id = 43 testIns.latitude = 40.222 testIns.longitude = -111.222 testIns.orientation = 'NE' testIns.background_color = 'orange' self.assertIsNot(dao.addClassification(testIns), -1) testIns.crop_id = 44 testIns.latitude = 40.333 testIns.longitude = -111.333 testIns.alphanumeric_color = 'white' resultingId = dao.addClassification(testIns) self.assertIsNot(resultingId, -1) classResult = dao.getClassification(resultingId) submissionResult = dao.submitPendingTarget(classResult.target) self.assertAlmostEqual(submissionResult.latitude, 40.222) self.assertAlmostEqual(submissionResult.longitude, -111.222) self.assertEqual(submissionResult.orientation, 'NE') self.assertEqual(submissionResult.background_color, 'orange') self.assertEqual(submissionResult.alphanumeric_color, 'black') self.assertEqual(submissionResult.alphanumeric, 'A') self.assertEqual(submissionResult.shape, 'circle') # make sure that when we submit another classification that belongs # to the same target that its submitted state automatically goes to # 'inherited_submission' testIns.crop_id = 45 resultingId = dao.addClassification(testIns) self.assertNotEqual(resultingId, -1) classResult = dao.getClassification(resultingId) self.assertIsNotNone(classResult) self.assertEqual(classResult.submitted, 'inherited_submission')
def test(self): truncateTable('incoming_image') dao = IncomingImageDAO(defaultConfigPath()) self.assertIsNotNone(dao) # test with empty table self.assertIsNone(dao.getNextImage(True)) self.assertIsNone(dao.getNextImage(False)) model = incoming_image() model.time_stamp = 1547453775.2 model.focal_length = 16.0 model.image_path = '/im/a/totally/real/path/i/swear.jpg' model.manual_tap = False model.autonomous_tap = False resultingId = dao.addImage(model) self.assertNotEqual(resultingId, -1) # identical timestamps should make no difference model.focal_length = 16.0 model.image_path = '/im/a/totally/real/path/2/i/swear.jpg' resultingId2 = dao.addImage(model) self.assertNotEqual(resultingId2, -1) resultModel = dao.getNextImage(True) self.assertIsNotNone(resultModel) self.assertEqual(resultModel.image_id, resultingId) self.assertTrue(resultModel.manual_tap) self.assertFalse(resultModel.autonomous_tap) resultModel = dao.getNextImage(True) self.assertIsNotNone(resultModel) self.assertEqual(resultModel.image_id, resultingId2) self.assertTrue(resultModel.manual_tap) self.assertFalse(resultModel.autonomous_tap) resultModel = dao.getNextImage(False) self.assertIsNotNone(resultModel) self.assertEqual(resultModel.image_id, resultingId) self.assertTrue(resultModel.manual_tap) self.assertTrue(resultModel.autonomous_tap) resultModel = dao.getNextImage(True) self.assertIsNone(resultModel) resultModel = dao.getNextImage(False) self.assertIsNotNone(resultModel) self.assertEqual(resultModel.image_id, resultingId2) self.assertTrue(resultModel.manual_tap) self.assertTrue(resultModel.autonomous_tap) resultModel = dao.getNextImage(False) self.assertIsNone(resultModel)
def get(self, target_id): manual = checkXManual(request) dao = SubmittedTargetDAO(defaultConfigPath()) resultTarget = dao.getTarget(target_id, (not manual)) if resultTarget is None: return { 'message': 'Failed to retrieve target {}'.format(target_id) }, 404 # get the crop id from the path the target is using. # the crop_id is much more useful client-size than a crop_path outDict = resultTarget.toDict() croppedDao = CroppedManualDAO( defaultConfigPath()) if manual else CroppedAutonomousDAO( defaultConfigPath()) croppedImg = croppedDao.getImageWithCropPath(outDict['crop_path']) if croppedImg is not None: outDict['crop_id'] = croppedImg.crop_id return jsonify(outDict)
def get(self, ts): dao = IncomingGpsDAO(defaultConfigPath()) gps = dao.getGpsByClosestTS(ts) # response validation: if gps is None: return { 'message': 'Failed to get closest gps by time_stamp. Is the table empty?' }, 404 # success! return jsonify(gps.toDict())
def test(self): model = incoming_gps() model.time_stamp = 1547453775.2 model.lat = 40.111 model.lon = -111.222 model.alt = 1234.5 truncateTable('incoming_gps') dao = IncomingGpsDAO(defaultConfigPath()) resultingId = dao.addGps(model) self.assertIsNotNone(resultingId) self.assertNotEqual(resultingId, -1)
def test(self): model = incoming_state() model.time_stamp = 1547453775.2 model.roll = 40.111 model.pitch = -111.222 model.yaw = 12.3 truncateTable('incoming_state') dao = IncomingStateDAO(defaultConfigPath()) self.assertIsNotNone(dao) resultingId = dao.addState(model) self.assertIsNotNone(resultingId) self.assertNotEqual(resultingId, -1)