Giter Club home page Giter Club logo

openpreserve / jpylyzer Goto Github PK

View Code? Open in Web Editor NEW
67.0 29.0 27.0 198.35 MB

JP2 (JPEG 2000 Part 1) validator and properties extractor. Jpylyzer was specifically created to check that a JP2 file really conforms to the format's specifications. Additionally jpylyzer is able to extract technical characteristics.

Home Page: http://jpylyzer.openpreservation.org/

License: Other

Shell 2.51% Python 96.03% Dockerfile 0.27% Java 1.19%
jp2 jpeg2000 code4lib

jpylyzer's Introduction

jpylyzer

About

Jpylyzer is a JP2 (JPEG 2000 Part 1) image validator and properties extractor. Its development was partially supported by the SCAPE Project. The SCAPE project is co-funded by the European Union under FP7 ICT-2009.4.1 (Grant Agreement number 270137).

Jpylyzer homepage

Please visit the jpylyzer homepage for links to the most recent package downloads (Debian packages and Windows binaries), and a User Manual which documents all aspects of the software:

https://jpylyzer.openpreservation.org/

CI Status

  • Build Status Travis-CI

  • Build Status OPF Jenkins

Using jpylyzer from the command line

Calling jpylyzer in a command window without any arguments results in the following helper message:

usage: jpylyzer [-h] [--format FMT] [--mix {1,2}] [--nopretty]
            [--nullxml] [--recurse] [--packetmarkers] [--verbose]
            [--version] jp2In [jp2In ...]

Positional arguments

Argument Description
jp2In input image(s), may be one or more (whitespace-separated) path expressions; prefix wildcard (*) with backslash (\) in Linux

Optional arguments

Argument Description
[-h, --help] show help message and exit
[--format FMT] validation format; allowed values are jp2 (JPEG 2000 Part 1, used by default), j2c (Part 1 codestream), jph (JPEG 2000 Part 15 / High Throughput JPEG 2000) and jhc (Part 15 codestream)
[--mix {1,2}] report additional output in NISO MIX format (version 1.0 or 2.0)
[--nopretty] suppress pretty-printing of XML output
[--nullxml] extract null-terminated XML content from XML and UUID boxes(doesn't affect validation)
[--recurse, -r] when analysing a directory, recurse into subdirectories
[--packetmarkers] Report packet-level codestream markers (plm, ppm, plt, ppt)
[--verbose] report test results in verbose format
[-v, --version] show program's version number and exit

Output

Output is directed to the standard output device (stdout).

Examples

Validate JP2 image:

jpylyzer rubbish.jp2 > rubbish-jp2.xml`

Validate JPEG 2000 Part 1 codestream:

jpylyzer --format j2c rubbish.j2c > rubbish-j2c.xml`

Validate JPH (High Throughput) image:

jpylyzer --format jph rubbish.jph > rubbish-jph.xml`

Validate JPEG 2000 Part 15 (High Throughput) codestream:

jpylyzer --format jhc rubbish.jhc > rubbish-jhc.xml`

In the above examples, output is redirected to the output files ‘rubbish-???.xml’. By default jpylyzer’s XML is pretty-printed, so you should be able to view the file using your favourite text editor. Alternatively use a dedicated XML editor, or open the file in your web browser.

Output format

The output file contains the following top-level elements:

  1. One toolInfo element, which contains information about jpylyzer (its name and version number)

  2. One or more file elements, each of which contain information about about the analysed files

In turn, each file element contains the following sub-elements:

  1. fileInfo: general information about the analysed file

  2. statusInfo: information about the status of jpylyzer's validation attempt

  3. isValid: outcome of the validation

  4. tests: outcome of the individual tests that are part of the validation process (organised by box)

  5. properties: image properties (organised by box)

  6. propertiesExtension: wrapper element for NISO MIX output (only if the --mix option is used)

  7. warnings: reported warnings

Using jpylyzer as a Python module

Instead of using jpylyzer from the command-line, you can also import it as a module in your own Python programs. To do so, install jpylyzer with pip. Then import jpylyzer into your code by adding:

from jpylyzer import jpylyzer

Subsequently you can call any function that is defined in jpylyzer.py. In practice you will most likely only need the checkOneFile function. The following minimal script shows how this works:

#! /usr/bin/env python3

from jpylyzer import jpylyzer

# Define JP2
myFile = "/home/johan/jpylyzer-test-files/aware.jp2"

# Analyse with jpylyzer, result to Element object
myResult = jpylyzer.checkOneFile(myFile)

# Return image height value
imageHeight = myResult.findtext('./properties/jp2HeaderBox/imageHeaderBox/height')
print(imageHeight)

Here, myResult is an Element object that can either be used directly, or converted to XML using the ElementTree module[^3].

For validation a raw JPEG 2000 codestreams, call the checkOneFile function with the additional validationFormat argument, and set it to j2c:

# Define Codestream
myFile = "/home/johan/jpylyzer-test-files/rubbish.j2c"

# Analyse with jpylyzer, result to Element object
myResult = jpylyzer.checkOneFile(myFile, 'j2c')

Steps in preparing a jpylyzer release

See instructions here

jpylyzer's People

Contributors

adamretter avatar bitsgalore avatar carlwilson avatar davetaz avatar david-russo avatar hsilva-keep avatar info6005 avatar larsmans avatar malaterre avatar matoking avatar midendian avatar rschmidt13 avatar stweil avatar tledoux avatar userjd avatar virtualtim 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

Watchers

 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

jpylyzer's Issues

Feature request: pixel data analytics

  1. PSNR for lossy compression with various compression settings
  2. lossless: compare round-trip compress-decompress image with original image, to verify lossyless

Website: Resources not loaded over HTTPS

Dev Effort

0.5D

Description

When opening the Jpylyzer Website over HTTPS, CSS is blocked by modern browsers to avoid loading it over an unsecure connection. Images included via HTTP are loaded but show a warning in the browser's console.

To avoid this you could:

  1. link all resources via their HTTPS links which works when opening the page over HTTP and HTTPS
  2. use relative links

Homepage updates

In usage instructions add desc of --nullxml and --nopretty options. Do this once binary builds are ready.

Unicode decode error if file name / path contains non-ASCII characters

Jpylyzer crashes with a Unicode Decode Error if the name or path of an analysed file contains non-ASCII characters, e.g.:

jpylyzer NL-Albert-Mülli_1-2_new_Page_2_Image_0001.jpf

The problem also occurs if the (full) path to the file contains such characters (since jpylyzer adds the full path to its output!).

Will be fixed in version 1.6.1!

Update

The fix in 1.6.1 now causes an attribute error when running jpylyzer under Python 3.2 because the decode attribute does not exist for str objects in that version! Will be fixed in 1.6.2 (1.6.1 Win binaries and Deb packages are unaffected by this).

Jpylyzer can't find SOD marker if tile part contains optional marker segments

If a tile-part contains any optional marker segments (COD, COC, QCD, etc.) it will report the following error:

- <contiguousCodestreamBox>
- <tileParts>
- <tilePart>
  <foundSODMarker>False</foundSODMarker> 
  </tilePart>
  </tileParts>
  </contiguousCodestreamBox>

This is because jpylyzer erroneously expects the SOD (start-of-data) marker at a fixed byte position after the SOT.

As a result, images that contain any of these markers will fail validation.

This applies, for example, to JP2s that were created using OpenJPEG (1.5) using the default settings, even though such images are really valid.

Pages: simplify updating of binary paths

For Win, Debian links paths remain identical apart from the version number. So making the version number a site variable would simplify updating to new versions.

Also: display current version number on front page!

XMP metadata in UUID boxes

Some software products use UUID boxes for storing XMP metadata. See the XMP spec:

http://wwwimages.adobe.com/www.adobe.com/content/dam/Adobe/en/devnet/xmp/pdfs/cs6/XMPSpecificationPart3.pdf

Page 16 of that document does mention that XMP packets are stored in a UUID box for JPEG 2000 (confusingly they refer to Part 12 -ISO Base Media Format- of the standard there, which is a container format for video. This is something else than JP2/ Part 1 (even though it uses a similar box structure), but both Exiftool and Kakadu have adopted this for JP2 (and possibly other products as well).

Currently jpylyzer isn't able to read XMP metadata in a UUID box. Support for this will be added in a future version of jpylyzer.

DTD / XSD validation

This is somewhat related to issue #23. It would be nice if there was a DTD for jpylyzer produce XML instances. Thanks

Infinite loop if image only contains one tile-part

Jpylyzer will enter an infinite loop for images that contain only one single tile-part (caused by the fact that the value of psot is 0 in that case, and jpylyzer doesn't handle this well).

This applies e.g. to some GeoJP2 images created with the ECW JPEG 2000 SDK. Incidentally JHOVE 1.6 will report an ill-formed codestream for these images, so most likely it also doesn't handle the zero psot values correctly.

Will be fixed in jpylyzer 1.5.3 ...

XML contains null byte in case of JP2s with UUID Info Box

XML contains null byte in case of JP2s with UUID Info Box, which means that output is not well-formed and cannot be opened with XML editor/viewer.

Happens at least with the Win32 binary under Windows 7 (strangely if I remember well problem didn't happen under XP).

A bit embarrassing, as the new sample images all contain this (otherwise not very common) box.

Solution: try to strip away last character (which should be null byte according to standard, but I assumed that elementtree would take care of this automatically!)

Avoiding control characters in XML output

Under various circumstances (e.g. corrupted files) XML output may contain illegal control characters (e.g. null bytes). For properties that are directly converted from bytes to text there is already a mechanism that will suppress null characters, but they may stil occur in UTF-8 and Latin-encoded text.

This may affect output related to codestream comments (Latin), and URL boxes within UUID Info boxes (UTF-8).

XML boxes might be affected as well, although the built-in well formedness check in the XML box validator function will probably prevent any problems here.

Possible solutions:

  1. Use containsControlCharacters directly in URL and Codestream validator functions (i.e. before decoding to UTF-8/Latin)
  2. OR replace control characters with something else

While at it, containsControlCharacters should catch all characters in the 0-31 byte range (none of which are allowed in XML and none of which can be replaced with entity references)

When bitmashing, jpylyzer sometimes breaks

Some malformed (deliberately bit-flipped) JP2s appear to make jpylyzer hang. e.g. cc-16-kdu.jp2-killed-10918 runs for a long time and then stops, outputting simply 'Killed'.

anj@anj-VirtualBox:~/bitwiser$ hexdump -C cc-16-kdu-hangs/cc-16-kdu.jp2-killed-10918 > hd1
anj@anj-VirtualBox:~/bitwiser$ hexdump -C src/test/resources/cc-16-kdu.jp2 > hd2
anj@anj-VirtualBox:~/bitwiser$ diff hd*
131c131
< 00000830  00 6a 70 32 63 ff 4f ff  51 00 32 00 00 80 00 00  |.jp2c.O.Q.2.....|

---
> 00000830  00 6a 70 32 63 ff 4f ff  51 00 32 00 00 00 00 00  |.jp2c.O.Q.2.....|

More interestingly, cc-16-kdu.jp2-killed-22181 appears to cause an infinite loop:

....
User warning: ignoring unknown box
User warning: ignoring unknown box
User warning: ignoring unknown box
....

There may be further interesting modes of failure hidden in the other examples I have. I'll see about attaching them... Seems I cannot - so I uploaded the zip of fails here: https://dl.dropbox.com/u/135740/cc-16-kdu-hangs.zip

Issue running 1.6.0 against complex file name

I have three jp2 images which are named:

"x-fmt-392 (JPEG2000) [1].jp2"
"x-fmt-392 (JPEG2000) [2].jp2"
"x-fmt-392 (JPEG2000) [3].jp2"

When running any of the commands:

jpylyzer 'x-fmt-392 (JPEG2000) [1].jp2'
jpylyzer "x-fmt-392 (JPEG2000) [1].jp2"
jpylyzer x-fmt-392\ (JPEG2000)\ [1].jp2

the output is:
"User warning: no images to check!"

Similarly, running:

jpylyzer *.jp2

produces:
jpylyzer: error: unrecognized arguments: x-fmt-392 (JPEG2000) [2].jp2 x-fmt-392 (JPEG2000) [3].jp2

Running either of

jpylyzer '.jp2'
jpylyzer "
.jp2"

results in the correct running of the program. Likewise if I rename one of the files "test.jp2", I am able to run the program on that file on its own.

debian: no debconf config

The thought was to prompt for the users JP2 profile on first setup, not sure if this is needed. If not then the template file should be removed.

Test foundSODMarker not in User Manual

Section 7.9 of the User Manual documents the Tile-part level properties and tests, but a description of test foundSODMarker is missing.

This test checks if the last marker in every tile-part is a start of data marker.

Will be added in next revision of Manual.

Validation of codestream comments

JP2 filespec states that (non-binary) codestream comments use iso-8859-15 / Latin encoding. However jpylyzer doesn't check for the presence of code points that are undefined in that encoding. There should be a test for this.

Add missing codestream marker segments

Dev Effort

TBC

Description

Currently jpylyzer doesn’t support the full set of marker segments that can occur in a codestream. Missing ones should ideally be included at some point (although some of them are pretty rare).

A good first step would be to create empty validator functions for all missing marker segments, which is quick and easy to implement. This would allow jpylyzer to report at least the presence of these markers in a file (without providing any details about its fields). This is already very useful information by itself, as some decoders appear to choke on some of the more obscure marker segments, so knowing they're there can already be helpful.

scriptName is empty when jpylyzer is called from Java - Jython

(Submitted by email by Thomas Ledoux of BnF while using Jython)

1/ when called from java, the scriptName as no value (probably because sys.argv[0] is not valuated), it would be nice to add a safeguard
if len(scriptName) == 0:
scriptName = 'jpylyzer'
so that the output fills the

Feature request: User Manual as Markdown Extra, figures as SVG

The source document of the User Manual is currently an MS Word document, which is a bit awkward to edit/maintain. Apart from that it creates a dependency on proprietary software (LibreOffice / OpenOffice will mess up the layout). Some of the figures were originally created in MS Powerpoint, has similar problems.

An alternative would be to migrate the User Manual to Markdown Extra (which includes table support), and to provide the figures as SVG. This would lower the barrier to contributing to the User Manual, and it would simplify things as well. In combination with a tool like Pandoc it would also enable us to generate versions of the User Manual in pretty much any desired delivery format (HTML, PDF, EPUB, etc.).

How to do this?

Possible workflow:

  1. Convert Word doc to HTML

  2. Clean up HTML if needed (e.g. HTMLTidy)

  3. Convert HTML to Markdown. In Pandoc:

    pandoc -f html -t markdown_phpextra umanual.html > umanual.md

  4. Manually clean up result

More info on 'unknown' boxes

Mathieu Malaterre suggested to include a bit more info on any "unknown" boxes encountered by jpylyzer, such as the associated string. Will look at this for jpylyzer 1.6!

Add jpylyzer page

It would be nice to have a proper jpylyzer home page, which would be pretty easy to set up with Github pages.

No defined namespace

Also submitted by Thomas Ledoux (e/mail)±

2/ it's surprising that the XML output doesn't have any namespace defined (that would be handy to validate the output and to mangle it with XSLT...)
The modification seems straightforward
replace
root=ET.Element('jpylyzer')
by something like
root=ET.Element('jpylyzer', {'xmlns': 'http://openplanetsfoundation.org/ns/jpylyzer'})
I suppose the main problem is to decide which URI to choose (openplanets or kb ???)

jpylyzer 1.5 doesn't build on AWS

While trying to build Debian packages for jpylyzer 1.5 on AWS the following problems occur (both for AMD 64 and i386) (copied from 'state.txt'):

Switched to branch 'master'
From git://github.com/openplanets/jpylyzer
   8d15734..cc1d25d  master     -> origin/master
 * [new tag]         1.5.0      -> 1.5.0
From git://github.com/openplanets/jpylyzer
 * [new tag]         1.1.5      -> 1.1.5
 * [new tag]         1.1.6      -> 1.1.6
 * [new tag]         1.2.0      -> 1.2.0
 * [new tag]         1.3.0      -> 1.3.0
 * [new tag]         1.4.0      -> 1.4.0
Updating 8d15734..cc1d25d
Fast-forward
 boxvalidator.py        |  358 ++-
 buildWin32.bat         |    1 +
 byteconv.py            |   24 +
 config.py              |    1 +
 debian/jpylyzer.pod    |    4 +
 etpatch.py             |    6 +-
 jpylyzer.py            |   68 +-
 jpylyzerUserManual.pdf |11787 +++++++++++++++++++++++++-----------------------
 8 files changed, 6455 insertions(+), 5794 deletions(-)
 create mode 100644 config.py
Note: checking out '1.5.0'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -b with the checkout command again. Example:

  git checkout -b new_branch_name

HEAD is now at cc1d25d... By default only report results of individual tests that failed, added --verbose switch which will force reporting of everything (second try)
--2012-04-10 08:42:40--  http://p2-registry.ecs.soton.ac.uk/opf/gh2ch/?repo=https://github.com/openplanets/jpylyzer&software=jpylyzer&distribution=stable&urgency=low
Resolving p2-registry.ecs.soton.ac.uk... 152.78.189.23
Connecting to p2-registry.ecs.soton.ac.uk|152.78.189.23|:80... connected.
HTTP request sent, awaiting response... 302 Found
Location: get_changelog.php?repo=https://github.com/openplanets/jpylyzer&software=jpylyzer&distribution=stable&urgency=low [following]
--2012-04-10 08:42:40--  http://p2-registry.ecs.soton.ac.uk/opf/gh2ch/get_changelog.php?repo=https://github.com/openplanets/jpylyzer&software=jpylyzer&distribution=stable&urgency=low
Reusing existing connection to p2-registry.ecs.soton.ac.uk:80.
HTTP request sent, awaiting response... 200 OK
Length: 7829 (7.6K) [text/plain]
Saving to: `debian/changelog'

     0K .......                                               100%  151M=0s

2012-04-10 08:42:45 (151 MB/s) - `debian/changelog' saved [7829/7829]

Use of uninitialized value $ENV{"HOME"} in concatenation (.) or string at /usr/share/perl5/Dpkg/BuildFlags.pm line 109.
dpkg-buildpackage: export CFLAGS from dpkg-buildflags (origin: vendor): -g -O2
dpkg-buildpackage: export CPPFLAGS from dpkg-buildflags (origin: vendor): 
dpkg-buildpackage: export CXXFLAGS from dpkg-buildflags (origin: vendor): -g -O2
dpkg-buildpackage: export FFLAGS from dpkg-buildflags (origin: vendor): -g -O2
dpkg-buildpackage: export LDFLAGS from dpkg-buildflags (origin: vendor): -Wl,-Bsymbolic-functions
dpkg-buildpackage: source package jpylyzer
dpkg-buildpackage: source version 1.5.0
dpkg-buildpackage: source changed by Johan van der Knijff <[email protected]>
 dpkg-source --before-build jpylyzer-1.5.0
dpkg-buildpackage: host architecture i386
 debian/rules clean
dh_testdir
dh_testroot
rm -f build-stamp
rm -f jpylyzer.1
make clean
make[1]: Entering directory `/home/ubuntu/jpylyzer-1.5.0'
rm -fR build
rm -fR dist
rm -f jpylyzer.spec
rm -f logdict2.7.2.final.0-1.log
rm -f logdict2.7.2.final.0-2.log
rm -f logdict2.7.2.final.0-3.log
rm -f warnjpylyzer.txt
make[1]: Leaving directory `/home/ubuntu/jpylyzer-1.5.0'
dh_clean
    rm -f debian/jpylyzer.substvars
    rm -f debian/jpylyzer.*.debhelper
    rm -rf debian/jpylyzer/
    rm -f debian/*.debhelper.log
    rm -f debian/files
    find .  \( \( -type f -a \
            \( -name '#*#' -o -name '.*~' -o -name '*~' -o -name DEADJOE \
         -o -name '*.orig' -o -name '*.rej' -o -name '*.bak' \
         -o -name '.*.orig' -o -name .*.rej -o -name '.SUMS' \
         -o -name TAGS -o \( -path '*/.deps/*' -a -name '*.P' \) \
        \) -exec rm -f {} \; \) -o \
        \( -type d -a -name autom4te.cache -prune -exec rm -rf {} \; \) \)
 dpkg-source -b jpylyzer-1.5.0
dpkg-source: warning: no source format specified in debian/source/format, see dpkg-source(1)
dpkg-source: info: using source format `1.0'
dpkg-source: info: building jpylyzer in jpylyzer_1.5.0.tar.gz
dpkg-source: info: building jpylyzer in jpylyzer_1.5.0.dsc
 debian/rules build
dh_testdir
dh_testdir
touch build-stamp
make build
make[1]: Entering directory `/home/ubuntu/jpylyzer-1.5.0'
pymakespec --onefile jpylyzer.py
make[1]: pymakespec: Command not found
make[1]: *** [build] Error 127
make[1]: Leaving directory `/home/ubuntu/jpylyzer-1.5.0'
make: *** [build] Error 2
dpkg-buildpackage: error: debian/rules build gave error exit status 2
cp: omitting directory `jpylyzer'
rm: cannot remove `jpylyzer': Is a directory
DONE

Add Windows binaries + PDF Manual to repo

With Github's decision to drop support for separate file uploads we have no location for storing new versions of the Windows binaries and the documentation PDF.

Solution: include both in repo itself (these files are pretty small anyway).

Once this is done the links on the OPF jpylyzer page need to be updated as well.

End-of-line characters removed from extracted XML

If extracted XML contains end-of-line characters, they are removed in jpylyzer's output. This happens here (byteconv.py):

def removeControlCharacters(string):
    # Remove control characters from string
    # Source: http://stackoverflow.com/a/19016117/1209004
    return "".join(ch for ch in string if unicodedata.category(ch)[0]!="C")

I tried to fix this by changing the Unicode category code from C to Cc. However, in that case null characters aren't filtered out, even though they are part of the Cc category. See:

http://www.fileformat.info/info/unicode/category/Cc/list.htm

Might be a bug in unicodedata. For now I'll just leave it as it is.

Jpylyzer crashes on very large images

Jpylyzer loads the whole image into memory, which causes an exception if system memory is exceeded. No easy solution for this, as changing this behaviour would require fundamentally different way of parsing.

To do:

  • Add exception handler so memory errors are handled gracefully
  • Perhaps add output element that reports jpylyzer success/failure at analysing file

Extracted XML incomplete using Python 2.7

While running jpylyzer with --nullxml OPTION on this file:

http://sdowww.lmsal.com/sdomedia/hv_jp2kwrite/v0.8/jp2/AIA/2014/02/01/304/2014_02_01__00_11_07_13__SDO_AIA_AIA_304.jp2

Doing this with Python 2.7, some of the resulting XML elements are empty, even though in reality they do contain text (e.g. look at <HV_COMMENT> at the bottom). Using Python 3.3 it works correctly. So something goes wrong while parsing the XML. Could be a bug in ElementTree.

This also affects the Windows executables / Debian packages, since they are built using Python 2.7.

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.