{ "cells": [ { "cell_type": "markdown", "id": "roman-latin", "metadata": {}, "source": [ "# Open C3D files" ] }, { "cell_type": "markdown", "id": "quick-father", "metadata": {}, "source": [ "Marcos Duarte" ] }, { "cell_type": "markdown", "id": "whole-checkout", "metadata": {}, "source": [ "**Text and binary files** \n", "There are two kinds of computer files: text and binary files. Text files are structured as a sequence of lines of electronic text. The most common formats of a text file are ASCII (with 128 ($2^7$) different characters and UTF-8 (which includes non-English characters). A binary file is a file that is not structured as a text file. Because in fact everything in a computer is stored in binary format (a sequence of zeros and ones), text files are binary files that store text codes.\n", "\n", "To open and read a text file is simple and straightforward. A text file doesn't need additional information to be read, and can be openned by any text-processing software. This is not the case of a binary file, we need to have extra information about how the file is structured to be able to read it. However, binary files can store more information per file size than text files and we can read and write binary files faster than text files. This is one of the reasons why software developers would choose a binary format.\n", "\n", "**C3D format**\n", "\n", "> The C3D format is a public domain, binary file format that has been used in Biomechanics, Animation and Gait Analysis laboratories to record synchronized 3D and analog data since the mid 1980's. It is supported by all 3D major Motion Capture System manufacturers, as well as other companies in the Biomechanics, Motion Capture and Animation Industries ([http://www.c3d.org/](http://www.c3d.org/)).\n", "\n", "There is a very detailed [technical implementation manual of the C3D file format](http://www.c3d.org/pdf/c3dformat_ug.pdf).\n", "\n", "The C3D file format has three basic components: \n", "\n", " - Data: at this level the C3D file is simply a binary file that stores raw 3D and analog information. \n", " - Standard parameters: default information about the raw 3D and analog data that is required to access the data. \n", " - Custom parameters: information specific to a particular manufacturers’ software application or test subject. \n", "\n", "Regarding to what is useful to the analysis, a C3D file normally has four types of information:\n", "\n", " - 3D point: 3D coordinates of markers or any related biomechanical quantities such as angles, joint forces and moments, etc.\n", " - Analaog: analog data acquired with an A/D converter.\n", " - Event: specific frames as events of the acquisition.\n", " - Metadata: information about the subject, the system configuration, etc.\n", " \n", "All C3D files contain a minimum of three sections of information:\n", "\n", " - A single, 512 byte, header section.\n", " - A parameter section consisting of one, or more, 512-byte blocks. \n", " - 3D point/analog data section consisting of one, or more, 512-byte blocks.\n", " \n", "To demonstrate the process of reading a binary file, let's read only part of the header of a C3D file:" ] }, { "cell_type": "code", "execution_count": 1, "id": "taken-latex", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "File \"data/Gait.c3d\":\n", "Number of 3D points: 33\n", "Number of frames: 487\n", "Frame rate: 100.00\n", "Analog data per frame: 10\n" ] } ], "source": [ "from __future__ import division, print_function\n", "from struct import unpack # convert C structs represented as Python strings\n", "from cStringIO import StringIO # reads and writes a string buffer\n", "\n", "def getFloat(floatStr,processor):\n", " \"16-bit float string to number conversion\"\n", " if processor > 1: #DEC or SGI\n", " floatNumber = unpack('f',floatStr[2:] + floatStr[0:2])[0]/4\n", " else:\n", " floatNumber = unpack('f',floatStr)[0]\n", " return floatNumber \n", "\n", "filename = './../data/Gait.c3d' # data from sample03.zip @ http://www.c3d.org/sampledata.html\n", "fid = open(filename, 'rb') # open file for reading in binary format\n", "#Header section:\n", "bytes = fid.read(512)\n", "buf = StringIO(bytes)\n", "firstBlockParameterSection, fmt = unpack('BB', buf.read(2))\n", "if fmt != 80:\n", " print('This file is not a valid C3D file.')\n", " fid.close()\n", "# First block of parameter section:\n", "firstBlockByte = 512*(firstBlockParameterSection - 1) + 2\n", "fid.seek(firstBlockByte)\n", "nparameter_blocks, processor = unpack('BB', fid.read(2))\n", "processor = processor - 83\n", "processors = ['unknown', 'Intel', 'DEC', 'SGI']\n", "#Back to the header section:\n", "n3Dpoints, = unpack('H', buf.read(2))\n", "nTotalAnalogDataPerFrame, = unpack('H', buf.read(2))\n", "nFirstFrame3D, = unpack('H', buf.read(2))\n", "nLastFrame3D, = unpack('H', buf.read(2))\n", "maxInterpGap, = unpack('H', buf.read(2))\n", "scaleFactor = getFloat(buf.read(4), processor)\n", "dataStartBlock, = unpack('H', buf.read(2))\n", "nAnalogDataPerFrame, = unpack('H', buf.read(2))\n", "frameRate = getFloat(buf.read(4), processor)\n", "\n", "print('File \"%s\":' %filename)\n", "print('Number of 3D points: %d' % n3Dpoints)\n", "print('Number of frames: %d' %(nLastFrame3D - nFirstFrame3D + 1))\n", "print('Frame rate: %.2f' % frameRate)\n", "print('Analog data per frame: %d' % nAnalogDataPerFrame) " ] }, { "cell_type": "markdown", "id": "chubby-assumption", "metadata": {}, "source": [ "To read a complete C3D file, and fast enough, is not simple. Fortunately, there is a Python package to work with C3D files: the [BTK library](https://code.google.com/p/b-tk/). The same developer of BTK, Arnaud Barré, also wrote another nice software: [Mokka](http://b-tk.googlecode.com/svn/web/mokka/index.html), an *'open-source and cross-platform software to easily analyze biomechanical data'*. \n", "\n", "[See this page on how to install the BTK library for Python](https://code.google.com/p/b-tk/wiki/PythonBinaries). \n", "\n", "Let's use BTK to read a C3D file. \n", "There is a different version of BTK for each OS; here is a workaround to import the right BTK library according to your OS:" ] }, { "cell_type": "code", "execution_count": 2, "id": "young-knock", "metadata": {}, "outputs": [], "source": [ "import platform, sys\n", "\n", "if platform.system() == 'Windows':\n", " if sys.maxsize > 2**32:\n", " sys.path.insert(1, r'./../functions/btk/win64')\n", " else:\n", " sys.path.insert(1, r'./../functions/btk/win32')\n", "elif platform.system() == 'Linux':\n", " if sys.maxsize > 2**32:\n", " sys.path.insert(1, r'./../functions/btk/linux64')\n", " else:\n", " sys.path.insert(1, r'./../functions/btk/linux32')\n", "elif platform.system() == 'Darwin':\n", " sys.path.insert(1, r'./../functions/btk/mac')" ] }, { "cell_type": "markdown", "id": "statistical-earth", "metadata": {}, "source": [ "The BTK files above were taken from [https://code.google.com/p/b-tk/wiki/PythonBinaries](https://code.google.com/p/b-tk/wiki/PythonBinaries) with the exception of the files for the Mac OS. I compiled these files for the latest Mac OS version (10.9) and Python 2.7.6. Use the Mac files from the BTK website if you have older Mac OS and Python versions.\n", "\n", "With the BTK files and the path to them in the Python path, to use them it's easy:" ] }, { "cell_type": "code", "execution_count": 3, "id": "unknown-glucose", "metadata": {}, "outputs": [], "source": [ "import btk" ] }, { "cell_type": "markdown", "id": "swiss-heritage", "metadata": {}, "source": [ "Read a C3D file:" ] }, { "cell_type": "code", "execution_count": 4, "id": "wireless-stand", "metadata": {}, "outputs": [], "source": [ "reader = btk.btkAcquisitionFileReader() # build a btk reader object \n", "reader.SetFilename(\"./../data/Gait.c3d\") # set a filename to the reader\n", "acq = reader.GetOutput() # btk aquisition object\n", "acq.Update() # Update ProcessObject associated with DataObject" ] }, { "cell_type": "markdown", "id": "sufficient-blood", "metadata": {}, "source": [ "Get information about the C3D file content:" ] }, { "cell_type": "code", "execution_count": 5, "id": "bulgarian-progress", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Acquisition duration: 4.87 s\n", "Point frequency: 100.00 Hz\n", "Number of frames: 487\n", "Point unit: mm\n", "Analog frequency: 1000.00 Hz\n", "Number of analog channels: 28\n", "Number of events: 0\n" ] } ], "source": [ "print('Acquisition duration: %.2f s' %acq.GetDuration()) \n", "print('Point frequency: %.2f Hz' %acq.GetPointFrequency())\n", "print('Number of frames: %d' %acq.GetPointFrameNumber())\n", "print('Point unit: %s' %acq.GetPointUnit())\n", "print('Analog frequency: %.2f Hz' %acq.GetAnalogFrequency())\n", "print('Number of analog channels: %d' %acq.GetAnalogNumber()) \n", "print('Number of events: %d' %acq.GetEventNumber())" ] }, { "cell_type": "markdown", "id": "valuable-florist", "metadata": {}, "source": [ "Get the labels of the 3D points and analog channels:" ] }, { "cell_type": "code", "execution_count": 6, "id": "controversial-albania", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Marker labels:\n", "R.Shoulder R.Offset R.Elbow R.Wrist L.Shoulder L.Elbow L.Wrist R.ASIS L.ASIS V.Sacral R.Thigh R.Knee R.Shank R.Ankle R.Heel R.Toe L.Thigh L.Knee L.Shank L.Ankle L.Heel L.Toe R.Knee.Medial R.Ankle.Medial L.Knee.Medial L.Ankle.Medial R.Foot.Medial R.Foot.Lateral L.Foot.Medial L.Foot.Lateral C7 IJ PX \n", "\n", "Analog channels:\n", "f1x f1y f1z m1x m1y m1z f2x f2y f2z m2x m2y m2z L Rectus R Rectus L Vaste Ext R Vaste Ext L GRF R GRF L Ischio R Ischio L Biceps R Biceps L Tibialis R Tibialis L Sol R Sol L Jum Int R Jum Int " ] } ], "source": [ "print('Marker labels:')\n", "for i in range(0, acq.GetPoints().GetItemNumber()):\n", " print(acq.GetPoint(i).GetLabel(), end=' ') \n", "print('\\n\\nAnalog channels:')\n", "for i in range(0, acq.GetAnalogs().GetItemNumber()):\n", " print(acq.GetAnalog(i).GetLabel(), end=' ') " ] }, { "cell_type": "markdown", "id": "competitive-fundamentals", "metadata": {}, "source": [ "Get all events:" ] }, { "cell_type": "code", "execution_count": 7, "id": "technical-momentum", "metadata": {}, "outputs": [], "source": [ "for i in range(0, acq.GetEvents().GetItemNumber()):\n", " print(acq.GetEvent(i).GetLabel() + ' at frame %d' %acq.GetEvent(i).GetFrame()) " ] }, { "cell_type": "markdown", "id": "breeding-potential", "metadata": {}, "source": [ "This file doesn't have any event.\n", "\n", "Get all metadata:" ] }, { "cell_type": "code", "execution_count": 8, "id": "classified-insertion", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "POINT:\n", "USED FRAMES DESCRIPTIONS LABELS INITIAL_COMMAND X_SCREEN Y_SCREEN LAB_ROT RATE UNITS SCALE DATA_START \n", "\n", "ANALOG:\n", "USED DESCRIPTIONS LABELS RATE BITS FORMAT GEN_SCALE OFFSET UNITS SCALE \n", "\n", "FORCE_PLATFORM:\n", "USED TYPE ORIGIN CORNERS CAL_MATRIX ZERO CHANNEL \n", "\n" ] } ], "source": [ "for i in range(acq.GetMetaData().GetChildNumber()):\n", " print(acq.GetMetaData().GetChild(i).GetLabel() + ':')\n", " for j in range(acq.GetMetaData().GetChild(i).GetChildNumber()):\n", " print(acq.GetMetaData().GetChild(i).GetChild(j).GetLabel(), end=' ')\n", " print('\\n')" ] }, { "cell_type": "markdown", "id": "periodic-petroleum", "metadata": {}, "source": [ "Get the 3D position data of all markers as a Numpy array:" ] }, { "cell_type": "code", "execution_count": 9, "id": "champion-sheep", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "\n", "data = np.empty((3, acq.GetPointFrameNumber(), 1))\n", "for i in range(0, acq.GetPoints().GetItemNumber()):\n", " label = acq.GetPoint(i).GetLabel()\n", " data = np.dstack((data, acq.GetPoint(label).GetValues().T))\n", "data = data.T\n", "data = np.delete(data, 0, axis=0) # first marker is noisy for this file\n", "data[data==0] = np.NaN # handle missing data (zeros)" ] }, { "cell_type": "code", "execution_count": 10, "id": "joined-texas", "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(33L, 487L, 3L)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data.shape" ] }, { "cell_type": "code", "execution_count": 11, "id": "committed-bosnia", "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "-753.301107883 2438.03477883\n" ] } ], "source": [ "print(np.nanmin(data), np.nanmax(data))" ] }, { "cell_type": "markdown", "id": "technical-variance", "metadata": {}, "source": [ "There are 33 markers (with x, y, z coordinates) and 487 frames. \n", "Let's visualize these data.\n", "\n", "First, the vertical ground reaction force:" ] }, { "cell_type": "code", "execution_count": 12, "id": "exact-summit", "metadata": {}, "outputs": [ { "data": { "image/png": "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\nRK5CYII=\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import matplotlib.pyplot as plt\n", "%matplotlib inline\n", "\n", "ana = acq.GetAnalog(\"f1z\")\n", "fz1 = ana.GetValues()/ana.GetScale()\n", "ana = acq.GetAnalog(\"f2z\")\n", "fz2 = ana.GetValues()/ana.GetScale()\n", "freq2 = acq.GetAnalogFrequency()\n", "t = np.linspace(1, len(fz1), num=len(fz1))/freq2\n", "plt.figure(figsize=(10, 4))\n", "plt.plot(t, fz1, label='Fz1')\n", "plt.plot(t, fz2, label='Fz2')\n", "plt.legend()\n", "plt.xlabel('Time [s]')\n", "plt.ylabel('GRF vertical [N]')\n", "plt.show()" ] }, { "cell_type": "markdown", "id": "featured-maldives", "metadata": {}, "source": [ "Let's visualize the markers positions in a 3D animation:" ] }, { "cell_type": "code", "execution_count": 13, "id": "bulgarian-program", "metadata": {}, "outputs": [], "source": [ "# Use the IPython magic %matplotlib qt to plot a figure in a separate window\n", "# because the Ipython Notebook doesn't play inline matplotlib animations yet.\n", "%matplotlib qt" ] }, { "cell_type": "code", "execution_count": 14, "id": "systematic-navigation", "metadata": {}, "outputs": [], "source": [ "from mpl_toolkits.mplot3d import Axes3D\n", "import matplotlib.animation as animation\n", "\n", "dat = data[:, 130:340, :]\n", "freq = acq.GetPointFrequency()\n", "\n", "fig = plt.figure()\n", "ax = fig.add_axes([0, 0, 1, 1], projection='3d')\n", "ax.view_init(10, 150)\n", "pts = []\n", "for i in range(dat.shape[0]):\n", " pts += ax.plot([], [], [], 'o')\n", "\n", "ax.set_xlim3d([np.nanmin(dat[:, :, 0]), np.nanmax(dat[:, :, 0])])\n", "ax.set_ylim3d([np.nanmin(dat[:, :, 1])-400, np.nanmax(dat[:, :, 1])+400])\n", "ax.set_zlim3d([np.nanmin(dat[:, :, 2]), np.nanmax(dat[:, :, 2])])\n", "ax.set_xlabel('X [mm]')\n", "ax.set_ylabel('Y [mm]')\n", "ax.set_zlabel('Z [mm]')\n", "\n", "# animation function\n", "def animate(i):\n", " for pt, xi in zip(pts, dat):\n", " x, y, z = xi[:i].T\n", " pt.set_data(x[-1:], y[-1:])\n", " pt.set_3d_properties(z[-1:]) \n", " return pts\n", "\n", "# Animation object\n", "anim = animation.FuncAnimation(fig, func=animate, frames=dat.shape[1], interval=1000/freq, blit=True)\n", "\n", "plt.show()" ] }, { "cell_type": "code", "execution_count": 15, "id": "architectural-burning", "metadata": {}, "outputs": [], "source": [ "# get back the inline plot\n", "%matplotlib inline" ] }, { "cell_type": "markdown", "id": "constitutional-dover", "metadata": {}, "source": [ "This is the animation you see in a separate window when you run the code above:\n", "\n", "
walking

" ] }, { "cell_type": "markdown", "id": "asian-mortality", "metadata": {}, "source": [ "To save the animation generated above as a video file (you need to have [FFmpeg](http://www.ffmpeg.org/) or [ImageMagick](http://www.imagemagick.org/script/index.php) installed):" ] }, { "cell_type": "code", "execution_count": 18, "id": "cordless-elite", "metadata": {}, "outputs": [], "source": [ "anim.save('walking.mp4', writer='ffmpeg', fps=50)\n", "# or\n", "anim.save('walking.gif', writer='imagemagick', fps=50)" ] }, { "cell_type": "markdown", "id": "above-roads", "metadata": {}, "source": [ "You can also write to a C3D file using BTK; [look at its webpage](http://b-tk.googlecode.com/svn/doc/Python/0.2/_getting_started.html)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.8" }, "nbTranslate": { "displayLangs": [ "*" ], "hotkey": "alt-t", "langInMainMenu": true, "sourceLang": "en", "targetLang": "fr", "useGoogleTranslate": true }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": false }, "varInspector": { "cols": { "lenName": 16, "lenType": 16, "lenVar": 40 }, "kernels_config": { "python": { "delete_cmd_postfix": "", "delete_cmd_prefix": "del ", "library": "var_list.py", "varRefreshCmd": "print(var_dic_list())" }, "r": { "delete_cmd_postfix": ") ", "delete_cmd_prefix": "rm(", "library": "var_list.r", "varRefreshCmd": "cat(var_dic_list()) " } }, "types_to_exclude": [ "module", "function", "builtin_function_or_method", "instance", "_Feature" ], "window_display": false } }, "nbformat": 4, "nbformat_minor": 1 }