Git Product home page Git Product logo

niwidgets's Introduction

Neuroimaging Widgets (niwidgets)

This repository is supposed to provide easy and general wrappers to display interactive widgets that visualise standard-format neuroimaging data, using new functions and standard functions from other libraries. It looks like this:

Install via:

pip install niwidgets

Or, to get the most up-to-date development version from github:

pip install git+git://github.com/nipy/niwidgets/

It requires nibabel and nilearn:

pip install nibabel nilearn

Check out the examples using the code in this notebook here: https://github.com/nipy/niwidgets/blob/master/index.ipynb (you need to run the notebook on your local machine to use the interactive features).

or using binder here: https://mybinder.org/v2/gh/nipy/niwidgets/master?filepath=index.ipynb

Usage:

There are currently three supported widgets:

  1. Volume widgets. This widget is primarily designed to mimic existing tools such as , but it also allows you to wrap plots from the nilearn plotting library to make them interactive.

  2. Surface widgets. This widget takes freesurfer-generated volume files and turns them into widgets using the ipyvolume library. It allows you to add different overlays for the surface files.

  3. Streamline widgets. This widget accepts .trk files and displays the tracts using ipyvolume.

To see how to use these widgets, please check the documentation.

As an example of how you might generate a Volume widget:

from niwidgets import NiftiWidget

my_widget = NiftiWidget('./path/to/file.nii')

You can then create a plot either with the default nifti plotter:

my_widget.nifti_plotter()

This will give you sliders to slice through the image, and an option to set the colormap.

You can also provide your own plotting function:

import nilearn.plotting as nip

my_widget.nifti_plotter(plotting_func=nip.plot_glass_brain)

By default, this will give you the following interactive features: - selecting a colormap - if supported by the plotting function, x-y-x sliders (e.g. for nip.plot_img)

You can, however, always provide features you would like to have interactive yourself. This follows the normal ipywidgets format. For example, if you provide a list of strings for a keyword argument, this becomes a drop-down menu. If you provide a tuple of two numbers, this becomes a slider. Take a look at some examples we have in this notebook (you need to run the notebook on your local machine to use the interactive features).

Hopefully we will be able to add more default interactive features in the future, as well as plotting of other data (such as surface projections). If you have any suggestions for plot features to be added, please let us know - or add them yourself and create a pull request!

Development

Contributing

Please contribute! When writing new widgets, please make sure you include example data that allows users to try a widget without having to munge their data into the right format first.

Please also make sure you write a test for your new widget. It's hard to test jupyter widgets, but it would be great if you could at least write a test that "instantiates" a widget. This allows us to maintain a stable release.

Development installation

As always with pip packages, you can install a "development" version of this package by cloning the git repository and installing it via pip install -e /path/to/package.

Updating the documentation

To update the documentation, you can do the following things:

  • Make your changes on a separate branch, such as DOC/update-api-documentation.
  • Merge your branch into master Make sure you have the packages in
  • doc-requirements.txt installed Run make gh-pages in the root directory of
  • the repository

This should run sphinx to generate the documentation, push it to the gh-pages branch, and then revert to master.


Developed by Jan Freyberg, Bjoern Soergel, Satrajit Ghosh, Melanie Ganz, Murat Bilgel, Ariel Rokem, and elyb01.

niwidgets's People

Contributors

bilgelm avatar cancan101 avatar criddler avatar dependabot[bot] avatar janfreyberg avatar maartenbreddels avatar melanieganz avatar mgxd avatar msofka avatar satra avatar zvibaratz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

niwidgets's Issues

Can't load nifti

Typing:

test = NiftiWidget('/coefs/wholebrain30.nii') test.nifti_plotter(plotting_func=plotting.plot_glass_brain, threshold=(0.0, 10.0, 0.01), display_mode=['ortho','xz'])

Causes this error:

FileNotFoundError: [Errno 2] No such file or directory: '/coefs'

If I specify the path alternatively as './coefs/wholebrain30.nii', it complains:

TypeError: argument should be string, bytes or integer, not PosixPath

Make masking more accurate / flexible

The current masking technique doesn't work well.

It should be improved by:

  • checking not too much of the image is masked
  • allowing standard masks by e.g. passing 'mne'

ContextualVersionConflict in Google Colab

Hi,

I'm having a lot of trouble with this package on Google Colab:

!pip install --quiet niwidgets
from niwidgets import NiftiWidget
---------------------------------------------------------------------------
ContextualVersionConflict                 Traceback (most recent call last)
<ipython-input-2-80347fcf605d> in <module>()
      1 get_ipython().system('pip install --quiet niwidgets')
----> 2 from niwidgets import NiftiWidget

5 frames
/usr/local/lib/python3.6/dist-packages/pkg_resources/__init__.py in resolve(self, requirements, env, installer, replace_conflicting, extras)
    789                 # Oops, the "best" so far conflicts with a dependency
    790                 dependent_req = required_by[req]
--> 791                 raise VersionConflict(dist, req).with_context(dependent_req)
    792 
    793             # push the new requirements onto the stack

ContextualVersionConflict: (nibabel 2.3.3 (/usr/local/lib/python3.6/dist-packages), Requirement.parse('nibabel<3.0,>=2.4'), {'niwidgets'})

Can the requirements be made a bit more flexible or updated?

Here's the notebook: https://colab.research.google.com/drive/1k6QK75t0Ab1Zo-_j2YG3x85EvRSVjGLT

Thanks

Arguments are not passed to custom plotter

As I see this (I might be wrong), when specifying the plotting_func the widget creates internally a new figure in any case, and passing then a custom figure handle as argument creates an error.

from niwidgets import NiftiWidget
import nilearn.plotting as nip

my_widget = NiftiWidget(img)
# my_widget.nifti_plotter(figsize=(10,20))

fig, axes = plt.subplots(1, 3, figsize=(5, 10))
my_widget.nifti_plotter(plotting_func=nip.plot_img, colormap=None, figsize=(5, 10),
                        figure=fig,
                        axes = axes)

The default case works fine, the img is shown as 3 axes and the figsize can be adjusted. To get more control over the figure and axes, I used the function call shown above but get the follow error message:

---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-92-a281499c2288> in <module>()
     33 my_widget.nifti_plotter(plotting_func=nip.plot_img, colormap=None, figsize=(5, 10),
     34                         figure=fig,
---> 35                         axes = axes
     36                        )

~/anaconda3/lib/python3.6/site-packages/niwidgets/niwidget_volume.py in nifti_plotter(self, plotting_func, colormap, figsize, **kwargs)
     83             self._default_plotter(**kwargs)
     84         else:
---> 85             self._custom_plotter(plotting_func, **kwargs)
     86 
     87     def _default_plotter(self, mask_background=False, **kwargs):

~/anaconda3/lib/python3.6/site-packages/niwidgets/niwidget_volume.py in _custom_plotter(self, plotting_func, **kwargs)
    241 
    242         # Create the widget:
--> 243         interact(self._custom_plot_wrapper, data=fixed(self.data), **kwargs)
    244         plt.ion()
    245 

~/anaconda3/lib/python3.6/site-packages/ipywidgets/widgets/interaction.py in __call__(self, _InteractFactory__interact_f, **kwargs)
    521         #    def f(*args, **kwargs):
    522         #        ...
--> 523         w = self.widget(f)
    524         try:
    525             f.widget = w

~/anaconda3/lib/python3.6/site-packages/ipywidgets/widgets/interaction.py in widget(self, f)
    437             The function to which the interactive widgets are tied.
    438         """
--> 439         return self.cls(f, self.opts, **self.kwargs)
    440 
    441     def __call__(self, __interact_f=None, **kwargs):

~/anaconda3/lib/python3.6/site-packages/ipywidgets/widgets/interaction.py in __init__(self, _interactive__interact_f, _interactive__options, **kwargs)
    193             getcallargs(f, **{n:v for n,v,_ in new_kwargs})
    194         # Now build the widgets from the abbreviations.
--> 195         self.kwargs_widgets = self.widgets_from_abbreviations(new_kwargs)
    196 
    197         # This has to be done as an assignment, not using self.children.append,

~/anaconda3/lib/python3.6/site-packages/ipywidgets/widgets/interaction.py in widgets_from_abbreviations(self, seq)
    293             if not (isinstance(widget, ValueWidget) or isinstance(widget, fixed)):
    294                 if widget is None:
--> 295                     raise ValueError("{!r} cannot be transformed to a widget".format(abbrev))
    296                 else:
    297                     raise TypeError("{!r} is not a ValueWidget".format(widget))

ValueError: <Figure size 360x720 with 0 Axes> cannot be transformed to a widget

<Figure size 360x720 with 0 Axes>

When passing only the axes handles, the error is

AssertionError: The axes passed are not in the figure

path import issue

I am getting an error related to specifying a path.

TypeError: argument should be string, bytes or integer, not PosixPath

This was with the pip install niwidgets basic installation; I had trouble with this on 2.7.2 so I switched my kernel to 3.5.3 and am still having the same issue...


TypeError Traceback (most recent call last)
in ()
5 import ipywidgets as widgets
6 ###http://nipy.org/niwidgets/installation.html#install-via-pip
----> 7 my_widget = NiftiWidget('../SampleDataFiles/fdt_paths.nii.gz')
8 get_ipython().run_line_magic('load_ext', 'autoreload')
9 get_ipython().run_line_magic('autoreload', '2')

~/.virtualenvs/nipypev3/lib/python3.5/site-packages/niwidgets/niwidget_volume.py in init(self, filename)
43 """
44 self.filename = Path(filename).resolve()
---> 45 if not os.path.isfile(self.filename):
46 raise OSError('File ' + self.filename.name + ' not found.')
47

~/.virtualenvs/nipypev3/lib/python3.5/genericpath.py in isfile(path)
28 """Test whether a path is a regular file"""
29 try:
---> 30 st = os.stat(path)
31 except OSError:
32 return False

TypeError: argument should be string, bytes or integer, not PosixPath

Confirm that ipyvolume fov set actually changes fov?

fig.camera_fov = 1

I'm currently trying to use the streamline widget to plot some output, however I've noticed that the visualization always starts extremely far away (to the point where a user might thing there's nothing actually plotted).

I did some exploring (to the extent that I can) and it seems that the visualization is defaulting to a suspiciously arbitrary value:

catchPlot=sw.plot(display_fraction=1, width=1000, height=1000, style=style, percentile=0) VBox(children=(Figure(camera=PerspectiveCamera(fov=46.0, position=(0.0, 0.0, 2.0), quaternion=(0.0, 0.0, 0.0, … interactive(children=(FloatSlider(value=12.84813404083252, continuous_update=False, description='threshold', m…

This value appears to be the default FOV (as well as the other settings) as found in the ipyvolume documentation

Additionally, another ipyvolume user noted a similar behavior previously.

Is this a ipyvolume issue, a niwidget issue, or am I just using niwidgets incorrectly?

new release of jupyter-sphinx

This project lists jupyter-sphinx in its sphinx configuration or requirements file, but does not appear to use this extension directly anymore.
We have just rewritten jupyter-sphinx from scratch, and fully changed the interface.
Therefore we would like to give you a heads up, in case you find the new version useful.

Now all code execution is done via a single jupyter-execute sphinx directive, instead of the previous ipywidgets-setup/ipywidgets-embed.
The new version is also much more flexible: it allows to embed any output recognized by jupyter in sphinx documentation. See the project documentation for details.

We have released 0.2.0rc1 on pip, please give it a shot using pip install 0.2.0rc1 --pre — we would love to hear your feedback.
Please also let us know by responding here or in jupyter/jupyter-sphinx#33 if the new release disrupts your workflow.

cannot import "Video"

Just immediately after installation I get the following errror on Python 3.6.5:

from niwidgets import NiftiWidget

---------------------------------------------------------------------------
ImportError                               Traceback (most recent call last)
<ipython-input-1-c8f24c578dcb> in <module>()
----> 1 from niwidgets import NiftiWidget

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/niwidgets/__init__.py in <module>()
      8 from .exampledata import exampleatlas, examplezmap, examplet1  # noqa: F401
      9 from .niwidget_volume import NiftiWidget  # noqa: F401
---> 10 from .niwidget_surface import SurfaceWidget  # noqa: F401
     11 from .streamlines import StreamlineWidget  # noqa: F401

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/niwidgets/niwidget_surface.py in <module>()
      6 from xml.parsers.expat import ExpatError
      7 
----> 8 import ipyvolume.pylab as p3
      9 import matplotlib.pyplot as plt
     10 import nibabel as nb

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/ipyvolume/__init__.py in <module>()
      3 
      4 from . import styles
----> 5 from .widgets import *
      6 from .transferfunction import *
      7 from . import examples

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/ipyvolume/widgets.py in <module>()
      8 import logging
      9 import numpy as np
---> 10 from .serialize import array_cube_tile_serialization, array_serialization, array_sequence_serialization,\
     11     color_serialization, image_serialization, texture_serialization
     12 from .transferfunction import *

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/ipyvolume/serialize.py in <module>()
      7 from . import utils
      8 import ipywidgets
----> 9 import ipywebrtc
     10 import numpy as np
     11 import PIL.Image

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/ipywebrtc/__init__.py in <module>()
      3 import ipywidgets as widgets
      4 from ._version import version_info, __version__
----> 5 from .webrtc import *
      6 
      7 

/home/arman/anaconda/envs/development/lib/python3.6/site-packages/ipywebrtc/webrtc.py in <module>()
     10     Bool, Bytes, Dict, Instance, Int, List, TraitError, Unicode, validate
     11 )
---> 12 from ipywidgets import DOMWidget, Image, Video, Audio, register, widget_serialization
     13 from ipython_genutils.py3compat import string_types
     14 import ipywebrtc._version

ImportError: cannot import name 'Video'

Can't import module

I just tried to pip install from a directory I cloned from github and when I import NiftiWidget, I get the following error:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-1-ab0954c2e00f> in <module>()
      7 import seaborn as sns
      8 import numpy as np
----> 9 from niwidgets import NiftiWidget
     10 
     11 get_ipython().magic('matplotlib inline')

/data/MLcore/miniconda3/envs/nielsond/lib/python3.6/site-packages/niwidgets/__init__.py in <module>()
      9 # import widget classes.
     10 from .niwidget_volume import NiftiWidget  # noqa
---> 11 from .niwidget_surface import SurfaceWidget  # noqa

/data/MLcore/miniconda3/envs/nielsond/lib/python3.6/site-packages/niwidgets/niwidget_surface.py in <module>()
      7 from ipywidgets import interact, fixed, IntSlider
      8 # from ipyvolume import gcf
----> 9 import ipyvolume.pylab as p3
     10 import os
     11 # import pathlib & backwards compatibility

/data/MLcore/miniconda3/envs/nielsond/lib/python3.6/site-packages/ipyvolume/__init__.py in <module>()
      3 
      4 from . import styles
----> 5 from .widgets import *
      6 from .transferfunction import *
      7 from .examples import  *

/data/MLcore/miniconda3/envs/nielsond/lib/python3.6/site-packages/ipyvolume/widgets.py in <module>()
     10 from .serialize import array_cube_png_serialization, array_serialization, array_sequence_serialization,\
     11     color_serialization, image_serialization, texture_serialization
---> 12 from .transferfunction import *
     13 import warnings
     14 import ipyvolume

/data/MLcore/miniconda3/envs/nielsond/lib/python3.6/site-packages/ipyvolume/transferfunction.py in <module>()
     38 
     39 
---> 40 class TransferFunctionJsBumps(TransferFunction):
     41         _model_name = Unicode('TransferFunctionJsBumpsModel').tag(sync=True)
     42         _model_module = Unicode('ipyvolume').tag(sync=True)

TypeError: function() argument 1 must be code, not str

Issue Plotting float32 data

I am trying to plot a volume of float32 data (takes on values 0-1) and I get the following:
image
If instead I convert the float data to uint8 (*255), the plotting looks fine (not totally washed out).

Likewise If I just do:

plt.imshow(img_float.dataobj[...,87], vmin=0., vmax=1., cmap='viridis')

I also am able to see the image. What is happening in niwidgets to wash out the image?

Support Python 3.5

Right now only python 3.6 is supported:

/usr/src/app/src/niwidgets/niwidgets/niwidget_volume.py in __init__(self, filename)
     41                     ``PosixPath`` from python3's pathlib.
     42         """
---> 43         self.filename = Path(filename).resolve(strict=True)
     44 
     45         # load data in advance

TypeError: resolve() got an unexpected keyword argument 'strict'

due to strict being added in 3.6.

NiftiWidget can take in nibabel image

NiftiWidget should also allowed to be constructing providing the nibabel image. Right now only a path is allowed. load is this called on the path. I suggest allowing the user to pass in the same (ie the result of the user having called load himself) into NiftiWidget.

Alternatively, NiftiWidget can be made to subclass VolumetricWidget (handles more than nii files) and then VolumetricWidget takes in the nibabel image.

Python Version Support

Clarify what version(s) of Python are supported.

  • using python_requires in setup.py
  • in the README

Right now it looks like testing is agaisnt python 3.x but that does not seem to be made explicit.

interactive example on mybinder not working

Ahoi hoi there,

Problem:

I was just trying to run the examples on mybinder, but appears to be a problem as none of the imports do work:
Screen Shot 2019-10-16 at 9 30 34 AM

Steps to reproduce:

Try to run the examples on mybinder here.

Possible solution:

No precise idea, but seems that the examples are running on a local mybinder instance. Hence, maybe check if everything is okay there.

Happy to help solving this in case there's something I could do.

Best, Peer

How to change axes direction to ImageJ standard

Hi

i use this package for simple cardiac stack visualization. i'm writing a 3d analysis script for orientation and volumetric estimation of cardiac cells.

The issue is the rotation and the direction of the axes.

I explain it with an example:

I have a stack with M images if NxN pixels. If i plot one slice with Imagej (or matplotlib.imshow) i see correct orientation, when i create a Nibabel image with
my_stack_nib = nib.Nifti1Image(my_stack, affine) # with affine = numpy.eye(4) from your example

and plot it with
widget = niwidgets.NiftiWidget(my_stack_nib)
widget.nifti_plotter()

i see :

  • the xy plane rotated by 90' clockwise.
  • the z and y axex inverted respect to numpy stack information ( z = 0 on bottom and z = zmax in up, y = 0 in bottom and y = ymax in up).

To fix it , i tried to change
nifti_plotter(plotting_func=...)

i tried numpy.rotate on xy plan (OK) and flip on z axe (but this breaks correct reference from data to plot)
i tried to change affine matrix, but it seems not to works.

I search only a simple way to change axes direction without corrupt my data, please someone can help me?

thanks a lot
Dr. Francesco Giardini, LENS, Florence

.nifti_plotter() widget "value at point" output

The gif presented in the readme.md file depicting volumetric image plotting features a "value at point" output behavior that appears to print out the value at the specified voxel.

However, this does not appear to be a default behavior of the current version of the widget. Furthermore, a GitHub search of the repo for "value at point" does not return any hits. Is there a hidden option for this behavior? Has this functionality been removed?

link to documentation in README broken

Ahoi hoi there,

Problem:

Trying to get to the documentation through the link in the README leads to a 404, as the link appears to be broken:
Screen Shot 2019-10-16 at 9 30 52 AM

The link is https://github.com/nipy/niwidgets/blob/master/nipy.org/niwidgets.

Steps to reproduce:

Clicking on the documentation link in the README.

Possible solution:

Fix the link to lead to the working documentation, which appears to be
https://nipy.org/niwidgets/

Happy to submit a short PR for this in case you want.

Best, Peer

update docs to reflect the new widgets

it would be nice to update the documents to reflect the new widgets. if we use nbconvert and embed widgets we could also be able to generate interactive docs to demonstrate. see the ipywidgets docs for an example.

Cant import niwidgets

import niwidgets

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-c2cf98e8d4d7> in <module>()
----> 1 import niwidgets
      2 #from niwidgets import NiftiWidget

D:\Anaconda3\lib\site-packages\niwidgets\__init__.py in <module>()
      9 # import widget classes.
     10 from .niwidget_volume import NiftiWidget  # noqa
---> 11 from .niwidget_surface import SurfaceWidget  # noqa

D:\Anaconda3\lib\site-packages\niwidgets\niwidget_surface.py in <module>()
      7 from ipywidgets import interact, fixed, IntSlider
      8 # from ipyvolume import gcf
----> 9 import ipyvolume.pylab as p3
     10 import os
     11 # import pathlib & backwards compatibility

D:\Anaconda3\lib\site-packages\ipyvolume\__init__.py in <module>()
      3 
      4 from . import styles
----> 5 from .widgets import *
      6 from .transferfunction import *
      7 from .examples import  *

D:\Anaconda3\lib\site-packages\ipyvolume\widgets.py in <module>()
     10 from .serialize import array_cube_png_serialization, array_serialization, array_sequence_serialization,\
     11     color_serialization, image_serialization, texture_serialization
---> 12 from .transferfunction import *
     13 import warnings
     14 import ipyvolume

D:\Anaconda3\lib\site-packages\ipyvolume\transferfunction.py in <module>()
     11 
     12 import ipyvolume._version
---> 13 semver_range_frontend = "~" + ipyvolume._version.__version_js__
     14 
     15 

AttributeError: module 'ipyvolume' has no attribute '_version'

And i think the question is about jupyter extensions cause that i've tried the code in anaconda prompt and failed:
jupyter nbextension enable --py --sys-prefix ipyvolume

Pypi version of niwidgets broken

Hey there,

I installed niwidgets from pypi and when doing this:

from niwidgets import NiftiWidget

I get the following error:

---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
<ipython-input-3-580c792c4ebb> in <module>()
      8 from nilearn import image
      9 from nilearn import plotting
---> 10 from niwidgets import NiftiWidget
     11 
     12 try:

C:\Anaconda2\lib\site-packages\niwidgets\__init__.py in <module>()
      6 """
      7 # import example data
----> 8 from .exampledata import exampleatlas, examplezmap, examplet1  # noqa
      9 # import widget classes.
     10 from .niwidget_volume import NiftiWidget  # noqa

C:\Anaconda2\lib\site-packages\niwidgets\exampledata.py in <module>()
      4     # on >3 this ships by default
      5     from pathlib import Path
----> 6 except ModuleNotFoundError:
      7     # on 2.7 this should work
      8     try:

NameError: name 'ModuleNotFoundError' is not defined

I am using Python v2.7 BTW

Building from source?

Hi niwidgets developers, currently I am only able to get your package via a pip wheel. The instructions in the README to run

pip install git+git://github.com/nipy/niwidgets/

doesn't work due to a missing setup.py. Any advice on how to build from git/source?

FutureWarning raised when calling `nifti_plotter`

Hi all,

I get the following FutureWarning from numpy when calling nifti_plotter:

image

It seems to be caused by line 197 in niwidget_volume.py:

else np.fliplr(np.flipud(np.rot90(data[slice_obj], k=1)))

I think it doesn't have any real effect other than displaying the annoying warning. However, it could be nice to either:


Option A

Add:

import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)

before importing numpy.


Option B

Even simpler, change line 197 to:

else np.fliplr(np.flipud(np.rot90(data[tuple(slice_obj)], k=1)))

I tested both solutions locally and they seem to work as expected.

If that makes sense to you I will gladly create a PR.

Thank you for all your amazing work!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.