Giter Site home page Giter Site logo

lynnesbian / ocrbot Goto Github PK

View Code? Open in Web Editor NEW
67.0 3.0 10.0 221 KB

An OCR (Optical Character Recognition) bot for Mastodon (and compatible) instances

Home Page: https://fedi.lynnesbian.space/@OCRbot

License: GNU Affero General Public License v3.0

Python 100.00%
mastodon tesseract ocr python python3

ocrbot's Introduction

OCRbot

OCRbot is a Mastodon (and compatible, such as Pleroma) bot that uses OCR to automatically generate text descriptions of images. It reads the image and outputs what it thinks is the text contained, using tesseract. It requires Python 3.

Screenshot of OCRbot in action

Working Example

A working version of OCRbot can be found here. I'll always run the latest version, and the instance hosting it has a much higher character limit than Mastodon's default of 500 or Pleroma's default of 5000 (specifically, it has a limit of 65535 chars). If you'd like to run your own version that works differently (for example, maybe you want a version that works with Japanese text instead, or a version that uses a different OCR engine), then you're free to fork and modify!

Installation

Tesseract (Required)

  • Debian/Ubuntu: sudo apt install tesseract-ocr
  • Arch Linux: sudo pacman -Syu tesseract tesseract-data-eng

What about the higher quality data packs?

According to tesseract's GitHub wiki:

Most users will want tessdata_fast and that is what will be shipped as part of Linux distributions. tessdata_best is for people willing to trade a lot of speed for slightly better accuracy.

With the setup I'm using, I really can't afford to use much more processing power just for "slightly better accuracy". Additionally, most Linux distributions only ship the fast set, which makes obtaining the best set an extra step. I haven't tested how much better fast is than best, but given that distributions like Arch and Debian deemed fast good enough to be the only package available, I'm inclinded to suspect that this is a case of diminishing returns, and that it's not worth the extra effort of obtaining these files, as well as the extra CPU usage required to use them.

OCRbot (Required)

git clone https://github.com/Lynnesbian/OCRbot/
cd OCRbot
pip3 install -r requirements.txt

Running OCRbot

Initial Setup

Run login.py, and answer the interactive prompts. To further configure OCRbot, open config.json and edit the settings as you wish. Here is an explanation of the options in the config file:

Setting Meaning Example
site The instance your bot will log in to and post from. https://botsin.space
cw The content warning (aka subject) OCRbot will apply to non-error posts. OCR output
ocr_threads The amount of CPU threads to use for running tesseract. 8
char_limit The maximum length of an OCRbot post. This limit does not apply to errors. I recommend setting it to your instance's character limit. 500
default_language The language you'd like OCRbot to default to. eng
admin The account you'd like OCRbot to tell users to report errors to. If you format it as [email protected] (omitting the leading @ sign), it won't automatically tag you. This ensures your notifications don't get flooded if the bot breaks. [email protected]

Most importantly, make sure you choose the instance your OCRbot account will post from. Then, run login.py and log in with the account you'd like OCRbot to post from. Finally, run service.py and OCRbot will take care of the rest.

Running OCRbot as a Service

You can use something like systemd or SysVinit to manage running service.py for you. I've provided an example systemd script here. It requires some editing (to specify where your OCRbot folder is located, and to specify the user to run it as -- don't run it as root), and then you simply need to

sudo mv systemd-example.service /etc/systemd/system/OCRbot.service
sudo systemctl start OCRbot

You may also sudo systemctl enable OCRbot if you want it to start on boot.

Caveats

OCRbot is not designed to replace captions, merely to supplement them. It works best with plain black text on a plain white background. For example, PDF scans and Wikipedia screenshots will work great, but handwriting and distorted text won't. It will almost always fail (at least partially) for:

  • Anything using handwriting, especially cursive
  • Text that is skewed, rotated, stretched, or otherwise distorted
  • Heavily compressed images
  • Text with layouts more complex than this page
  • WordArt
  • Unusual fonts
  • Videos and GIFs
  • Very large or very small text
  • Text on a complex background
  • Vertical text in a language where text is usually written horizontally, and vice versa

Additionally, OCRbot may sometimes fail to find images due to federation issues. This is a known issue, and OCRbot does its best to work around it, but due to the way federation works, it will never be completely solved.

If you can caption images yourself, I recommend doing so. If, for example, you are posting a screenshot of a tweet, you'll get better results from copy and pasting the text of the tweet as a caption rather than relying on OCRbot. OCRbot's image recognition is far from perfect and never will be.

Support for Other Languages

OCRbot is capable of supporting languages other than English. If you are using Debian, you can install the package tesseract-ocr-all to install all language files, or install individual ones, such as tesseract-ocr-jpn for Japanese. Arch also provides these packages, with slightly different names (e.g. tesseract-data-jpn), although it does not have an "all" package.

Errors

While invoking OCRbot, you may encounter a number of errors. While the text is somewhat self explanatory, this section exists to provide further elaboration on their meaning.

Error message Explanation
Couldn't find any media. OCRbot was unable to find any posts that had images attached, usually because your post or the one you're replying to doesn't have any images. It could also happen if the images have failed to federate properly.
Failed to find post containing image. This may be a federation issue, or you may have tagged OCRbot in a conversation without an image. Please note that OCRbot can only see images in the post you are directly replying to, and can't see images that are provided as URLs rather than attachments. (see above)
Specified post has no images. OCRbot successfully loaded the post, and was unable to find any images. This means that your post or the one you're replying to definitely doesn't have any images.
Couldn't read the specified image(s), sorry! OCRbot works best with plain black text on a plain white background. Here is some information about what it can and can't do: https://github.com/Lynnesbian/OCRbot/blob/master/README.md#caveats OCRbot couldn't read your image. In the same way that you would have trouble reading a blurry photo of text, OCRbot has trouble with things such as cursive handwriting, rotated text, strange fonts, and more. Check the caveats section on this page for more information.
Tesseract returned no text. Either your image contains no text, or tesseract (the program OCRbot relies on) encountered an error and returned nothing.
Failed to read image. Download may have timed out. If any one image takes longer than 30 seconds to download, OCRbot gives up on it. This can occur because a server is down, experiencing slowdown, or most likely, because wasabi is down.
Failed to run tesseract. Specified language was: [language] Tesseract, the program OCRbot relies on to provide captions, crashed.

Donating

Please don't feel obligated to donate at all. Donations can be provided via PayPal or Ko-fi. Don't feel obligated to donate!

License

Copyright (C)2019 Lynnesbian (https://fedi.lynnesbian.space/@lynnesbian)

This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.

The full text of the license is provided under the name "LICENSE" in the root directory of this repository.

ocrbot's People

Contributors

benlubar avatar dependabot-preview[bot] avatar lynnesbian avatar moggers87 avatar roxxers 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

ocrbot's Issues

add a parameter for the length of the tut, so that the text is split into parts

so if my instance support 500 char I would use the parameter 500 and I could then copy part by part to my tut.

alternatively you could have a parameter that would put a number at the line that the paramater value reaches the text. so if @ocrbot taglenght 200 the text would be like this

"transcribed text 200 char"
200 - "transcribed text more 200"
400 - "transcribed text more 200"

Clean up the output

OCRbot currently produces rather messy output, which is to be expected from an automated OCR program, but we can clean it up a little!

  • Don't post "Image 1:" if there's only one image
  • Don't post more than two line breaks at a time
  • Make sure that it never just posts a blank reply with nothing in it

Fix people accidentally invoking OCRbot

One possible solution would be to ignore any mentions that don't come first - for example, respond to
@ OCRbot @ userA
but ignore
@ userA @ OCRbot

This solves the problem of people not realising why OCRbot ignored them when mentioned, but also means that more than one person can ask OCRbot to caption the same post.

Support for other languages

Right now, OCRbot only works with English. It'd be really great for it to support other languages too! This could be done either by

  • Specifying the language you want when mentioning OCRbot (easy)
  • Detecting the language of the text (hard)

Implement character limit

Right now, the character limit in config.json is ignored. It should be implemented, and output longer than the limit should be split over multiple posts.

Problem while running "python3 service.py"

Hi there Lynnesbian and other people willing to help or who have encountered the same problem as I.

While running the command "python3 service.py", I have the following warning.

This was run on an ubuntu 18.04 machine.

image

I thought it might be because of the ubuntu 18, so I tried again on debian 10.

image

And nope, still the same problem.

My first time setting the bot upwas successful though, everything worked like a charm. It was shortly after the v3.3.0 release I think, and it was on an ubuntu 20.04.

I wonder what changed that this time it doesn't work and what could have caused the problem.

Have a nice day people.

Reply to the original post

As someone who often calls the OCRbot on other people's posts when they forget alt text, I'd like to be able to do so without polluting my own timeline.

I'd like to delete my post after OCRbot replies to it, but I can't because that breaks threads. It would be great if the bot could reply to the original message (and mention me as before).

login.py fails with Plerom after secret is entered

After copying the secret over from Pleroma to command line where I started login.py I get this error:
Traceback (most recent call last):

  File "/home/utzer/.local/lib/python3.10/site-packages/mastodon/Mastodon.py", line 525, in log_in
    response = self.__api_request('POST', '/oauth/token', params, do_ratelimiting=False)
  File "/home/utzer/.local/lib/python3.10/site-packages/mastodon/Mastodon.py", line 3425, in __api_request
    raise ex_type(
mastodon.Mastodon.MastodonAPIError: ('Mastodon API returned error', 400, 'Bad Request', 'Invalid credentials')

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/utzer/ocrbot/login.py", line 66, in <module>
    cfg['secret'] = client.log_in(code=input("Secret: "), scopes=scopes)
  File "/home/utzer/.local/lib/python3.10/site-packages/mastodon/Mastodon.py", line 533, in log_in
    raise MastodonIllegalArgumentError('Invalid access token or redirect_uris: %s' % e)
mastodon.Mastodon.MastodonIllegalArgumentError: Invalid access token or redirect_uris: ('Mastodon API returned error', 400, 'Bad Request', 'Invalid credentials')

Wondering if there is any tip why the connection fails.

Read DMs

Allow OCRbot to read images from DMs so people can DM it an image they want captioned!

OCRbot broken after Mastodon 3.4.0 upgrade

I had an instance of OCR bot running and then upgraded my Mastodon instance from 3.3.0 to 3.4.0. OCRbot immediately stopped working. The Mastodon instance works fine, and other bots that do things like post RSS feeds are working fine. Here is the error message I get:

$ /home/ocrbot/ocrbot/OCRbot/service.py
Logging in...
Using Tesseract (sh)
Available languages: eng
Starting OCRbot.
Traceback (most recent call last):
File "/home/ocrbot/ocrbot/OCRbot/service.py", line 314, in
client.stream_user(rl) #go!
File "/home/ocrbot/.local/lib/python3.8/site-packages/decorator.py", line 232, in fun
return caller(func, *(extras + args), **kw)
File "/home/ocrbot/.local/lib/python3.8/site-packages/mastodon/Mastodon.py", line 102, in wrapper
return function(self, *args, **kwargs)
File "/home/ocrbot/.local/lib/python3.8/site-packages/mastodon/Mastodon.py", line 3110, in stream_user
return self.__stream('/api/v1/streaming/user', listener, run_async=run_async, timeout=timeout, reconnect_async=reconnect_async, reconnect_async_wait_sec=reconnect_async_wait_sec)
File "/home/ocrbot/.local/lib/python3.8/site-packages/mastodon/Mastodon.py", line 3626, in __stream
connection = connect_func()
File "/home/ocrbot/.local/lib/python3.8/site-packages/mastodon/Mastodon.py", line 3553, in connect_func
raise MastodonNetworkError("Could not connect to streaming server: %s" % connection.reason)
mastodon.Mastodon.MastodonNetworkError: Could not connect to streaming server: Internal Server Error

Switch to PyOCR

PyOCR is a lot nicer to use than pytesseract, and it seems to support a lot more functionality.

OCR bot starts clogging threads with his error message pics

For some reasons this happens quite often. For me, calling the bot from Friendica, it does not work any more. It only did one day, when I discovred it. A simple one line text error message with a link to further explanations would be less annoying and clogging. I'm thinking about to block this bot. On the other hand, I find it quite useful, if it works. So please look at some threads and solve the problems.

Excuse me for opening a new issue. I cannot judge, whether this issue is related to others here.

Fix error messages

Right now, OCRbot doesn't actually post error messages. This needs to be fixed

Support for translations

OCRbot's error messages and content warnings are currently always served in English. As OCRbot supports more languages than just English, there should be translations of the messages available.

Improve OCR accuracy

  • i found a cool project that can fix the perspective of input text. this means that if the whole image is tilted in the same way, we can fix it to be readable. https://mzucker.github.io/2016/10/11/unprojecting-text-with-ellipses.html
  • i found another cool project by the same person that can de-curve an image and make it nice and flat. https://mzucker.github.io/2016/08/15/page-dewarping.html
  • the default binarisation (converting a color image to black and white, as in, 100% black or 100% white, no grey) engine is kinda crappy, but we can use openCV to do a better job than the built-in one.
  • tesseract trips up when the text is riiight at the edge without a border. this is easily fixed by adding a 10px border around the input.

OCRbot shouldn't mention people

If you ask OCRbot to transcribe an image that contains, for example, a mstdn-ebooks bot handle, it will mention the bot, and then the bot will reply, and it will reply, and the bot will reply...

You can also use this to get around blocks.

This can be fixed by inserting a ZWS after any @s, making it impossible to create a string like @user.

Timeouts

Hello,

Many thanks for your work on this useful project.
I'm getting timeouts when I try to run reply.py:

Apr 08 18:43:01 ocr-bot01 systemd[1]: Started OCRbot reply manager.
Apr 08 18:43:02 ocr-bot01 python3[367]: Logging in...
Apr 08 18:43:02 ocr-bot01 python3[367]: Using Tesseract (sh)
Apr 08 18:43:02 ocr-bot01 python3[367]: Available languages: eng, fra
Apr 08 18:43:02 ocr-bot01 python3[367]: Starting OCRbot.
Apr 08 18:48:02 ocr-bot01 python3[367]: Traceback (most recent call last):
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 387, in _make_request
Apr 08 18:48:02 ocr-bot01 python3[367]:     six.raise_from(e, None)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "<string>", line 3, in raise_from
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 383, in _make_request
Apr 08 18:48:02 ocr-bot01 python3[367]:     httplib_response = conn.getresponse()
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/http/client.py", line 1331, in getresponse
Apr 08 18:48:02 ocr-bot01 python3[367]:     response.begin()
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/http/client.py", line 297, in begin
Apr 08 18:48:02 ocr-bot01 python3[367]:     version, status, reason = self._read_status()
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/http/client.py", line 258, in _read_status
Apr 08 18:48:02 ocr-bot01 python3[367]:     line = str(self.fp.readline(_MAXLINE + 1), "iso-8859-1")
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/socket.py", line 586, in readinto
Apr 08 18:48:02 ocr-bot01 python3[367]:     return self._sock.recv_into(b)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/ssl.py", line 1012, in recv_into
Apr 08 18:48:02 ocr-bot01 python3[367]:     return self.read(nbytes, buffer)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/ssl.py", line 874, in read
Apr 08 18:48:02 ocr-bot01 python3[367]:     return self._sslobj.read(len, buffer)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3.6/ssl.py", line 631, in read
Apr 08 18:48:02 ocr-bot01 python3[367]:     v = self._sslobj.read(len, buffer)
Apr 08 18:48:02 ocr-bot01 python3[367]: socket.timeout: The read operation timed out
Apr 08 18:48:02 ocr-bot01 python3[367]: During handling of the above exception, another exception occurred:
Apr 08 18:48:02 ocr-bot01 python3[367]: Traceback (most recent call last):
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/requests/adapters.py", line 449, in send
Apr 08 18:48:02 ocr-bot01 python3[367]:     timeout=timeout
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 639, in urlopen
Apr 08 18:48:02 ocr-bot01 python3[367]:     _stacktrace=sys.exc_info()[2])
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/util/retry.py", line 357, in increment
Apr 08 18:48:02 ocr-bot01 python3[367]:     raise six.reraise(type(error), error, _stacktrace)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/six.py", line 693, in reraise
Apr 08 18:48:02 ocr-bot01 python3[367]:     raise value
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 601, in urlopen
Apr 08 18:48:02 ocr-bot01 python3[367]:     chunked=chunked)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 389, in _make_request
Apr 08 18:48:02 ocr-bot01 python3[367]:     self._raise_timeout(err=e, url=url, timeout_value=read_timeout)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/lib/python3/dist-packages/urllib3/connectionpool.py", line 309, in _raise_timeout
Apr 08 18:48:02 ocr-bot01 python3[367]:     raise ReadTimeoutError(self, url, "Read timed out. (read timeout=%s)" % timeout_value)
Apr 08 18:48:02 ocr-bot01 python3[367]: urllib3.exceptions.ReadTimeoutError: HTTPSConnectionPool(host='social.mental.af', port=443): Read timed out. (read timeout=300)
Apr 08 18:48:02 ocr-bot01 python3[367]: During handling of the above exception, another exception occurred:
Apr 08 18:48:02 ocr-bot01 python3[367]: Traceback (most recent call last):
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "reply.py", line 216, in <module>
Apr 08 18:48:02 ocr-bot01 python3[367]:     client.stream_user(rl) #go!
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "</usr/local/lib/python3.6/dist-packages/decorator.py:decorator-gen-83>", line 2, in stream_user
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/mastodon/Mastodon.py", line 77, in wrapper
Apr 08 18:48:02 ocr-bot01 python3[367]:     return function(self, *args, **kwargs)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/mastodon/Mastodon.py", line 1987, in stream_user
Apr 08 18:48:02 ocr-bot01 python3[367]:     return self.__stream('/api/v1/streaming/user', listener, run_async=run_async, timeout=timeout, reconnect_async=reconnect_async, reconnect_async_wait_sec=reconnect_async_wait_sec)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/mastodon/Mastodon.py", line 2324, in __stream
Apr 08 18:48:02 ocr-bot01 python3[367]:     connection = connect_func()
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/mastodon/Mastodon.py", line 2319, in connect_func
Apr 08 18:48:02 ocr-bot01 python3[367]:     timeout=(self.request_timeout, timeout))
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 546, in get
Apr 08 18:48:02 ocr-bot01 python3[367]:     return self.request('GET', url, **kwargs)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 533, in request
Apr 08 18:48:02 ocr-bot01 python3[367]:     resp = self.send(prep, **send_kwargs)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/requests/sessions.py", line 646, in send
Apr 08 18:48:02 ocr-bot01 python3[367]:     r = adapter.send(request, **kwargs)
Apr 08 18:48:02 ocr-bot01 python3[367]:   File "/usr/local/lib/python3.6/dist-packages/requests/adapters.py", line 529, in send
Apr 08 18:48:02 ocr-bot01 python3[367]:     raise ReadTimeout(e, request=request)
Apr 08 18:48:02 ocr-bot01 python3[367]: requests.exceptions.ReadTimeout: HTTPSConnectionPool(host='social.mental.af', port=443): Read timed out. (read timeout=300)
Apr 08 18:48:02 ocr-bot01 systemd[1]: ocr-bot.service: Main process exited, code=exited, status=1/FAILURE
Apr 08 18:48:02 ocr-bot01 systemd[1]: ocr-bot.service: Failed with result 'exit-code'.

I don't understand why those timeouts appears, do you have any clue?

AI image recognition/description support

Maybe you could use some artificial intelligence to detect non-text images and describe them.

Problems:

  • sometimes makes mistakes, but I guess you cannot prevent this
  • you likely need to use a third-party service, I guess(?)

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.