def scale_image(img_id_in, img_id_out, group_id, **kwargs): # only works on black and white images for now # that should only be a problem for images that aren't of type 'L'. Add this test if 'scale_type' in kwargs: scale_type = kwargs['colorize_bicubic'] else: scale_type = 'colorize_bicubic' #TODO add a test to show that scale_type makes it in through kwargs group_document = get_group_document(group_id) group_id = group_document['_id'] img_dict_in = find_picture(str(img_id_in)) img_filename_in = img_dict_in['filename'] img_filename_out = build_picture_name(img_id_out) pic_path_in = img_dict_in['uri'] pic_path_out = build_picture_path(picture_name=img_filename_out, snap_id=img_dict_in['snap_id']) image_in = Image.open(pic_path_in) # scale image scale_method = Image.BICUBIC if scale_type and 'bilinear' in scale_type: scale_method == Image.BILINEAR if scale_type and 'antialias' in scale_type: scale_method == Image.ANTIALIAS width = current_app.config['STILL_IMAGE_WIDTH'] height = current_app.config['STILL_IMAGE_HEIGHT'] image_scaled = image_in.resize((width, height), scale_method) #TODO: below is terribly inefficient. After I look at PIL internals I should be able to do better #blur image if scale_type and 'blur' in scale_type: for i in range(1, 10): image_scaled = image_scaled.filter(ImageFilter.BLUR) #colorize image if scale_type and 'colorize' in scale_type: (colorize_range_low, colorize_range_high) = ('#000080', '#FFD700') if 'colorize_range_low' in group_document and 'colorize_range_high' in group_document: colorize_range_low = group_document['colorize_range_low'] colorize_range_high = group_document['colorize_range_high'] image_colorized = ImageOps.colorize(image_scaled, colorize_range_low, colorize_range_high) image_colorized.save(pic_path_out) else: image_scaled.save(pic_path_out) img_dict_out = { '_id': str(img_id_out), 'type': 'picture', 'source': 'analysis', 'source_image_id': str(img_id_in), 'analysis_type': scale_type, 'group_id': group_id, 'snap_id': img_dict_in['snap_id'], 'filename': img_filename_out, 'uri': pic_path_out, 'created': str(datetime.datetime.now()) } save_picture_document(img_dict_out)
def test_picam_still_calls_all_chained_tasks(self, cs_take_picam_still, as_clean_up_files, as_send_mail): snap_id = uuid.uuid4() group_id = get_group_document('current')['_id'] ct.take_picam_still(snap_id=snap_id, group_id=group_id, delay=0, repeat=0) cs_take_picam_still.assert_called_once_with(snap_id, group_id, ANY, ANY) as_clean_up_files.assert_called_once_with(snap_id, group_id) as_send_mail.assert_called_once_with(snap_id, group_id)
def distort_image_shepards_fixed(img_id_in, img_id_out, group_id, **kwargs): group_document = get_group_document(group_id) group_id = group_document['_id'] img_dict_in = find_picture(str(img_id_in)) img_filename_out = build_picture_name(img_id_out) pic_path_in = img_dict_in['uri'] pic_path_out = build_picture_path(picture_name=img_filename_out, snap_id=img_dict_in['snap_id']) command = "convert {0} -distort Shepards '300,110 350,140 600,310 650,340' {1}".format(pic_path_in, pic_path_out) os.system(command) img_dict_out = { '_id': str(img_id_out), 'type': 'picture', 'source': 'analysis', 'source_image_id': str(img_id_in), 'analysis_type': 'distort', 'group_id': group_id, 'snap_id': img_dict_in['snap_id'], 'filename': img_filename_out, 'uri': pic_path_out, 'created': str(datetime.datetime.now()) } save_picture_document(img_dict_out)
def distort_image_shepards_fixed(img_id_in, img_id_out, group_id, **kwargs): group_document = get_group_document(group_id) group_id = group_document['_id'] img_dict_in = find_picture(str(img_id_in)) img_filename_out = build_picture_name(img_id_out) pic_path_in = img_dict_in['uri'] pic_path_out = build_picture_path(picture_name=img_filename_out, snap_id=img_dict_in['snap_id']) command = "convert {0} -distort Shepards '300,110 350,140 600,310 650,340' {1}".format( pic_path_in, pic_path_out) os.system(command) img_dict_out = { '_id': str(img_id_out), 'type': 'picture', 'source': 'analysis', 'source_image_id': str(img_id_in), 'analysis_type': 'distort', 'group_id': group_id, 'snap_id': img_dict_in['snap_id'], 'filename': img_filename_out, 'uri': pic_path_out, 'created': str(datetime.datetime.now()) } save_picture_document(img_dict_out)
def update_group(group_id): group_dict = get_group_document(group_id) if request.headers['Content-Type'] == 'application/json': for k in request.json.keys(): if doc_attribute_can_be_set(k): group_dict[k] = request.json[k] save_document(group_dict) return Response(json.dumps(group_dict), status=200, mimetype='application/json')
def list_groups(): ''' Lists all groups Includes paging and searching on any field in the group document ''' # If no settings or groups yet create a group group_dict = get_group_document('current') return generic_list_view(document_type='group')
def scale_image(img_id_in, img_id_out, group_id, **kwargs): # only works on black and white images for now # that should only be a problem for images that aren't of type 'L'. Add this test if 'scale_type' in kwargs: scale_type = kwargs['colorize_bicubic'] else: scale_type = 'colorize_bicubic' #TODO add a test to show that scale_type makes it in through kwargs group_document = get_group_document(group_id) group_id = group_document['_id'] img_dict_in = find_picture(str(img_id_in)) img_filename_in = img_dict_in['filename'] img_filename_out = build_picture_name(img_id_out) pic_path_in = img_dict_in['uri'] pic_path_out = build_picture_path(picture_name=img_filename_out, snap_id=img_dict_in['snap_id']) image_in = Image.open(pic_path_in) # scale image scale_method = Image.BICUBIC if scale_type and 'bilinear' in scale_type: scale_method == Image.BILINEAR if scale_type and 'antialias' in scale_type: scale_method == Image.ANTIALIAS width = current_app.config['STILL_IMAGE_WIDTH'] height = current_app.config['STILL_IMAGE_HEIGHT'] image_scaled = image_in.resize((width, height), scale_method) #TODO: below is terribly inefficient. After I look at PIL internals I should be able to do better #blur image if scale_type and 'blur' in scale_type: for i in range(1,10): image_scaled = image_scaled.filter(ImageFilter.BLUR) #colorize image if scale_type and 'colorize' in scale_type: (colorize_range_low, colorize_range_high) = ('#000080', '#FFD700') if 'colorize_range_low' in group_document and 'colorize_range_high' in group_document: colorize_range_low = group_document['colorize_range_low'] colorize_range_high = group_document['colorize_range_high'] image_colorized = ImageOps.colorize(image_scaled, colorize_range_low, colorize_range_high) image_colorized.save(pic_path_out) else: image_scaled.save(pic_path_out) img_dict_out = { '_id': str(img_id_out), 'type': 'picture', 'source': 'analysis', 'source_image_id': str(img_id_in), 'analysis_type': scale_type, 'group_id': group_id, 'snap_id': img_dict_in['snap_id'], 'filename': img_filename_out, 'uri': pic_path_out, 'created': str(datetime.datetime.now()) } save_picture_document(img_dict_out)
def get_group_gallery(group_id): try: group_dict = get_group_document(group_id) group_id = group_dict['_id'] args_dict = {'group_id': group_id} (page, items_per_page) = get_paging_info_from_request(request) pictures_dict = find_pictures(args_dict, gallery_url_not_null=True, page=page, items_per_page=items_per_page) except Exception as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json') return Response(json.dumps(pictures_dict), status=200, mimetype='application/json')
def get_group(group_id): try: group_dict = get_group_document(group_id) except NotFoundError as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json') return Response(json.dumps(group_dict), status=200, mimetype='application/json')
def test_get_group_document_current_gets_the_current_group_document(self): settings_doc = adms.get_settings_document() some_other_group_id = str(uuid.uuid4()) some_other_group_doc = {'type': 'group'} current_app.db[some_other_group_id] = some_other_group_doc group_doc_from_current = adms.get_group_document('current') assert group_doc_from_current['_id'] != some_other_group_id assert group_doc_from_current['_id'] == settings_doc['current_group_id']
def test_take_thermal_still_calls_all_chained_tasks_multiple_times_when_repeat_specified(self, cs_take_thermal_still, ans_scale_image, ads_clean_up_files, ads_send_mail): snap_id = uuid.uuid4() group_id = get_group_document('current')['_id'] ct.take_thermal_still(snap_id=snap_id, group_id=group_id, delay=0, repeat=1) cs_take_thermal_still.assert_has_calls([call(snap_id, group_id, ANY), call(ANY, group_id, ANY)]) ans_scale_image.assert_has_calls([call(ANY, ANY, group_id), call(ANY, ANY, group_id)]) ads_send_mail.assert_has_calls([call(snap_id, group_id), call(ANY, group_id)]) ads_clean_up_files.assert_has_calls([call(snap_id, group_id), call(ANY, group_id)])
def get_group_gallery(group_id): ''' Fetches the photo gallery for a supplied group id Includes paging and searching on any field in the picture document ''' try: group_dict = get_group_document(group_id) group_id = group_dict['_id'] return generic_list_view(document_type='picture', args_dict={'group_id': group_id, 'gallery_url_not_null': True}) except Exception as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json')
def test_take_thermal_still_skips_scale_image_when_requested(self, cs_take_thermal_still, ans_scale_image, ads_clean_up_files, ads_send_mail): snap_id = uuid.uuid4() group_id = get_group_document('current')['_id'] ct.take_thermal_still(snap_id=snap_id, group_id=group_id, delay=0, repeat=0, scale_image=False) cs_take_thermal_still.assert_called_once_with(snap_id, group_id, ANY) ans_scale_image.assert_not_called() ads_send_mail.assert_called_once_with(snap_id, group_id) ads_clean_up_files.assert_called_once_with(snap_id, group_id)
def get_merge_type(group_id, **kwargs): if 'merge_type' in kwargs and kwargs['merge_type']: merge_type = kwargs['merge_type'] print 'getting merge type from kwargs: ' + merge_type else: group_document = get_group_document(group_id) if 'merge_type' in group_document: merge_type = group_document['merge_type'] print 'getting merge type from group_doc: ' + merge_type else: merge_type = 'screen' print 'defaulting merge type to screen' return merge_type
def get_merge_type(group_id, **kwargs): if 'merge_type' in kwargs and kwargs['merge_type']: merge_type = kwargs['merge_type'] print 'getting merge type from kwargs: '+merge_type else: group_document = get_group_document(group_id) if 'merge_type' in group_document: merge_type = group_document['merge_type'] print 'getting merge type from group_doc: '+merge_type else: merge_type = 'screen' print 'defaulting merge type to screen' return merge_type
def test_picam_still_calls_all_chained_tasks_multiple_times_when_repeat_specified(self, cs_take_picam_still, as_clean_up_files, as_send_mail): snap_id = uuid.uuid4() group_id = get_group_document('current')['_id'] ct.take_picam_still(snap_id=snap_id, group_id=group_id, delay=0, repeat=2) tps_calls = [call(snap_id, group_id, ANY, ANY), call(ANY, group_id, ANY, ANY), call(ANY, group_id, ANY, ANY)] ascuf_calls = [call(snap_id, group_id), call(ANY, group_id), call(ANY, group_id)] assm_calls = [call(snap_id, group_id), call(ANY, group_id), call(ANY, group_id)] cs_take_picam_still.assert_has_calls(tps_calls) as_clean_up_files.assert_has_calls(ascuf_calls) as_send_mail.assert_has_calls(assm_calls)
def get_group_pictures(group_id): ''' Fetches pictures for a supplied group id Includes paging and searching on any field in the picture document ''' try: group_dict = get_group_document(group_id) group_id = group_dict['_id'] return generic_list_view(document_type='picture', args_dict={'group_id': group_id}) except Exception as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json')
def merge_images(img1_primary_id_in, img1_alternate_id_in, img2_id_in, img_id_out, group_id): #deal with the fact that different merge methods require different parameters group_document = get_group_document(group_id) group_id = group_document['_id'] img1_id_in = img1_primary_id_in if picture_exists(img1_alternate_id_in): img1_id_in = img1_alternate_id_in if 'merge_type' in group_document: merge_type = group_document['merge_type'] if hasattr(ImageChops, merge_type): merge_method = getattr(ImageChops, merge_type) else: merge_method = getattr(ImageChops, 'screen') img1_dict_in = find_picture(str(img1_id_in)) img1_filename_in = img1_dict_in['filename'] img2_dict_in = find_picture(str(img2_id_in)) img2_filename_in = img2_dict_in['filename'] img_filename_out = build_picture_name(img_id_out) pic1_path_in = build_picture_path(picture_name=img1_filename_in, snap_id=img1_dict_in['snap_id']) pic2_path_in = build_picture_path(picture_name=img2_filename_in, snap_id=img1_dict_in['snap_id']) pic_path_out = build_picture_path(picture_name=img_filename_out, snap_id=img1_dict_in['snap_id']) image1_in = Image.open(pic1_path_in) image2_in = Image.open(pic2_path_in) image_out = merge_method(image1_in.convert('RGBA'), image2_in.convert('RGBA')) image_out.save(pic_path_out) img_dict_out = { '_id': str(img_id_out), 'type': 'picture', 'source': 'merge', 'source_image_id_1': str(img1_id_in), 'source_image_id_2': str(img2_id_in), 'merge_type': merge_type, 'group_id': group_id, 'snap_id': img1_dict_in['snap_id'], 'filename': img_filename_out, 'uri': pic_path_out, 'created': str(datetime.datetime.now()) } save_picture_document(img_dict_out)
def update_group(group_id): ''' Updates group record ''' try: group_dict = get_group_document(group_id) if request.headers['Content-Type'] == 'application/json': for k in request.json.keys(): if doc_attribute_can_be_set(k): group_dict[k] = request.json[k] update_generic(group_dict, 'group') return Response(json.dumps(group_dict), status=200, mimetype='application/json') return Response(json.dumps('problem with request data'), status=409, mimetype='application/json') except Exception as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json')
def get_group_pictures(group_id): try: group_dict = get_group_document(group_id) group_id = group_dict['_id'] args_dict = {'group_id': group_id} (page, items_per_page) = get_paging_info_from_request(request) pictures_dict = find_pictures(args_dict, page=page, items_per_page=items_per_page) except Exception as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json') return Response(json.dumps(pictures_dict), status=200, mimetype='application/json')
def get_group(group_id): ''' Gets a particular group supports these levels of information: - group dict only - links to photos - photos included, grouped by snap id ''' try: if 'child_objects' in request.args: # TODO add documentation in sphinx group_dict = get_group_document_with_child_objects(group_id) elif 'child_links' in request.args: # TODO add documentation in sphinx group_dict = get_group_document_with_child_links(group_id) else: group_dict = get_group_document(group_id) return Response(json.dumps(group_dict), status=200, mimetype='application/json') except Exception as e: return Response(json.dumps(e.message), status=e.status_code, mimetype='application/json')
def test_take_both_still_calls_all_chained_tasks(self, cs_take_picam_still, cs_take_thermal_still, ans_scale_image, ans_distort_image_shepards_fixed, ms_merge_image, ads_clean_up_files, ads_send_mail): snap_id = uuid.uuid4() group_id = get_group_document('current')['_id'] ct.take_both_still(snap_id=snap_id, group_id=group_id, delay=0, repeat=0) cs_take_thermal_still.assert_called_once_with(snap_id, group_id, ANY) cs_take_picam_still.assert_called_once_with(snap_id, group_id, ANY, ANY) ans_scale_image.assert_called_once_with(ANY, ANY, group_id) ans_distort_image_shepards_fixed.assert_called_once_with(ANY, ANY, group_id) ms_merge_image.assert_called_once_with(ANY, ANY, ANY, ANY, group_id) ads_send_mail.assert_called_once_with(snap_id, group_id) ads_clean_up_files.assert_called_once_with(snap_id, group_id)
def scale_image(img_id_in, img_id_out, group_id, **kwargs): # only works on black and white images for now # that should only be a problem for images that aren't of type 'L'. Add this test group_document = get_group_document(group_id) if 'scale_type' in kwargs: scale_type = kwargs['scale_type'] else: if 'scale_type' in group_document: scale_type = group_document['scale_type'] else: scale_type = 'colorize_bicubic' group_id = group_document['_id'] img_dict_in = get_document_with_exception(str(img_id_in), 'picture') img_filename_in = img_dict_in['filename'] img_filename_out = build_picture_name(img_id_out) pic_path_in = img_dict_in['uri'] pic_path_out = build_picture_path(picture_name=img_filename_out, snap_id=img_dict_in['snap_id']) image_in = Image.open(pic_path_in) image_scaled = scale_image_subtask(scale_type, image_in) image_scaled = blur_image(scale_type, image_scaled) image_colorized = colorize_image(scale_type, group_document, image_scaled) image_colorized.save(pic_path_out) img_dict_out = { '_id': str(img_id_out), 'type': 'picture', 'source': 'analysis', 'source_image_id': str(img_id_in), 'analysis_type': scale_type, 'group_id': group_id, 'snap_id': img_dict_in['snap_id'], 'filename': img_filename_out, 'uri': pic_path_out, 'created': str(datetime.datetime.now()) } save_generic(img_dict_out, 'picture')
def test_clean_up_files_cleans_pictures_from_the_snap_and_updates_snap_document(self): snap_id = uuid.uuid4() group_id = adms.get_group_document('current')['_id'] pic_ids = self.build_three_pictures(snap_id) for pic_id in pic_ids: pic_doc = get_document(pic_id) assert os.path.isfile(pic_doc['uri']) assert str(snap_id) in pic_doc['uri'] assert os.path.isdir(os.path.join(current_app.config['PICTURE_SAVE_DIRECTORY'], str(snap_id))) with current_app.test_request_context('/whatever'): # needs a context because clean_up_files calls search_generic adms.clean_up_files(snap_id, group_id) for pic_id in pic_ids: pic_doc = get_document(pic_id) filename = build_picture_name(pic_id) expected_picture_path = os.path.join(current_app.config['PICTURE_SAVE_DIRECTORY'], filename) assert pic_doc['uri'] == expected_picture_path assert os.path.isfile(expected_picture_path) assert not os.path.isdir(os.path.join(current_app.config['PICTURE_SAVE_DIRECTORY'], str(snap_id))) snap_document = get_document(snap_id) assert snap_document['files_have_been_cleaned_up'] == True
def take_picam_still(snap_id, group_id, normal_exposure_pic_id, long_exposure_pic_id): ''' Top level method in the camera service for taking a still image via the picam (regular raspberry pi) camera. Also saves a picture record to the db Depending on settings and real time conditions, may cause a second, longer exposure to be taken ''' group_document = get_group_document(str(group_id)) retake_picam_pics_when_dark = get_retake_picam_pics_when_dark_setting(group_document) brightness_threshold = get_brightness_threshold(group_document) picture_name = build_picture_name(normal_exposure_pic_id) pic_path = build_picture_path(picture_name=picture_name, snap_id=snap_id) pic_dict = { '_id': str(normal_exposure_pic_id), 'type': 'picture', 'source': 'picam', 'exposure_type': 'standard', 'group_id': str(group_id), 'snap_id': str(snap_id), 'filename': picture_name, 'uri': pic_path, 'created': str(datetime.datetime.now()) } take_standard_exposure_picam_still(pic_path) save_picture(pic_dict) image_is_too_dark = check_if_image_is_too_dark(pic_path, brightness_threshold) if image_is_too_dark and retake_picam_pics_when_dark: picture_name = build_picture_name(long_exposure_pic_id) pic_path = build_picture_path(picture_name=picture_name, snap_id=snap_id) pic_dict2 = copy.deepcopy(pic_dict) pic_dict2['exposure_type'] = 'long' pic_dict2['_id'] = str(long_exposure_pic_id) pic_dict2['filename'] = picture_name pic_dict2['uri'] = pic_path pic_dict2['created'] = str(datetime.datetime.now()) take_long_exposure_picam_still(pic_path) save_picture(pic_dict2)
def test_get_group_document_fails_if_doc_is_not_of_type_group(self): the_id = str(uuid.uuid4()) the_doc = {'type': 'not_group'} current_app.db[the_id] = the_doc with pytest.raises(NotFoundError): the_returned_doc = adms.get_group_document(the_id)
def test_get_group_document_succeeds_if_doc_is_of_type_group(self): the_id = str(uuid.uuid4()) the_doc = {'type': 'group'} current_app.db[the_id] = the_doc the_returned_doc = adms.get_group_document(the_id) assert the_returned_doc['_id'] == the_id