dgobbi / vtk-dicom Goto Github PK
View Code? Open in Web Editor NEWA set of classes for using DICOM in VTK.
License: BSD 3-Clause "New" or "Revised" License
A set of classes for using DICOM in VTK.
License: BSD 3-Clause "New" or "Revised" License
The vtkDICOMParser reports PixelDataFound if the last element in the file was a PixelData element, even if that element was within a sequence i.e. an icon image within a DICOMDIR file.
Recently, I added the methods GetFileIndex(slice) and GetFrameIndex(slice) to vtkDICOMMetaData. This was needed to deal with the complexities of enhanced multi-frame files. However, dealing with separate file and frame indices is cumbersome. Having them both encapsulated inside a single object, which can be called vtkDICOMIndex, will simplify the interface. Then, one need only call meta->GetIndex(slice) to get the index needed to do the lookup.
A new method GetAttributeValue(vtkDICOMIndex idx, vtkDICOMTag tag) will also be needed. This might also be a good opportunity to shorten the method name to Get().
Enhanced DICOM files such as 3D MR and CT files can be read, but cannot yet be written. The way that these enhanced files are written is very similar for PET, MR, CT, and Secondary Capture so all three of these enhanced storage IODs should be supported.
Hi, all:
I have a problem when use your lib. I use cmake and make to install the lib into my source file. But when i cmake && make my project, there comes an error:
CMakeFiles/nlmFilter.dir/process.cpp.o: In function vtkSmartPointer<vtkNIFTIReader>::New()': /usr/local/include/vtk-5.10/vtkSmartPointer.h:113: undefined reference to
vtkNIFTIReader::New()'
collect2: error: ld returned 1 exit status
make[2]: *** [nlmFilter] Error 1
make[1]: *** [CMakeFiles/nlmFilter.dir/all] Error 2
make: *** [all] Error 2
Even when i use
vtkSmartPointer reader = vtkSmartPointer::New();
or
vtkNIFTIReader reader = vtkNIFTIReader::New();
I cannot pass the make, does anybody kown?
currently it fails with:
[ 61%] Compiling Java Classes
/usr/lib/jvm/default-java/bin/javac -source 1.5 -classpath /usr/lib/vtk-5.8/java -sourcepath /usr/lib/vtk-5.8/java/vtk/ -d /home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.4.4/obj-x86_64-linux-gnu/java /home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.4.4/obj-x86_64-linux-gnu/java/vtk/*.java
/home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.4.4/obj-x86_64-linux-gnu/java/vtk/vtkDICOMGenerator.java:7: cannot find symbol
symbol: class vtkObject
public class vtkDICOMGenerator extends vtkObject
^
/home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.4.4/obj-x86_64-linux-gnu/java/vtk/vtkDICOMCTGenerator.java:18: cannot find symbol
The vtkDICOMSliceSorter sorts first by slice position, and when multiple slices occupy the same position, those slices are sorted by time. The time sorting should be done by a different class.
Should it be called a "component sorter" instead? No, because "time" doesn't always become "components". If it is meant to generically handle a fourth dimension (or more specifically, the first non-spatial dimension) then it could be called a "sequence sorter", "succession sorter", or "progression sorter" but all of those sound bad. The name vtkDICOMTimeSorter is good, because 95% of the time, the 4th dimension is time, and for the other 5% of the time people will just have to put up with a name that doesn't quite fit.
These "sorters" should get a series of file, frame indices and do a stable-sort to provide a new series of indices. Generically, the (file,frame) work together as a single index and should be handled as a unit. This makes it easy to transparently deal with both enhanced multi-frame and ordinary single-frame files.
I cannot build vtk-dicom against vtk6 package (debian). It fails with:
Linking CXX executable ../bin/dicomdump
cd /home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.4.2/obj-x86_64-linux-gnu/Programs && /usr/bin/cmake -E cmake_link_script CMakeFiles/dicomdump.dir/link.txt --verbose=1
/usr/bin/c++ -g -O2 -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -D_FORTIFY_SOURCE=2 -Wno-deprecated -Wl,-z,relro CMakeFiles/dicomdump.dir/dicomdump.cxx.o -o ../bin/dicomdump -rdynamic ../lib/libvtkDICOM.so /usr/lib/x86_64-linux-gnu/libvtkImagingStatistics-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkIOImage-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkDICOMParser-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkIOCore-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkmetaio-6.0.so.6.0.0 -ljpeg -lpng -lz -ltiff /usr/lib/x86_64-linux-gnu/libvtkImagingCore-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonExecutionModel-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonDataModel-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonMisc-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonSystem-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonTransforms-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonMath-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtkCommonCore-6.0.so.6.0.0 /usr/lib/x86_64-linux-gnu/libvtksys-6.0.so.6.0.0 -ldl -Wl,-rpath,/home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.4.2/obj-x86_64-linux-gnu/lib:
../lib/libvtkDICOM.so: undefined reference to vtkIOMPIImage_AutoInit_Destruct()' ../lib/libvtkDICOM.so: undefined reference to
vtkIOMPIImage_AutoInit_Construct()'
collect2: error: ld returned 1 exit status
make[2]: *** [bin/dicomdump] Error 1
If the reader is given a set of DICOM files that have the same ImageOrientationPatient and for which some, but not all, of the ImagePositionPatient locations are repeated, then the reader crashes instead of reporting an error. Instead, it should either 1) report an error to say that it cannot reconstruct the files into a volume because some files are missing, 2) report a warning and fill in the missing slots with blank slices, or 3) report a warning and load the images as a series of slices, rather than as an multidimensional volume.
Would be nice if vtkDICOM would not list all transitive linking in export file. See doc at:
The PixelData is currently reported as being "zero bytes" in length, which might be confusing:
(7FE0,0010) OW "PixelData" : [] (0 bytes)
It would be better to report it with the actual size, if not encapsulated. The size can be computed from BitsAllocated, SamplesPerPixel, Rows, Columns, and NumberOfFrames. Or, it can be read from the file, because it is stored in the 4 bytes just before the FileOffset returned by the parser:
(7FE0,0010) OW "PixelData" : [...] (131072 bytes)
And if it is encapsulated, then report as follows:
(7FE0,0010) OB "PixelData" : [...] (compressed)
Related to this, if a value is OB, OW, OF, OD then should its value should be written as [...] instead of giving the first few values? It would make the dump more compact.
This is one of those "out there" ideas. It would be nice to be able to search for "//ImagePositionPatient" to find any ImagePositionPatient that is 2 levels deep. Or to be able to search for "//ImagePositionPatient" to recursively look for ImagePositionPatient at any depth. One immediate problem with this idea is that multiple return values are possible. This could be handled by having the Get method return a multiplex value (i.e. a value containing multiple values), but that would be bad because multiplex values are supposed to be part of the implementation and not an often-used part of the API.
This idea of wildcards in paths is, of course, borrowed from the xml XPath functionality.
Especially for comparison of names with accented characters, the lack of normalization can cause problems when searching for attribute values. Unicode chapter 3.13, which covers case folding, also has some relevant discussion on normalization for string comparison. It suggests the following:
"A modified form of Default Case Folding is designed for best behavior when doing caseless
matching of strings interpreted as identifiers. This folding is based on Case_Folding(C),
but also removes any characters which have the Unicode property value
Default_Ignorable_Code_Point=True. It also maps characters to their NFKC equivalent
sequences. Once the mapping for a string is complete, the resulting string is then normalized
to NFC. That last normalization step simplifies the statement of the use of this folding
for caseless matching."
Annex 31 of the Unicode standard provides further discussion, but it is geared towards identifiers (e.g. in source code) rather than names.
vtk-dicom requires vtkzlib when building. This should be set conditionally to VTK_USE_SYSTEM_ZLIB.
The vtkDICOMReader relies on information in the metadata to sort the input files and frames into output slices and scalar components. For data sets that strictly follow the standard for their IOD class, it does a good job. However, for non-conforming data, it cannot tell whether to assign spatial or time dimensions to the files or frames, and it instead assigns vector dimensions to them. For example, frames that are assigned to vector dimensions become scalar components, rather than slices, in the output data.
There should be reader settings that allow slices and frames to be forced to be interpreted either as slices or as components.
For some reason the java test does not seems to be loading the lib properly, see:
CT images have two features that can complicate image processing:
The vtkDICOMReader has recently been modified so that the PatientMatrix has a yz shear if the gantry is tilted. So, it would be good to have a filter that follows the reader than can use this shear to re-grid the image as a rectangular volume. There are actually several things that this post-reader filter can do:
Perhaps the filter could also work in the opposite direction, take a rectangular volume and produce a series of slices that mimic a specific gantry tilt. This would be useful for image registration, when a rectangular volume must be written with the geometry of a gantry-titled volume.
The vtkDICOMReader now reports an error if the files do not all have the same rows and columns. Instead, whenever the geometry (or data type, number of components, photometric interpretation) the data could be sorted into a new "stack" that can be chosen.
JIS X 0201 is a variant of ASCII where backslash is replaced by the yen symbol (also, tilde is replaced by a macron). When I convert JIS X 0201 to utf-8, I unconditionally convert the backslash to yen, which is wrong. This conversion should only be done for ST, LT, and UT. For other VRs, backslash is not meant to be interpreted as a character code, but is rather meant to be interpreted as a value separator and therefore it must not be converted.
In other words, perform the reverse of what vtkDICOMApplyRescale does, with a filter called (for example) vtkDICOMCreateRescale. It should examine the data, and decide on a good scale factor to fit the data into either short or unsigned short, and generate the meta data. For CT, it will add RescaleSlope and RescaleIntercept. For MR, it will add the real world value mapping sequence.
Two big difficulties: 1) the input data might not have a vtkDICOMMetaData object to be added to, and 2) the attributes go in different places in enhanced files vs. legacy files.
The filter needs to know what IOD is being built, just like the vtkDICOMGenerator. This makes me wonder if maybe the filter is unnecessary, and the conversion from real values should be the responsibility of the generator.
Convert the private dictionary .tpl files from dicom3tools into dictionary hash tables that can be used from vtk-dicom. Add a mechanism to vtkDICOMMetaData for identifying the correct dictionary to use for whichever private tags are encountered.
At least on debian, it is recommended to list jar dependencies in the Class-Path variable.
Please add a proper java manifest file.
$ cat manifest.txt
Class-Path: vtk.jar
It was a design decision to keep leading padding spaces, and any internal inter-value spaces, in the results from the AsString() method. The idea was that the result of AsString() should be as close as possible to the actual value internals.
However, I would like to reverse this decision because it conflicts with the results of GetString(), which always trims the padding.
The AsString() method should strip all values, re-concatenate them with backslash, and then return them.
The code in vtkDICOMCharacterSet is based on later versions of the code pages than are specified in the DICOM standard. Most significantly, the code is based on Unicode 7.0 while the standard lists Unicode 3.2. Similarly, the standard lists TIS 620-2533 for Thai, while the code uses ISO 8859-11 (the difference is that ISO 8859-11 defines A0 as non-breaking space, while TIS 620-2533 leaves A0 undefined).
I believe that there should not be a problem with using later versions of these standards when reading the files, but when writing the files care should be taken not to use any code points that are undefined in earlier versions of the code pages.
The LUTs are defined at the image level, meaning that they might be different for each image in a series. Since a vtkImageData generally corresponds to a series, rather than an image, the use of a vtkLookupTable to represent a palette LUT and then applying that LUT to the image data will not always work.
Two options are:
I'm partial to the second solution due to its flexibility. Later on, if I need to add support for the ultrasound "Enhanced Palette Color Lookup Table Module" or the enhanced MR/CT "Supplemental Palette Color Lookup Table Module", I can add filter classes for those as well.
Note that there is one case where loading a palette into a vtkLookupTable should be done: the "Palette Color IOD" allows a palette to be stored in its own DICOM file. Such files should be read into a vtkLookupTable, and it should be possible to generate such files from a vtkLookupTable.
This is self-explanatory.
The command-line utilities assume that argv is utf-8, which is true on OS X and generally on Linux, but not on Windows. On UNIX/Linux, type "locale" to see the encoding used by the shell.
A solution on Windows is to replace argc and argv with values from __wgetmainargs(), after conversion from UTF-16 to UTF-8. This could be done by a utility function called at the top of main(). Note that __wgetmainargs() is only supported in VC2010 and up.
Currently, if niftitodicom encounters a floating-point image, it converts the image to short but does not save the conversion factor. It should, of course, save the conversion factor in the RealWorldValueMappingSequence. Figuring out the real world units from the NIfTI intent codes doesn't seem possible, since intent codes are generally statistical (and unitless).
Should dicomtonifti do the reverse? I.e. should it write a float image if RealWorldValueMappingSequence is present? Or should it just save the intercept and slope?
Right now vtk-dicom install all headers in $DESTDIR/include. VTK has always prefered parallel installation (eg: $DESTDIR/include/vtk-X.Y).
It would make sense to have this also.
A broad range of DICOM IODs share very similar characteristics, so it is probably not necessary to have a separate generator for every IOD.
At least on debian it is required to target a particular java version for proper execution on defined JVM version.
currently vtk-dicom does not allow setting of -target / -source flag.
See equivalent issues:
https://bugs.debian.org/747108
Pay attention that you need to set both -source & -target :
https://bugs.debian.org/729513
Accessing data set attributes via vtkDICOMMetaData is flexible but sometimes inconvenient, for two very different reasons:
Solution: add GetAttributeAsXX() methods to vtkDICOMReader, which can optionally take slice and component indices (rather than file and frame indices):
There are some tricky things to watch out for, however. First, there is no easy way to allow for tag paths (though this is a minor point). Second, if a vtkDataArray is returned, it must be owned by the reader, so the reader will need a table to store all arrays that are returned. Third, deciding on the type to use for the array is tricky: should IS be a vtkIntArray, a vtkUnsignedIntArray, a vtkStringArray, or a vtkCharArray? A vtkIntArray is the most convenient, while a vtkStringArray most closely matches the DICOM standard. Perhaps it should be made explicit:
Please set SONAME/SOVERSION for shared lib ABI.
In enhanced files, keys of interest can be found in the base data set, in the PerFrameFunctionGroupsSequence sequences, or in the SharedFunctionGroupsSequence sequences. Retrieving attributes whose location is unknown can be done by searching all three locations, as per the vtkDICOMMetaDataAdapter. However, there is currently no support for querying such keys in vtkDICOMParser (or for returning information about frame-level matches, for that matter).
A solution is for the Parser to realize when it is within a FunctionalGroupsSequence, scan the whole query, and record any matches or mismatches. It might be good, in any case, for the Parser to keep track of which attributes did not match.
I cannot build + run vtk-dicom with the following settings:
$ export CFLAGS = -fvisibility=hidden -fvisibility-inlines-hidden
$ export CXXFLAGS = -fvisibility=hidden -fvisibility-inlines-hidden
$ cmake
$ make && make test
[...]
8/9 Test #9: TestDICOMJava ....................***Failed 1.21 sec
Ran the vtk-dicom Java test.
Exception in thread "main" java.lang.UnsatisfiedLinkError: vtk.vtkDICOMReader.VTKInit()J
at vtk.vtkDICOMReader.VTKInit(Native Method)
at vtk.vtkObject.(vtkObject.java:97)
at vtk.vtkAlgorithm.(vtkAlgorithm.java:378)
at vtk.vtkImageAlgorithm.(vtkImageAlgorithm.java:78)
at vtk.vtkImageReader2.(vtkImageReader2.java:258)
at vtk.vtkDICOMReader.(vtkDICOMReader.java:166)
at TestDICOMJava.main(TestDICOMJava.java:72)
9/9 Test #8: TestDICOMPython ..................***Failed 2.74 sec
Traceback (most recent call last):
File "/home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.5.0/Testing/TestDICOMPython.py", line 5, in
import vtkDICOMPython
ImportError: /home/mathieu/debian/debian-med/trunk/packages/vtk-dicom/trunk/vtk-dicom-0.5.0/obj-x86_64-linux-gnu/lib/vtkDICOMPython.so: undefined symbol: PyVTKAddFile_vtkNIFTIWriter
Since I cannot re-open an issue. I am reporting a new.
Using latest git, here is what I get:
$ git clone [email protected]:dgobbi/vtk-dicom.git
$ cd vtk-dicom
$ cmake --version
cmake version 2.8.11.1
$ cmake . /tmp/v/vtk-dicom
-- The C compiler identification is GNU 4.7.2
-- The CXX compiler identification is GNU 4.7.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
CMake Error at CMakeLists.txt:8 (cmake_policy):
Policy "CMP0022" is not known to this version of CMake.
-- Configuring incomplete, errors occurred!
Strings such as "gggg,eeee\gggg,eeee" should be converted to attribute tags in CreateValue, perhaps parentheses should also be permitted "(gggg,eeee)".
Conversion from string to SQ should also be supported, using a syntax similar to the query files, though the query file syntax lacks the ability to list multiple items in a sequence.
At least in debian you cannot install a lib (shared object with SONAME set) with undefined symbol.
With VTK 5.8 vtk-dicom need to link to vtkPythonCore
I've implemented queries so that (with the notable exception of UIDs) a multi-valued query key means that all values for the key must match the data attribute value. However, if vtkDICOMMetaData can be used to store a series of queries. If any of the queries matches the dataset, then we can say that the dataset matches the "combined query".
PixelData has a VR of OX, meaning OB or OW. So how to use it in a query? For universal matching, the VR should not matter, but apparently it does. When I do a "dicomdump -k PixelData", it complains "Unrecognized DICOM tag "PixelData"". With "-k PixelData:OW" it behaves as expected. Ditto for "-k PixelData:OF", it matches and prints the pixel data as "OW". But "-k PixelData:OB" seems to be ignored, as if no query was done whatsoever.
Further investigation is needed, because I don't know what is going on, or whether this is a shallow bug or a deep one.
It does not make much sense to have a SONAME set for the python module (glue lib):
readelf -d vtkDICOMPython.so
[...]
0x000000000000000e (SONAME) Library soname: [vtkDICOMPython.so]
It confuses debian/lint checker. Same comment for java module, no need for SONAME. See GDCM project for example on how to use cmake: NO_SONAME
The goal is a tool similar to the unix "find" command, except that it tests against the metadata in the files (study date, patient name, etc).
It will use its command-line arguments to build a dicom c-find query, but instead of querying an AE across a network, it will query files on the filesystem. It can then do one of the following:
A tricky thing is, you might want it to take a certain action on each file, or you might want it to take an action on each series, or on each study.
Items in the RealWorldValueMappingSequence define a RealWorldValueSlope and RealWorldValueIntercept. These can be used for real-world mapping in MR, since the legacy MR IOD does not include RescaleSlope or RescaleIntercept.
These could, potentially, be used when converting between NIfTI and DICOM.
One drawback: the RealWorldValueMappingSequence is too flexible, it can contain multiple items with multiple conversions (including conversions via lookup table rather than slope & intercept). This makes it difficult to support at the application level.
It would be nice if all command line tool would support --help flag. This prove particularly useful when generating man page from --help output (eg help2man on debian).
Using cmake 2.8.11, here is what I get:
dh_auto_configure -- -DCMAKE_SKIP_RPATH=OFF -DDICOM_LIB_DIR:STRING=/lib/x86_64-linux-gnu -DCMAKE_BUILD_TYPE:STRING=None -DBUILD_PYTHON_WRAPPERS:BOOL=OFF -DBUILD_JAVA_WRAPPERS:BOOL=OFF -DUSE_DCMTK:BOOL=ON -DUSE_GDCM:BOOL=ON -DBUILD_SHARED_LIBS:BOOL=ON
-- The C compiler identification is GNU 4.8.2
-- The CXX compiler identification is GNU 4.8.2
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
CMake Error at CMakeLists.txt:8 (cmake_policy):
Policy "CMP0022" is not known to this version of CMake.
Currently, a vtkDICOMValue must always have a valid VR. It is not allowed to have a VR or XX (or XS or OX). Perhaps values with a VR of XX should be allowed, but certain rules would have to be followed:
Implicit conversion to VR-less values would be allowed. This means that SetAttributeValue() would not need separate signatures for std::string etc., because std::string could be implicitly converted to a value. In fact, I would have to remove SetAttributeValue(vr, double) because it would cause ambiguity (convert to "double" or convert to "vtkDICOMValue"?).
What are the use cases? Here is one that I thought of:
vtkDICOMItem item;
item.SetAttributeValue(DC::SomeTag, 10);
meta->SetAttributeValue(DC::SomeSeqTag, item);
As you can see, this use case is weak. I could simply add a SetAttributeValue() method that takes an item, rather than relying on implicit conversion to vtkDICOMValue. Also, this doesn't help at all if I needed to add a sequence with multiple items.
If a developer does a "make install", the results should be similar to installing VTK itself. Currently, there are differences.
vtk-dicom:
include/
lib/vtkDICOMPython.so
share/java/vtkdicom.jar
lib/dicom-0.5/cmake/
share/dicom-0.5/
vtk:
include/vtk-6.2/
lib/python2.7/site-packages/vtk/vtkCommonCorePython.so
lib/vtk.jar
lib/cmake/vtk-6.2/
share/vtk-6.2/
Things to fix:
The SetAttributeValue(vtkDICOMTag tag, double value) method will check the dictionary to find the correct VR to use with the tag. However, the dictionary can return a VR of XS or OX, in which case the real VR must be determined with a more complicated set of rules. For example, XS is usually US of the pixel data is unsigned (if PixelRepresentation is 0), otherwise it is SS.
If there is insufficient information to determine the sign of XS (or the size of OX) then there is trouble. The vtkDICOMMetaData::FindDictVR() method will raise an error, but vtkDICOMItem::FindDictVR() will return UN (and in debug mode, it will assert).
Currently, it is necessary for the user to explicitly add a vtkDICOMValue(VR::US, ..) or vtkDICOMValue(VR::SS, ...). Is there a better way of at least informing the user that this needs to be done?
Would be nice to indicate minimum VTK version is:
$ cat CMakeLists.txt
[...]
find_package(VTK)
if(VTK_FOUND)
include(${VTK_USE_FILE})
endif()
thx
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.