Giter Site home page Giter Site logo

pyinat / naturtag Goto Github PK

View Code? Open in Web Editor NEW
35.0 35.0 4.0 88.97 MB

Tag your nature photos with iNat taxonomy and observation metadata

Home Page: https://naturtag.readthedocs.io

License: MIT License

Python 96.80% Shell 2.91% PowerShell 0.29%
biodiversity biodiversity-data cli darwin-core dwc exif hierarchical-keywords inaturalist iptc photography taxonomy xmp

naturtag's People

Contributors

dependabot[bot] avatar jwcook avatar kianmeng 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

Watchers

 avatar  avatar  avatar

naturtag's Issues

Improved layout & theming

Menus

  • Theming / styling for context menu to match KivyMD
  • More context menu items:
    • Taxon: view on iNaturalist
    • Observation: view on iNaturalist

Layout

  • Redo buttons on main image selection screen
  • Tabbed view or navbar buttons to switch between main screens
  • Show thumbnail item for selected taxon (if any)
  • Show thumbnail item for selected observation (if any)

Refresh feature for both GUI and CLI

The purpose of this will be to take any images with partial metadata (for example, just a taxon ID), and update them with full metadata.

  • Look for all images in a directory with metadata identifying a taxon and/or observation
    • Add --recursive option
  • Query info from iNaturalist API
  • Translate into EXIF + XMP metadata
  • Update images + sidecar files with new metadata
  • Add a GUI feature to do the same

Most of the necessary pieces are already implemented, they just need to be combined and wrapped in a CLI command.

Basic image tagging with CLI

The CLI should be able to:

  • Take a set of images plus either an observation or taxon ID
  • Fetch info from iNaturalist API
  • Convert this info to image metadata (at least XMP)
  • Write updated metadata to specified images

Metadata-based photo clustering

This is a more complex/far-off feature idea, but a very useful one. Currently, the app only supports tagging one taxon or observation at a time. These features would make it suitable for processing many at once.

  • After loading images, "cluster" them by taxon/observation (if tagged)
  • Group related images together in a (collapsible/expandable) box
    • Something like the iNaturalist web UI for observation uploads
  • Display a group's (condensed) taxon/observation info in a layout to the right
  • Clicking a taxon/observation from that list will go to its search/info page
  • Display remaining ungrouped/untagged images in a separate layout to the left
  • Drag ungrouped images into an existing group

Image metadata view

  • Basic metadata display from thumbnail context menu
  • Refresh metadata in UI after tagging
  • Prettier version: display in a data table or formatted list
  • Show hierarchical keywords in a tree, if available
  • Use pygments to highlight metadata

Tab for user's observation taxa

Quick notes on new user-observed taxa tab:

  • Use GET /observations/species_counts
  • Submit PR to pyinaturalist for additional endpoint(s)
  • Run an initial search for all user's observation taxa
  • On taxon selection screen, indicate if the selected taxon has been observed by the user (another line item? or icon?)
  • Add option in Settings to toggle including or excluding casual observations (default false)
  • Add button on cache screen to manually refresh observed species
  • Save last search date/time
  • Refresh observed taxa on startup only if expired

Organize (rename / move) image files based on taxon and observation info

Goes with #50. I'll need to pick one of the two types of feature:

Simple renaming (filename suffix)

Both the CLI and GUI could optionally append the taxon name to the updated image(s), like:

$ naturtag -t 792988 IMG_2020_02_02.jpg
Written to IMG_2020_02_02_Dryobates pubescens_(Downy_Woodpecker).jpg

Advanced renaming (filename/path template)

A more advanced feature would be to take a file path template, with metadata fields as placeholders, for example:

~/Observations/{year}/{month}/IMG_{date}_{time}_{taxon.name}_({taxon.preferred_common_name})

For dates, python date format strings could be used.

Issues with KivyMD DataTables in Metadata view

There are some known issues with KivyMD DataTables as of 0.104.1. It appears that they will be fixed in the next release, however.
I will probably hold off on the next naturtag release until those fixes are released.

Auto-scan for taxon and observation info when adding local images

Scenario: an image was tagged previously with enough info to look up an existing taxon and/or observation.

  • Read, combine, and process metadata on loading a local image
  • Look for IDs in keywords or DwC metadata
  • Look for rank=name formatted keywords or DwC metadata
  • Query taxon
  • If found, select this taxon on the taxon info screen (#3)
  • Query observation
  • If found, select this observation on the observation info screen (#46 )

Currently this only aims to support handling a single taxon or observation at a time. Handling a set of images associated with multiple taxa or observations may be out of scope, or a cool (but complicated) future feature.

Optimize bulk taxon requests

Often there can be a large number of GET /taxa API requests made in a short amount of time. Currently this is done naively by fetching a single taxon record at a time and caching the result, so any subsequent requests for taxon_id=x will get the cached result. The problem with doing bulk requests instead is that a request for taxon_id=x,y,z would not result in any subsequent cache hit for taxon_id=x, taxon_id=x,z, etc.

So in other words, in the long term, there won't be any redundant requests for a given taxon, but in the short term, naturtag can reach the API rate limit while building up the cache.

There are lots of potential solutions to this:

  • Instead of just relying on requests-cache, manually build and query a local SQLite db of taxon data (just a relevant subset of columns).
  • Extend requests-cache to create individual cache entries for bulk requests; e.g. a request for taxon_id=x,y,z will create separate cache entries for taxon_id=x, taxon_id=y, taxon_id=z.
  • Possibly pre-load either of these options with data from the iNat bulk export tool

CLI: Support searching by taxon name (exact)

This would work by specifying a full scientific name:

naturtag --taxon-name 'Dirona picta'

Performing a fuzzy search (with potentially multiple results) will take a bit more work and can be a separate issue.

Status bar & I/O progress bars

This will be a small status bar at the bottom of the screen, used to display I/O progress and status messages.

  • Add status bar to all screens
  • Show progress when loading stored taxa (history, etc.)
  • Show progress when loading large number of children in taxonomy section
  • Show progress when loading taxon search results
  • Animate fade-out after finishing progress

Taxon search history & frequently viewed items

For easier access when exploring taxa, it would be useful to have a list of recently selected items. Related to #17.

  • Store history (up to some maximum?) of selected taxa
  • Persist history in a config file in app dir (taxon IDs only)
  • Also save most frequently viewed taxa {id: num_views}
  • Load history on startup
  • Create tabbed view to switch between search, history, frequent
  • Display history as a scrollable list view, same as ancestor/children section (#3)
  • Display frequent items as a scrollable list view
  • Add an 'Up one level' button to quickly go to the selected taxon's parent (if any)
  • Delay loading all the info for taxa in history/frequent, and show progress bar
    • EDIT: Moved to Issue #18

Misc Behavior

  • Deduplicate history items for display
  • When a taxon is selected, update items in history
  • If item already exists in history, move existing object to the top
  • When a taxon is selected, update and re-sort frequent items

Taxon autocomplete search

The UI should provide a feature to search taxa by name, with autocomplete functionality similar to the iNat web UI.

  • Text input field with basic integration with GET /taxa_autocomplete API endpoint
  • Query rate-limiting/input delay to prevent spamming requests
  • Display a dropdown with matches, including at least rank, scientific name, and common name
    • Make dropdown items selectable, and when clicked, fetch full taxon info to display (see issue #3)
  • Press enter to run full search

Configurable user data path

One of the two following options would be useful:

Option 1: Portable mode

This option would store all settings, cache, thumbnails, etc. in the application's own directory instead of the system app directory (~\AppData\Local, ~/.local/, etc.).

  • If config file is present in the app dir, use that as DATA_DIR
  • On initial startup, provide a prompt to use in portable mode or normal mode

Option 2: Configurable user data path

Currently the config file is also stored in the data dir, so would need some other way to specify where to look for the initial config file

Finds taxa but can't encode jpg

Again, running under MacOS Mojave command line.

naturtag -t 48978 # seems to run correctly

naturtag -c -t 48978 # gives multiple identical values for common names

naturtag -d -x -t 48978 Dirona_picta.jpg # multiple errors, below. Let me know if I can help.

Directory Thumbnail, entry 0x0201: Data area exceeds data buffer, ignoring it.

Directory Thumbnail, entry 0x0201: Data area exceeds data buffer, ignoring it.

Directory Thumbnail, entry 0x0201: Data area exceeds data buffer, ignoring it.

Directory Thumbnail, entry 0x0201: Data area exceeds data buffer, ignoring it.

Directory Thumbnail, entry 0x0201: Data area exceeds data buffer, ignoring it.

Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.6/bin/naturtag", line 33, in
sys.exit(load_entry_point('naturtag==0.6.0', 'console_scripts', 'naturtag')())
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/click/core.py", line 829, in call
return self.main(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/click/core.py", line 782, in main
rv = self.invoke(ctx)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/click/core.py", line 1066, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/click/core.py", line 610, in invoke
return callback(*args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/click/decorators.py", line 21, in new_func
return f(get_current_context(), *args, **kwargs)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/naturtag/cli.py", line 122, in tag
glob_paths(image_paths),
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/naturtag/tagger.py", line 26, in tag_images
all_metadata.append(tag_image(image_path, keywords, dwc_metadata, create_xmp_sidecar))
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/naturtag/tagger.py", line 35, in tag_image
metadata.write(create_xmp_sidecar=create_xmp_sidecar)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/naturtag/models/image_metadata.py", line 96, in write
self._write(self.image_path)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/naturtag/models/image_metadata.py", line 112, in _write
img.modify_xmp(self.xmp)
File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/site-packages/pyexiv2/core.py", line 61, in modify_xmp
self.img.modify_xmp(self._dumps(data), encoding)
RuntimeError: XMP Toolkit error 102: Composite nodes can't have values
Failed to encode XMP metadata.

Starred / Saved taxa

Add the ability to save a selected taxon. Related to #14.

  • Add a 'star' toggle button next to selected taxon
  • Persist starred items in a config file in app dir (taxon IDs only)
  • Load on startup
  • Add a tab on the taxon search screen for starred items
  • Selecting the a 'star' toggle button adds taxon to the list and Starred tab
  • Selecting the a 'star' toggle button again removes the taxon from the list and Starred tab
  • Also add a 'remove' button to items in Starred tab
  • Add ability to reorder favorites
    • Drag & drop functionality does not seem to be possible with MDListItem
    • Maybe an 'up' button or right-click context menu option?

Cache controls & expiration

  • Provide expiration for cached API responses so they get refreshed periodically
    • Handle observations separately from taxa?
  • Provide size estimate of thumbnail cache in Settings menu
  • Provide button in Settings menu to manually clear thumbnail cache
  • Provide size estimate of API cache in Settings menu
  • Provide button in Settings menu to manually clear API cache
  • Provide button in Settings menu to clear history (including frequent)
  • Provide button in Settings menu to clear favorites

Known bugs

During prototyping/early development, known bugs and other quirks will be listed here instead of in separate issues.

  • Some thumbnails fetched from iNat fail to be processed by Kivy to be saved locally
    • This was an issue with image format detection: Kivy accepts jpg but not jpeg, PIL accepts jpeg but not jpg
  • Thumbnails are upside-down when retrieved later after initial load
    • Now applying image rotation based on EXIF orientation, if available
    • Other images fetched from iNat have no EXIF orientation but are always transposed; still not sure why, but now generate_thumbnails() will flip them by default.
  • White box randomly appears sometimes over 'Children' list in Taxonomy section
    • Note: This seems to only happen when downloading several images at once
    • Does not seem to happen when using previously cached thumbnails
    • Possible bug with Kivy AsyncImage?
  • Rogue tooltips appear from items in unselected tab
    • Possible bug with KivyMD HoverBehavior?
    • Decided to just remove tooltips for items in tabbed views since they were causing too many problems
  • When dragging & dropping files, non-image files will crash the application
  • Image previews always say they have a sidecar even if they don't
  • Case-insensitive glob on non-Windows platforms (glob uses os.path.normcase, which is case-insensitive only on Windows!)

Taxon full search

In addition to searching by name with autocompletion (issue #4), the UI should provide additional search filters for most of the relevant parameters available in the GET /taxa API endpoint.

Search filters

  • Buttons to filter by 'iconic taxa' categories (birds, mammals, insects, etc.)
  • Filter by rank: exact, min, and/or max
    • Dropdowns or multiselect list?
  • Filter by locale and preferred_place_id (defined in settings; see issue #13 )
  • Filter by "is active"

Search results

  • Display basic list of first page of results with rank, scientific name, common name
    • Correctly switch to Search Results tab after running search
  • Display inline thumbnail for taxon default photo
  • Selecting a taxon from results should then fetch and display full taxon info (see issue #3)

CI & other automation

  • Basic Travis CI config
  • Run tests
  • Build Sphinx docs
  • Publish docs to to readthedocs.io
    • docs badge

Improved taxon autocomplete search

Follow-up from #4:

Appearance

  • Dismiss dropdown after selection
  • Correctly return layout to original state after dismissing the dropdown and re-selecting text input
  • Use 'unknown' icon as a placeholder/default icon

Refactoring

  • Use events instead of direct references between components
  • Restructure so client code only has to specify one top-level widget and not all the child widgets

Improved image selection

Image Previews

  • On drag-and-drop event, filter out non-image files instead of crashing
  • Change the color of the photo overlay box based on its metadata
    • Has observation: primary color; has taxon: accent color; otherwise: grey
  • Update overlays after tagging
  • 'X' button on images to close (middle-click + 'remove' from context menu is enough for now)

Selection

  • Add recursive option (default?) to get_images_from_dir
  • Add ability to Ctrl-V paste an iNaturalist URL to select a taxon or observation
  • Add ability to Ctrl-V paste image files and/or directories
    • EDIT: Apparently this is not possible, since file references copied to the system clipboard from a file explorer are not tex

Layout

  • Save last used directory and open that on app launch
  • Add keyboard shortcuts to open file chooser

Context menus for local photos

On main image selector screen:

  • Right-click image to open a context menu
    • View taxon (option disabled if none)
    • View observation (option disabled if none)
    • View metadata
    • Copy Flickr tags
    • Remove
  • Ctrl-right-click (or some other shortcut) Middle-click to remove image
    • Note: Kivy doesn't provide an easy way to use keyboard + mouse events at the same time

Note: KivyMD's ContextMenu seems to be broken currently. Using kivy-garden.contextmenu instead.

Moving other items to issue #29

Caching & other performance optimizations

A number of methods are possible to improve performance and minimize disk I/O load on the iNaturalist API:

  • In main image selector screen, generate thumbnails to display for local images instead of keeping full image in memory
  • Cache thumbnails for local images
  • In taxon info screen, after downloading a taxon default photo, downscale if necessary (to around 400x400) and cache it for future use
  • Integrate cache for remote images with asynchronous image loading (kivy AsyncImage)
  • Cache iNaturalist API responses that are likely to be rerun multiple times (especially from GET /taxa)
  • Cache performance is currently getting slowed down by photos that can't be processed by Kivy before caching... Find a better workaround.

Add full-sized image viewer + carousel

Currently, only thumbnails are shown for local photos.

  • Left-click opens larger image view (load on-demand, keep in memory until tagged & removed)
  • On selecting image, use carousel and press/swipe left/right to cycle through images

Taxon info display

When a taxon is selected (either by ID or by future search features), the UI should display basic info about it, including:

  • Default image (if available)
  • Rank, name, common name
  • Link to taxon page on iNaturalist
  • Ancestors and direct children
  • Format this info in a layout that doesn't look terrible

Read taxon names from filenames

For example, if a file is named:

IMG_2020_02_02_Dryobates pubescens.jpg
  • Both the CLI and GUI could pull the taxon info from the filename.
  • This would probably be checked after tags, since tags would be more reliable
  • Tags would be applied if the taxon name returned one unambiguous search result
  • Otherwise show a selection prompt for top 10 results

Doesn't install correctly Mac OS

I tried to install naturtag today 2020-07-21 on Mac OS. with pip. There seems to be some version issue with pyinaturalist that I don't understand. naturtag wants pyinaturalist v. 0.10.0.dev206, but when I run naturtag at the command line I get the following error:
ImportError: cannot import name 'get_observation_species_counts' from 'pyinaturalist.node_api' (/Users/alan/opt/anaconda3/lib/python3.7/site-packages/pyinaturalist/node_api.py)

I looked at various versions and it seems that get_observation_species_counts is not defined in pyinaturalist v. 0.10.0.dev206 but is defined in v. 0.11.0. I'm not a python programmer, and I am relatively clueless, so not sure if this is a bug or user error.

Exception when entering username with KivMD ListItem + TextInput

Looks like a bug in handling the button state. Could be in either kivy or kivymd, but I need to do some more digging before reporting a bug.

This is failing on assert(self in touch.ud). If that's failing, that probably means that on_touch_down() somehow wasn't called before on_touch_up().

 Traceback (most recent call last):
   File "D:/Workspace/naturtag/naturtag/app/app.py", line 288, in <module>
     main()
   File "D:/Workspace/naturtag/naturtag/app/app.py", line 284, in main
     NaturtagApp().run()
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\app.py", line 950, in run
     runTouchApp()
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\base.py", line 573, in runTouchApp
     EventLoop.mainloop()
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\base.py", line 347, in mainloop
     self.idle()
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\base.py", line 391, in idle
     self.dispatch_input()
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\base.py", line 342, in dispatch_input
     post_dispatch_input(*pop(0))
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\base.py", line 308, in post_dispatch_input
     wid.dispatch('on_touch_up', me)
   File "kivy\_event.pyx", line 709, in kivy._event.EventDispatcher.dispatch
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivymd\uix\behaviors\ripplebehavior.py", line 245, in on_touch_up
     return super().on_touch_up(touch)
   File "D:\Workspace\naturtag\venv\lib\site-packages\kivy\uix\behaviors\button.py", line 164, in on_touch_up
     assert(self in touch.ud)
 AssertionError

Create models for API response objects

It will simplify a lot of common operations if API response JSON were to be wrapped with python models (dataclasses), likely using attrs.

  • Create base model
  • Create Observation model
  • Create Photo model
  • Create User model
  • Create Identification model

Asynchronous Taxon Loading

  • Loading taxon ancestors & children
  • Loading taxon history, frequent, and starred
  • Loading search results
  • Use kivy @mainthread for steps that need to communicate back to UI
  • Return incremental results back to UI (individual images, results, etc.)

Pre-generate atlas with CC-licensed thumbnails of most common taxa

This will speed up initial browsing of some of the most commonly observed taxa.

  • Calculate optimal atlas dimensions for provided images + constraints
    • Max size, max # of images per atlas chunk, max # of atlas chunks (pages? sub-atlases?)
  • Make script to recursively get children of iconic taxa (down 2 levels or so?)
  • Make script to get most commonly observed taxa
    • Regional or global?
  • Make script to generate thumbnails from selected taxa and package into an Atlas
  • Figure out better compression compatible with kivy Atlas?
  • Generate and package with Naturtag (if it's a reasonable size)

Image metadata display

The UI should provide a screen to view an image's metadata.

  • Categorized by format: EXIF , IPTC, XMP
  • Option to view all formats combined
  • Separate tab/section/whatever just for keywords
  • Initial version: Just dump raw text

Display basic info for selected observation

Observation view

When an observation is selected, the UI should display basic info about it, including:

  • Photos
  • Taxon: Rank, name, common name
  • Date/time: observed, uploaded
  • GPS: Lat/long, accuracy, geoprivacy, locale info (text only for now)
  • Description
  • Link to observation page on iNaturalist
    - [ ] Link to taxon screen
  • ID summary (total, # of agreements)

Persistent user settings & options

The UI should provide a Settings page with various options that will persist in a config file in ~/.local / %APPDATA%.

Metadata options

  • Toggle common names
  • Toggle hierarchical keywords
  • Toggle Darwin Core metadata
  • Create XMP files

iNaturalist options

  • iNaturalist username
  • Preferred locale?
  • pyinaturalist user_agent?

Display options

  • Toggle dark mode

Persistence

  • Define application dir
    • Using appdirs to determine user data dir
  • Choose config format
    • YAML?
  • Store settings in a config file in app dir
  • Load settings on startup
  • Set defaults if not config file exists
  • Add button on settings page to reset to defaults

API request error handling, rate-limiting, and batching

Moving this from #16. Getting occasional TooManyRequestsForUrl response from iNaturalist API when loading lots of taxon thumbnails. Mainly for children of the currently selected taxon (esp. when listing a large number of species under a genus)

  • Exception handling
  • Throttling for large requests
  • Batching for large requests (instead of several small requests for each taxon)
    • But also check API request cache for individual taxa before making batch request?
  • Delayed retries
  • Keep track of requests per minute and per hour to stay under limits
  • Fine-tuning for request caching? It works, but it's not very intelligent right now.

Asynchronous API calls

  • Pick a tool for async HTTP requests: grequests, aiohttp, twisted, tornado, ProcessPoolExecutor...
  • Make async calls for taxa
  • Make async calls for observations
  • Sync with status bar progress

Selectable taxonomy tree on taxon info display

If possible, the UI should display a taxonomy tree similar to the one shown in the 'Taxonomy' tab on iNaturalist.

  • Display all ancestors
  • Display scrollable list of direct children
  • Display the above as an indented tree
  • Make each node selectable, which will then display the info for that taxon

There are many possible performance optimizations for this, to avoid making an API call for every click. Pre-fetch and cache ancestor/child taxa records, pre-generate portions of the tree (could use taxon-keyword-gen?), etc. Will need to get further in implementation to determine what's feasible.

Taxon autocomplete search tab-completion

Follow-up from #4 and #20 . The taxon search completion is still a little clunky.

  • Format suggestion text to complete current input, e.g: Brown mar|morated stink bug
  • Display autocomplete suggestion text as the user types
  • Enable completion by pressing Tab (to complete search input, but not select the item yet)
  • Cycle through additional matches by pressing Tab again or with arrow keys
  • Asynchronously (re)load dropdown results
  • Draw dropdown box on top of other widgets instead of expanding to push them down (requires FloatLayout)
  • Make dropdown items less ugly... needs some formatting

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.