Not logged in!

Using feather Effectively

Best practises and additional information about the library
Dependancies, packages and libraries

Coming soon

Where possible, pass data via components

In summary (you can read the explanation below for a description), send data to the next step through components if possible (e.g. images). If this is not possible, namely when you output data from a model function which you want to pass to the next step, you can send your data variable in the return of the function. The only requirement for this variable is that it must be serialisable by pickle.

Your usecase may involve you needing to send data from one function to the next. The data may either be files that a user has already uploaded (e.g. a user uploaded images in the 1st step which are required by the 2nd and 3rd step), or data obtained by processing some inputs (e.g. we extracted some image features from an image which needs to be sent to the next step). Depending on which of the situations you're facing, you should send data to the next step in different ways.

Internally, when data is passed through a component, we perform serialisation based on the expected type of the input (e.g. we know that images sent to Image.View are going to be numpy arrays). This knowledge allows us to represent this data in an optimal way for file size (and hence speed of execution) purposes.

However, we cannot make the same assumption about arbitrary data that you output through the use of your own model function (e.g. maybe you output image features you want to use in a future step). To allow flexibility in the data you pass, we allow you to pass any data which is pickle serialisable. We HIGHLY recommend against passing images/videos/audio through python types as their in-memory representation is a lot higher than their native file object (e.g. a 600kb image can be 60mb when represented as a numpy array).

1def init():
2    return ftr.File.Upload(types=["images"], title="Upload your images for captioning")
3
4def run_object_detection(uploader):
5    images = uploader.get_image_files()
6
7    detected_objects, image_features = my_object_detector(images)
8
9    user_selected_objects = ftr.Image.WithSelectOne(images, detected_objects, 
10                                                    title="Select objects to generate a question on")
11    # DO THIS.
12    return user_selected_objects, image_features
13
14    ########### DON'T DO THIS.
15    # return user_selected_objects, image_features, images
16    ###########
17
18def run_captioning(user_selected_objects, image_features):
19    # access images through user_selected_objects:
20    images = user_selected_objects.images
21
22    # image_features is your np.array of image_features :)
23
24    ### rest of code ###

Getting all files in a working directory

You may have many files in your working directory which are required to run your code. This problem is especially amplified if you include a package in your directory which isn't as straightforward as `pip install package` (see Dependances, packages and libraries for more information). When this is the case, we provide a simple helper function that can recursively extract (from your current working directory) all the code_files and model_files required to run your code.

By default, ftr.helpers.get_all_files_from_curr_dir will search for extensions ending in: [".ckpt", ".pt", ".pth"] to try and identify models. If you've saved your model with a different extension, you can use the include_model_extensions and exclude_model_extensions to provide a list of extensions that you want to either exclude or include from file search. Files which AREN'T determined to be a model file will be added as a code file.

code_files, model_files = ftr.helpers.get_all_files_from_curr_dir(
                                              include_model_extensions=None, exclude_model_extensions=None)

Don't use .get()

All components which require user interaction have a .get() method associated with them, allowing you to easily obtain the required output. In all these cases, .get() ultimately calls another accessor method - filtering down to the relevant function by passed arguments if relevant. These accessor methods are component specific, and are documented for each component.

However, the use of .get() leads to polymorphic return types, and shys away from self-documenting code. It is not immediately clear to another reader of your code (or your future self in about 2 weeks!) what accessor is actually attempting to be accessed to get the data you want. Thus, we recommend you to use your desired accessor directly instead of .get().

1def init():
2    return ftr.File.Upload(types=["images"], title="Upload your images for captioning")
3
4def run_captioning(uploader):
5    # DO THIS.
6    images = uploader.get_image_files()
7    
8    # DON'T DO THIS.
9    images = uploader.get(format="images")
10
11    ### rest of code ###