lowe-lab-ucl / arboretum Goto Github PK
View Code? Open in Web Editor NEWTrack and lineage visualization with btrack and Napari :evergreen_tree:
License: MIT License
Track and lineage visualization with btrack and Napari :evergreen_tree:
License: MIT License
The leaf ID labels have low constrast (white with alpha=0.25) against a black background. We should change this so the contrast is better.
The time line in the property plotter works fine for the first selected cell, but the second and onwards it doesn't render at all.
Hi @quantumjot,
I was very excited to see this repo existing when browsing through the Bayesian Tracker repo!
I've just tried using btrack for the first time but I am having a hard time visualising the tracks. I've managed to do so in napari, but it's not that obvious (especially if you don't 'speak' the python language very well) and the fact that I would need to translate the tracks to my segmentation masks is beyond me at this moment in time.
So I was wondering when (more or less) would you expect to release a working plugin? Would it be as easy as feeding the plugin the 'tracks' variable generated from the tracker? What features would it have?
I understand that btrack is specifically designed for lineage tracing, and that the features of this plugin will align with that objective. It is not my current purpose though; I'm just interested in good cell tracking. Therefore, there are other features that would be particularly useful for general tracking purposes.
A sort of a wish list I guess (in order of preference):
These are features that would be really useful in my case (and I would guess for many other people tracking cells over time). I just wanted to put it out there to 1) see which (if any) of these features are on the arboretum roadmap and 2) as a suggestion.
Looking forward to hearing from you and seeing this plugin come to life.
Cheers,
Pablo
This tool seems great so far. I was able to install and run the test, and moved on to trying with some other data. There is only one "state" to the system, so I rewrote the config.json
as:
{
"TrackerConfig":
{
"MotionModel":
{
"name": "cell_motion",
"dt": 1.0,
"measurements": 1,
"states": 1,
"accuracy": 7.5,
"prob_not_assign": 0.001,
"max_lost": 5,
"A": {
"matrix": [1]
},
"H": {
"matrix": [1]
},
"P": {
"sigma": 150.0,
"matrix": [0.1]
},
"G": {
"sigma": 15.0,
"matrix": [0.5]
},
"R": {
"sigma": 5.0,
"matrix": [1]
}
},
"ObjectModel":
{},
"HypothesisModel":
{
"name": "cell_hypothesis",
"hypotheses": ["alive"],
"lambda_time": 5.0,
"lambda_dist": 3.0,
"lambda_link": 10.0,
"lambda_branch": 50.0,
"eta": 1e-10,
"theta_dist": 20.0,
"theta_time": 5.0,
"dist_thresh": 40,
"time_thresh": 2,
"apop_thresh": 5,
"segmentation_miss_rate": 0.1,
"apoptosis_rate": 0.001,
"relax": true
}
}
}
These settings cause python to crash, and the error is:
Instantiating BTRACK interface wrapper (v0.3.10, compiled Jun 17 2020 at 09:14:42)
[INFO][2020/07/20 10:46:24 PM] btrack (v0.3.10) library imported
[INFO][2020/07/20 10:46:24 PM] Starting BayesianTracker session
[INFO][2020/07/20 10:46:24 PM] Loading motion model: b'cell_motion'
[INFO][2020/07/20 10:46:24 PM] Set volume to ((0, 235), (0, 257), (-100000.0, 100000.0))
[INFO][2020/07/20 10:46:24 PM] Setting Bayesian update method to: BayesianUpdates.EXACT
[INFO][2020/07/20 10:46:24 PM] Starting tracking...
Assertion failed: (startRow >= 0 && blockRows >= 0 && startRow <= xpr.rows() - blockRows && startCol >= 0 && blockCols >= 0 && startCol <= xpr.cols() - blockCols), function Block, file ./btrack/include/eigen/Eigen/src/Core/Block.h, line 147.
I may have settings in the config
which don't totally make sense, as I'm not sure what some of them refer to.
It seems to be an issue in Eigen when the Kalman matrices are one by one? maybe?
I'm not sure if this holds if I try in btracker directly.
I'm running MacOS 10.14
From #60 (comment):
Title - I usually call the trees by their root cell ID i.e. Lineage Tree #67. Having this on top of the tree would be great for exporting the figure / making screenshots.
The edges of the tree should have the option to be colored by the properties of each vertex of the tracks.
Also, properties may have been renamed as features in napari now.
They have to click on the tree to select a track
For testing purposes it would save a lot of time to have a python script that:
Adds some sample data to napari
Opens a napari window
Opens and runs arboretum
Analogous to brainglobe/cellfinder#325 but wouldn't suffer from https://github.com/brainglobe/cellfinder-napari/issues/#71 as we (at least for now) run everything in the same thread.
###Pseudo-code sketch (untested and likely wrong!)
import napari
from pathlib import Path
viewer = napari.Viewer()
file_path = Path() # any images needing opening
viewer.open(file_path)
# call script
from arboretum import what_you_want_test
what_you_want_to_test() # this may change depending on what the dev wants to test?```
This only makes a difference if scaling etc. has been applied to the data.
From #60 (comment):
Would it be tricky to make the colormap of the tracks in the tree match the colormap below? (Suitable for visualising one track at a time)
I've used a combination of napari-btrack + arboretum in sync (maybe that's the problem!) - after clicking on a few cells and checking a multiple lineage trees, the kernel of my notebook from which I opened the napari viewer, crashes.
This is the trackeback that accumulates in the error log:
Traceback (most recent call last):
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/pandas/core/indexes/base.py", line 3621, in get_loc
return self._engine.get_loc(casted_key)
File "pandas/_libs/index.pyx", line 136, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/index.pyx", line 163, in pandas._libs.index.IndexEngine.get_loc
File "pandas/_libs/hashtable_class_helper.pxi", line 5198, in pandas._libs.hashtable.PyObjectHashTable.get_item
File "pandas/_libs/hashtable_class_helper.pxi", line 5206, in pandas._libs.hashtable.PyObjectHashTable.get_item
KeyError: 't'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/vispy/app/backends/_qt.py", line 502, in mousePressEvent
self._vispy_mouse_press(
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/vispy/app/base.py", line 184, in _vispy_mouse_press
ev = self._vispy_canvas.events.mouse_press(**kwargs)
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/vispy/util/event.py", line 453, in __call__
self._invoke_callback(cb, event)
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/vispy/util/event.py", line 471, in _invoke_callback
_handle_exception(self.ignore_callback_errors,
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/vispy/util/event.py", line 469, in _invoke_callback
cb(event)
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/napari/_qt/qt_viewer.py", line 990, in on_mouse_press
self._process_mouse_event(mouse_press_callbacks, event)
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/napari/_qt/qt_viewer.py", line 949, in _process_mouse_event
mouse_callbacks(layer, event)
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/napari/utils/interactions.py", line 120, in mouse_press_callbacks
gen = mouse_drag_func(obj, event)
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/plugin.py", line 105, in show_tree
self.track_id = track_id
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/util.py", line 35, in track_id
self.on_track_id_change()
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/plugin.py", line 68, in on_track_id_change
self.property_plotter.track_id = self.track_id
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/util.py", line 35, in track_id
self.on_track_id_change()
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/visualisation/base_plotter.py", line 141, in on_track_id_change
self.plot_property()
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/visualisation/base_plotter.py", line 149, in plot_property
t, prop = self.get_track_properties()
File "/Volumes/SANDISK/CellCycleDuration/arboretum/napari_arboretum/visualisation/base_plotter.py", line 172, in get_track_properties
return all_props["t"].values, all_props[self.tracks.color_by].values
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/pandas/core/frame.py", line 3506, in __getitem__
indexer = self.columns.get_loc(key)
File "/Volumes/SANDISK/CellCycleDuration/virtualenv/lib/python3.8/site-packages/pandas/core/indexes/base.py", line 3623, in get_loc
raise KeyError(key) from err
KeyError: 't'
stress test the plotter with larger lineage trees. perhaps consider swapping to vispy for rendering
When trying to draw the tree for track #21 in the example, the tree isn't complete:
and there are a bunch of errors:
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x16ecbff70>
WARNING: Traceback (most recent call last):
File "/Users/dstansby/projects/napari/arboretum/examples/show_sample_data.py", line 26, in <module>
napari.run()
File "/Users/dstansby/projects/napari/napari/napari/_qt/qt_event_loop.py", line 402, in run
app.exec_()
File "/Users/dstansby/projects/napari/napari/napari/_qt/qt_main_window.py", line 162, in event
return super().event(e)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/app/backends/_qt.py", line 567, in event
out = super(QtBaseCanvasBackend, self).event(ev)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/app/backends/_qt.py", line 903, in paintGL
self._vispy_canvas.events.draw(region=None)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/util/event.py", line 453, in __call__
self._invoke_callback(cb, event)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/util/event.py", line 471, in _invoke_callback
_handle_exception(self.ignore_callback_errors,
<< caught exception here: >>
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/util/event.py", line 469, in _invoke_callback
cb(event)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/scene/canvas.py", line 218, in on_draw
self._draw_scene()
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/scene/canvas.py", line 277, in _draw_scene
self.draw_visual(self.scene)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/scene/canvas.py", line 315, in draw_visual
node.draw()
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/scene/visuals.py", line 103, in draw
self._visual_superclass.draw(self)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/visuals/visual.py", line 605, in draw
v.draw()
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/scene/visuals.py", line 103, in draw
self._visual_superclass.draw(self)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/visuals/visual.py", line 605, in draw
v.draw()
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/visuals/visual.py", line 451, in draw
self._program.draw(self._vshare.draw_mode,
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/visuals/shaders/program.py", line 102, in draw
Program.draw(self, *args, **kwargs)
File "/Users/dstansby/mambaforge/envs/napari/lib/python3.9/site-packages/vispy/gloo/program.py", line 498, in draw
raise RuntimeError('All attributes must have the same size, got:\n'
RuntimeError: All attributes must have the same size, got:
<VertexBuffer size=217 last_dim=4>: 217
<VertexBuffer size=218 last_dim=2>: 218
ERROR: Invoking <bound method SceneCanvas.on_draw of <SceneCanvas (PyQt5) at 0x17fe8dbe0>> for DrawEvent
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x16ecbff70>
ERROR: Invoking <bound method SceneCanvas.on_draw of <SceneCanvas (PyQt5) at 0x17fe8dbe0>> repeat 2
Currently the plugin has a fixed size, but it would be nice if it extended all the way to the bottom of the plugin pane in napari, and expanded when the sidebar is moved.
#28 adds a single colour to existing branches. Each branch should be split up into individual time chunks so we can vary the colour along a branch.
for example, it could say - please click on a cell to display the lineage tree
merges should be visualized on the tree - currently only bifurcations are shown.
It would be great to have a track stitching/relabelling feature where I can click on a track and manually change the ID, stitching together two tracks in the process. In the following example the two images are separated by two frames and the segmentation is consistent between those frames. It's unclear why the ID switches and can probably be fixed by changing certain parameters, but sometimes I'm just after a quick fix for one particular track rather than having to re-track the whole dataset.
Currently, with a tracks layer selected, every mouse click on the canvas causes a lookup/draw event in arboretum. This means that clicking to pan around or zoom in the canvas is unnecesarily slow.
I would suggest we connect just a single mouse click event to the draw routine, and ignore all others (e.g. mouse drag, zoom).
[In ~150 frames-long movie with ~15k segmented objects] Some lineage trees appear outside of the window (branch aligned to the left window wall, see pic below). Also, I noticed that some trees don't have the horizontal line showing, until I find a tree that has it shown, and from that moment on all trees show the line - even those that didn't before.
Dear all,
First of all, thanks for developing Arboretum. I was wondering if it was possible to add a feature to save the lineage tree visualized.
Additionally, it would be nice if on the nodes, we could have a small circle with the ID inside, instead of having the ID numbers on the branches.
Thanks!
Ciro
From #60 (comment):
It would be great to be able to plot more than one cell on the graph, for comparisons. For now, just plotting two sisters (tracks which have the same parent) would be more than enough. Sisters are always members of one single tree, and usually share the track 't' parameter. Here, the colorbar with one blue and one orange track is more than enough!
I was thinking perhaps we could plot all the properties in the whole tree containing the currently selected cell, ie. all the branches being displayed in the main arboretum window.
In the tree viewer there should be a horizontal line that is drawn at the current time step in the main napari window.
Since we moved to vispy
we lost the y-axis - we should add this back.
For example a file name like Lineage_Tree_{RootID}_Property_{prop_name}.png
based on the currently visualised property.
Or also including some other information about the sample where this tree comes.
Exporting it into a vector format may be useful for extra configuration.
See issue: quantumjot/btrack#210
Currently, tracks are only displayed if they're part of a tree
From #60 (comment):
If we could draw a white box (or something like this) around the cell in the middle panel so we can monitor which cell we initially clicked on & have highlighted on the tree, that'd be gold. Just thinking about ideas how to not lose track (pun intended) of which cell we're visualising.
Moved from quantumjot/btrack#216
With this sample, I have a track that splits into 2 and then they join back into one.
viewer = napari.Viewer()
tracks = [
[0, 1, 50.5, 7.4],
[0, 2, 50.3, 59.3],
[1, 3, 73.2, 100.6],
[1, 4, 75.2, 150.3],
[2, 3, 13.1, 110.4],
[2, 4, 14.8, 150.7],
[3, 5, 54.4, 200.5],
[3, 6, 52.1, 250.4],
]
graph = {2: [0], 1:[0], 3:[1, 2]}
properties = {"t": [t[1] for t in tracks], "value": [t[3] for t in tracks]}
viewer._add_layer_from_data(tracks, {'graph': graph, 'properties': properties}, "tracks")
napari.run()
This shows up correctly on the viewer, but on the lineage tree, the track 3
is shown as only having a single parent:
Also, the plot at the bottom of the right panel suggests there might be some way to plot the 'properties' of points over time?
Is that right, and if so, how can I get it to show the "value" property in my example?
Thanks!
Now that the tracks layer has merged with napari, we should update arboretum to make use of it. Changes needed are:
btrack.to_napari()
introduced in 0.3.13)skimage.regionprops
for the localization and additional properties?following latest guidance from:
https://github.com/chanzuckerberg/napari-hub/blob/main/docs/customizing-plugin-listing.md
When mousing over the tree view it would be nice to have the value that the tree is coloured by pop up as a mouse tooltip.
When manually testing show_large_tree.py
from #42 on my local installation of arboretum (on Windows 10) I noticed I can make the tree show as unconnected by redocking the arboretum widget. I have dug deeper yet. For now, documenting the steps to reproduce here (interested to see whether anyone else can reproduce).
From the error messages it looks like there are some problems with drawing the lines. But whether that is due to vispy
, napari
, or arboretum
I don't know yet.
python examples/show_large_tree.py
Python traceback and stdout after step 2
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
WARNING: Traceback (most recent call last):
File "C:\Users\Alessandro\Documents\UCL-projects\napari-plugins\arboretum\examples\show_large_tree.py", line 40, in <module>
napari.run()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\napari\_qt\qt_event_loop.py", line 402, in run
app.exec_()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\app\backends\_qt.py", line 567, in event
out = super(QtBaseCanvasBackend, self).event(ev)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\app\backends\_qt.py", line 567, in event
out = super(QtBaseCanvasBackend, self).event(ev)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\app\backends\_qt.py", line 903, in paintGL
self._vispy_canvas.events.draw(region=None)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\util\event.py", line 453, in __call__
self._invoke_callback(cb, event)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\util\event.py", line 471, in _invoke_callback
_handle_exception(self.ignore_callback_errors,
<< caught exception here: >>
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\util\event.py", line 469, in _invoke_callback
cb(event)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\scene\canvas.py", line 218, in on_draw
self._draw_scene()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\scene\canvas.py", line 277, in _draw_scene
self.draw_visual(self.scene)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\scene\canvas.py", line 315, in draw_visual
node.draw()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\scene\visuals.py", line 103, in draw
self._visual_superclass.draw(self)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\visuals\visual.py", line 605, in draw
v.draw()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\scene\visuals.py", line 103, in draw
self._visual_superclass.draw(self)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\visuals\visual.py", line 605, in draw
v.draw()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\visuals\visual.py", line 451, in draw
self._program.draw(self._vshare.draw_mode,
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\visuals\shaders\program.py", line 102, in draw
Program.draw(self, *args, **kwargs)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\program.py", line 526, in draw
canvas.context.flush_commands()
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\context.py", line 172, in flush_commands
self.glir.flush(self.shared.parser)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\glir.py", line 579, in flush
self._shared.flush(parser)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\glir.py", line 501, in flush
parser.parse(self._filter(self.clear(), parser))
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\glir.py", line 819, in parse
self._parse(command)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\glir.py", line 781, in _parse
ob.draw(*args)
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\glir.py", line 1318, in draw
gl.check_error('Check before draw')
File "C:\Users\Alessandro\anaconda3\envs\lowe-lab-env\lib\site-packages\vispy\gloo\gl\__init__.py", line 204, in check_error
raise err
RuntimeError: OpenGL got errors (Check before draw): GL_INVALID_VALUE
ERROR: Invoking <bound method SceneCanvas.on_draw of <SceneCanvas (PyQt5) at 0x24b774db280>> for DrawEvent
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
ERROR: Invoking <bound method SceneCanvas.on_draw of <SceneCanvas (PyQt5) at 0x24b774db280>> repeat 2
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
After step 3 (when weird lines appear):
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
ERROR: Invoking <bound method SceneCanvas.on_draw of <SceneCanvas (PyQt5) at 0x24b774db280>> repeat 4
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B03569310>
WARNING: Error drawing visual <vispy.visuals.line.line._GLLineVisual object at 0x0000024B035A3340>
ERROR: Invoking <bound method SceneCanvas.on_draw of <SceneCanvas (PyQt5) at 0x24b774db280>> repeat 8
Hi @quantumjot, it seems some explicitly used libraries are not being listed as dependencies.
This includes:
qtpy
-> https://github.com/lowe-lab-ucl/arboretum/search?q=qtpyskimage
-> https://github.com/lowe-lab-ucl/arboretum/search?q=skimageAdditionally the library is importing PyQt5
https://github.com/lowe-lab-ucl/arboretum/search?q=pyqt5 and it should be using qtpy
instead. More details on why this is recommended can be found in the napari best practices documentation.
Could you make theses fixes for the next release?
Cheers!
I think it has been decided to move away from using pyqtgraph
as the plotting backend, based on performance issues with large trees (#16). Requirements for the plotting backend are:
QT
so it can be a napari
widget.Perhaps there are more requiremenst? If anyone has any please edit this or leave a comment below!
Options to consider are:
QWidget
Are there any other backends we should consider? And are there any other points anyone else can add to the above considerations based on their experience?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.