Package: handwriting-features
Handwriting featuresThis package provides aPyPi-installableeasy-to-use modern library for the extraction of a variety of common handwriting features focused on the domain of kinematic, dynamic, spatial, and temporal analysis of online handwriting/drawing. It is built on top of theHandwriting samplepackage for easy class-based manipulation with online handwriting data.It also provides an interface for the Featurizer API:Server side(RESTful API for feature extraction via injected features extraction library)Client side(lightweight client application)The full programming sphinx-generated docs can be seen in theofficial documentation.Contents:InstallationFeaturesUsageInterfaceExamplesLicenseContributorsInstallationpip install handwriting-featuresFeaturesThe following list of handwriting features is supported:kinematic featuresvelocityaccelerationjerkdynamic featuresazimuthtiltpressurespatial featuresstroke lengthstroke heightstroke widthwriting lengthwriting heightwriting widthnumber of intra-stroke intersectionsrelative number of intra-stroke intersectionstotal number of intra-stroke intersectionsrelative total number of intra-stroke intersectionsnumber of inter-stroke intersectionsrelative number of inter-stroke intersectionsvertical peaks indicesvertical valleys indicesvertical peaks valuesvertical valleys valuesvertical peaks velocityvertical valleys velocityvertical peaks distancevertical valleys distancevertical peaks durationvertical valleys durationtemporal featuresstroke durationratio of stroke durations (on-surface / in-air strokes)writing durationwriting duration overallratio of writing durations (on-surface / in-air writing)number of interruptionsnumber of interruptions relativecomposite featureswriting tempowriting stopsnumber of changes in x profilenumber of changes in y profilenumber of changes in azimuthnumber of changes in tiltnumber of changes in pressurenumber of changes in velocity profilerelative number of changes in x profilerelative number of changes in y profilerelative number of changes in azimuthrelative number of changes in tiltrelative number of changes in pressurerelative number of changes in velocity profileUsageThe main entry point and the class to be used for the computation of the handwriting features is namedHandwritingFeatures. To compute the features, an instance ofHandwritingFeaturesmust be created using valid handwriting data in the form of an instance of the wrapper on top of theHadwritingSamplenamedHandwritingSampleWrapper. TheHandwritingSampleis a class for the manipulation with online handwriting data installed fromthis repository. TheHandwritingSampleWrapperis used to enrich theHandwritingSamplewith the capabilities specific to the feature extraction.To support alternative ways of instantiating theHandwritingFeaturesfrom handwriting data stored in various data/file formats, theHandwritingFeaturesprovides the following alternative constructor methods:for various data formatsfrom_listfrom_numpy_arrayfrom_pandas_dataframefor various file formatsfrom_jsonfrom_svcAfter theHandwritingFeaturesobject is instantiated, the supported handwriting features can be computed using the following methods:kinematic featuresvelocityaccelerationjerkdynamic featuresazimuthtiltpressurespatial featuresstroke_lengthstroke_heightstroke_widthwriting_lengthwriting_heightwriting_widthnumber_of_intra_stroke_intersectionsrelative_number_of_intra_stroke_intersectionstotal_number_of_intra_stroke_intersectionsrelative_total_number_of_intra_stroke_intersectionsnumber_of_inter_stroke_intersectionsrelative_number_of_inter_stroke_intersectionsvertical_peaks_indicesvertical_valleys_indicesvertical_peaks_valuesvertical_valleys_valuesvertical_peaks_velocityvertical_valleys_velocityvertical_peaks_distancevertical_valleys_distancevertical_peaks_durationvertical_valleys_durationtemporal featuresstroke_durationratio_of_stroke_durationswriting_durationwriting_duration_overallratio_of_writing_durationsnumber_of_interruptionsnumber_of_interruptions_relativecomposite featureswriting_tempowriting_stopsnumber_of_changes_in_x_profilenumber_of_changes_in_y_profilenumber_of_changes_in_azimuthnumber_of_changes_in_tiltnumber_of_changes_in_pressurenumber_of_changes_in_velocity_profilerelative_number_of_changes_in_x_profilerelative_number_of_changes_in_y_profilerelative_number_of_changes_in_azimuthrelative_number_of_changes_in_tiltrelative_number_of_changes_in_pressurerelative_number_of_changes_in_velocity_profileFor more information, see theExamplessection.InterfaceBesides, the convenient use of theHandwritingFeaturesinterface class, the library also provides an interface for theFeaturizer APIatsrc/handwriting_features/interface/featurizer/. The Featurizer API supports the feature extraction from handwriting data of 1-M subjects given the pipeline of features. This offers an additional option to extract a variety of handwriting features via a micro-service type of architecture via injecting the handwriting features library into the API as the feature extractor to be used. For more information, see theofficial documentationof the Featurizer API.ExamplesThe package comes with examples showing how to compute/plot the handwriting features. For this purpose, it also provides example data from a subset of 4 writers coming from the Parkinson's Disease Handwriting Database (PaHaW): 2 healthy controls (HC; 1 male, 1 female) and 2 patients with Parkinson's disease (PD; 1 male, 1 female).For more information about the example data, see the info file atexamples/data/info.jsonor visit the official website ofBrain Diseases Analysis Laboratory.1. compute handwriting featuresimportosfromhandwriting_features.featuresimportHandwritingFeatures# Prepare the path to example datadata_path=os.path.join(os.path.dirname(os.path.realpath(__file__)),"..","data")# Prepare the subject groupsubject_group="HC-female"# subject_group = "HC-male"# subject_group = "PD-female"# subject_group = "PD-male"# Prepare the filename of the signal to be usedsignal_name="00026_w.cz.fnusa.1_1.svc"# Prepare the handwriting variablesvariables=["y","x","time","pen_status","azimuth","tilt","pressure"]# Instantiate the handwriting features object from an example signalfeature_path=str(os.path.join(data_path,subject_group,signal_name))feature_data=HandwritingFeatures.from_svc(feature_path,variables)# Prepare the sampling frequencyfs=133# Handwriting features:## 1. Kinematic features#    a) velocity#    b) acceleration#    c) jerk# 2. Dynamic features#    a) azimuth#    b) tilt#    c) pressure# 3. Spatial features#    a) stroke length#    b) stroke height#    c) stroke width#    d) writing length#    e) writing height#    f) writing width#    g) number of intra-stroke intersections#    h) relative number of intra-stroke intersections#    i) total number of intra-stroke intersections#    j) relative total number of intra-stroke intersections#    k) number of inter-stroke intersections#    l) relative number of inter-stroke intersections#    m) vertical peaks indices#    n) vertical valleys indices#    o) vertical peaks values#    p) vertical valleys values#    q) vertical peaks velocity#    r) vertical valleys velocity#    s) vertical peaks distance#    t) vertical valleys distance#    u) vertical peaks duration#    v) vertical valleys duration# 4. Temporal features#    a) stroke duration#    b) ratio of stroke durations (on-surface / in-air strokes)#    c) writing duration#    d) writing duration overall#    e) ratio of writing durations (on-surface / in-air writing)#    f) number of interruptions#    g) number of interruptions_relative# 5. Composite features#    a) writing tempo#    b) writing stops#    c) number of changes in x profile#    d) number of changes in y profile#    e) number of changes in azimuth#    f) number of changes in tilt#    g) number of changes in pressure#    h) number of changes in velocity profile#    i) relative number of changes in x profile#    j) relative number of changes in y profile#    k) relative number of changes in azimuth#    l) relative number of changes in tilt#    m) relative number of changes in pressure#    n) relative number of changes in velocity profile# 1. Kinematic featuresfeature_data.velocity(axis="x",in_air=False,statistics=["mean","std"])feature_data.acceleration(axis="y",in_air=True,statistics=["median","iqr"])feature_data.jerk(axis="xy",in_air=False)# 2. Dynamic featuresfeature_data.azimuth(in_air=False,statistics=["cv_parametric"])feature_data.tilt(in_air=True,statistics=["cv_nonparametric"])feature_data.pressure(statistics=())# 3. Spatial featuresfeature_data.stroke_length(in_air=False,statistics=["quartile_1","quartile_3"])feature_data.stroke_height(in_air=True,statistics=["slope_of_linear_regression"])feature_data.stroke_width(in_air=False,statistics=())# ...# 4. Temporal featuresfeature_data.stroke_duration(in_air=False,statistics=["percentile_5","percentile_95"])feature_data.ratio_of_stroke_durations(statistics=())feature_data.writing_duration(in_air=True)feature_data.ratio_of_writing_durations()# ...# 5. Composite featuresfeature_data.writing_tempo(in_air=False)feature_data.writing_stops(statistics=["mean","std"])# ...2. plot handwriting featuresimportosimportmatplotlib.pyplotaspltfrompprintimportpprintfromhandwriting_features.featuresimportHandwritingFeatures# Prepare the path to example datadata_path=os.path.join(os.path.dirname(os.path.realpath(__file__)),"..","data")# Prepare the subject groupsubject_group="HC-female"# subject_group = "HC-male"# subject_group = "PD-female"# subject_group = "PD-male"# Prepare the filename of the signal to be usedsignal_name="00026_w.cz.fnusa.1_1.svc"# Prepare the handwriting variablesvariables=["y","x","time","pen_status","azimuth","tilt","pressure"]# Instantiate the handwriting features object from an example signalfeature_path=str(os.path.join(data_path,subject_group,signal_name))feature_data=HandwritingFeatures.from_svc(feature_path,variables)# Extract the loaded data (wrapped handwriting sample)loaded_data=feature_data.wrapper# Prepare the sampling frequencyfs=133# Handwriting features:## 1. Kinematic features#    a) velocity#    b) acceleration#    c) jerk# 2. Dynamic features#    a) azimuth#    b) tilt#    c) pressure# 3. Spatial features#    a) stroke length#    b) stroke height#    c) stroke width#    d) writing length#    e) writing height#    f) writing width#    g) number of intra-stroke intersections#    h) relative number of intra-stroke intersections#    i) total number of intra-stroke intersections#    j) relative total number of intra-stroke intersections#    k) number of inter-stroke intersections#    l) relative number of inter-stroke intersections#    m) vertical peaks indices#    n) vertical valleys indices#    o) vertical peaks values#    p) vertical valleys values#    q) vertical peaks velocity#    r) vertical valleys velocity#    s) vertical peaks distance#    t) vertical valleys distance#    u) vertical peaks duration#    v) vertical valleys duration# 4. Temporal features#    a) stroke duration#    b) ratio of stroke durations (on-surface / in-air strokes)#    c) writing duration#    d) writing duration overall#    e) ratio of writing durations (on-surface / in-air writing)#    f) number of interruptions#    g) number of interruptions_relative# 5. Composite features#    a) writing tempo#    b) writing stops#    c) number of changes in x profile#    d) number of changes in y profile#    e) number of changes in azimuth#    f) number of changes in tilt#    g) number of changes in pressure#    h) number of changes in velocity profile#    i) relative number of changes in x profile#    j) relative number of changes in y profile#    k) relative number of changes in azimuth#    l) relative number of changes in tilt#    m) relative number of changes in pressure#    n) relative number of changes in velocity profile# Prepare the features computationaxis="x"in_air=Falsestatistics=()# 1. Compute the kinematic featuresfeature=feature_data.velocity(axis=axis,in_air=in_air,statistics=statistics)# feature = feature_data.acceleration(axis=axis, in_air=in_air, statistics=statistics)# feature = feature_data.jerk(axis=axis, in_air=in_air, statistics=statistics)# 2. Compute the dynamic features# feature = feature_data.azimuth(in_air=in_air, statistics=statistics)# feature = feature_data.tilt(in_air=in_air, statistics=statistics)# feature = feature_data.pressure(statistics=statistics)# 3. Compute the spatial features# feature = feature_data.stroke_length(in_air=in_air, statistics=statistics)# feature = feature_data.stroke_height(in_air=in_air, statistics=statistics)# feature = feature_data.stroke_width(in_air=in_air, statistics=statistics)# ...# 4. Compute the temporal features# feature = feature_data.stroke_duration(in_air=in_air, statistics=statistics)# feature = feature_data.ratio_of_stroke_durations(statistics=statistics)# feature = feature_data.writing_duration(in_air=in_air)# feature = feature_data.ratio_of_writing_durations()# ...# 5. Compute the composite features# feature_data.writing_tempo(in_air=False)# feature_data.writing_stops(statistics=["mean", "std"])# ...pprint(feature)# Plot the original data and the computed featureifnotstatistics:fig=plt.figure(figsize=(16,10))# Plot the original dataax=fig.add_subplot(1,2,1)ax.plot(loaded_data.sample_x,loaded_data.sample_y,"-",color="blue",linewidth=2,alpha=0.7)ax.yaxis.grid(True)ax.xaxis.grid(True)# Plot the original dataax=fig.add_subplot(1,2,2)ax.plot(feature,"-",color="red",linewidth=2,alpha=0.7)ax.yaxis.grid(True)ax.xaxis.grid(True)# Adjust the figurefig.tight_layout()# Store the graphs# plt.savefig(f"{signal_name}.pdf", bbox_inches="tight")# Show the graphsplt.get_current_fig_manager().window.state("zoomed")plt.show()LicenseThis project is licensed under the MIT License - see theLICENSEfile for details.ContributorsThis package is developed by the members ofBrain Diseases Analysis Laboratory. For more information, please contact the head of the laboratory Jiri Mekyskamekyska@vut.czor the main developer: Zoltan Galazgalaz@vut.cz.