Coder Social home page Coder Social logo

cnngeometric_pytorch's Introduction

CNNGeometric PyTorch implementation

This is the implementation of the paper:

I. Rocco, R. Arandjelović and J. Sivic. Convolutional neural network architecture for geometric matching. [website][CVPR version][Extended TPAMI version]

Dependencies

See requirements.txt

Demo

Please see the demo.py script or the demo_notebook.ipynb Jupyter Notebook.

Training

You can train the model using the train.py script in the following way:

python train.py  --geometric-model affine

For a full set of options, run python train.py -h.

Logging Configuration
  • For now it is implemented to log on TensorBoard just scalars of train and val loss
  • It is possible to specify a --logdir as a parameter, otherwise the logging folder will be named as the checkpoint one with _tb_logs as suffix
  • N.B. If is intended to use as logdir a GCP bucket it is necessary to install Tensorflow

Evaluation

You can evaluate the trained models using the eval.py script in the following way:

python eval.py  --model-1 trained_models/best_streetview_checkpoint_adam_hom_grid_loss_PAMI.pth.tar --eval-dataset pf

You can also evaluate a two-stage model in the following way:

python eval.py --model-1 trained_models/best_streetview_checkpoint_adam_hom_grid_loss_PAMI.pth.tar --model-2 trained_models/best_streetview_checkpoint_adam_tps_grid_loss_PAMI.pth.tar --eval-dataset pf

The eval.py scripts implements the evaluation on the PF-Willow/PF-PASCAL/Caltech-101 and TSS datasets. For a full set of options, run python eval.py -h.

Trained models

Model PF-Willow (PCK)
[Affine - VGG - StreetView] 48.4
[Homography - VGG - StreetView] 48.6
[TPS - VGG - StreetView] 53.8

BibTeX

If you use this code in your project, please cite us using:

@InProceedings{Rocco17,
  author = {Rocco, I. and Arandjelovi\'c, R. and Sivic, J.},
  title  = {Convolutional neural network architecture for geometric matching},
  booktitle = {{Proceedings of the IEEE Conference on Computer Vision and Pattern Recognition}},
  year = {2017},
}

or

@Article{Rocco18,
  author = {Rocco, I. and Arandjelovi\'c, R. and Sivic, J.},
  title  = {Convolutional neural network architecture for geometric matching},
  journal = {{IEEE Transactions on Pattern Analysis and Machine Intelligence}},
  number = {41},
  pages = {2553--2567},
  year = {2018},
}

cnngeometric_pytorch's People

Contributors

ignacio-rocco 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  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

cnngeometric_pytorch's Issues

Training with affine AND TPS

I have looked at the training code but I don't understand how you're using the affine warped output for TPS to get the final result. The code implies that you can train TPS separately and affine separately, but don't you need to both?

Am I missing something?

Evaluation method about TSS dataset

hello, Dr Ignacio Rocco .
In your paper, you use your method on different datasets, and I'm a little confused about TSS datasets. I would like to ask you how PCK on this datasets is computed? In your paper, you describe it is calculated from the points in mask, but I'm still a little confused about the computing method? where are the points from? could you tell me? Thank you very much! I' looking forward your reply.

Attempting to test pretrained model on my own image data pairs - weird results

I modified the demo.py to allow a user to run inference on an image pair of their choosing:

from __future__ import print_function, division
import os
import argparse
import torch
import torch.nn as nn
from torch.utils.data import Dataset, DataLoader
from model.cnn_geometric_model import CNNGeometric
from data import pf_dataset
from data.download_datasets import download_PF_willow
from image.normalization import NormalizeImageDict, normalize_image
from util.torch_util import BatchTensorToVars, str_to_bool
from geotnf.transformation import GeometricTnf
from geotnf.point_tnf import *
import matplotlib.pyplot as plt
from skimage import io
from collections import OrderedDict
import cv2

# for compatibility with Python 2
try:
    input = raw_input
except NameError:
    pass

"""

Script to demonstrate evaluation on a trained model as presented in the CNNGeometric CVPR'17 paper
on the ProposalFlow dataset

"""

print('CNNGeometric PF demo script')

# Argument parsing
parser = argparse.ArgumentParser(description='CNNGeometric PyTorch implementation')
# Paths
parser.add_argument('--model-aff', type=str,
                    default='trained_models/best_pascal_checkpoint_adam_affine_grid_loss_resnet_random.pth.tar',
                    help='Trained affine model filename')
parser.add_argument('--model-tps', type=str,
                    default='trained_models/best_pascal_checkpoint_adam_tps_grid_loss_resnet_random.pth.tar',
                    help='Trained TPS model filename')
parser.add_argument('--feature-extraction-cnn', type=str, default='resnet101',
                    help='Feature extraction architecture: vgg/resnet101')
# parser.add_argument('--pf-path', type=str, default='datasets/PF-dataset', help='Path to PF dataset')
parser.add_argument('--pf-path', type=str, default='datasets/PF-dataset', help='Path to PF dataset')
parser.add_argument('imgpath_source', help='path to source image')
parser.add_argument('imgpath_target', help='path to the target image')

args = parser.parse_args()

use_cuda = torch.cuda.is_available()

do_aff = not args.model_aff == ''
do_tps = not args.model_tps == ''

# Download dataset if needed
download_PF_willow('datasets/')

# Create model
print('Creating CNN model...')
if do_aff:
    model_aff = CNNGeometric(use_cuda=use_cuda, geometric_model='affine',
                             feature_extraction_cnn=args.feature_extraction_cnn)
if do_tps:
    model_tps = CNNGeometric(use_cuda=use_cuda, geometric_model='tps',
                             feature_extraction_cnn=args.feature_extraction_cnn)

# Load trained weights
print('Loading trained model weights...')
if do_aff:
    checkpoint = torch.load(args.model_aff, map_location=lambda storage, loc: storage)
    checkpoint['state_dict'] = OrderedDict(
        [(k.replace('vgg', 'model'), v) for k, v in checkpoint['state_dict'].items()])
    model_aff.load_state_dict(checkpoint['state_dict'])
if do_tps:
    checkpoint = torch.load(args.model_tps, map_location=lambda storage, loc: storage)
    checkpoint['state_dict'] = OrderedDict(
        [(k.replace('vgg', 'model'), v) for k, v in checkpoint['state_dict'].items()])
    model_tps.load_state_dict(checkpoint['state_dict'])

# Dataset and dataloader
# dataset = PFDataset(csv_file=os.path.join(args.pf_path, 'test_pairs_pf.csv'),
#                     training_image_path=args.pf_path,
#                     transform=NormalizeImageDict(['source_image', 'target_image']))
# dataloader = DataLoader(dataset, batch_size=1,
#                         shuffle=True, num_workers=4)
batchTensorToVars = BatchTensorToVars(use_cuda=use_cuda)

affineTnf = GeometricTnf(out_h=240, out_w=240, use_cuda = False)


def get_image(img_path):
    # img_name = os.path.join(self.training_image_path, img_name_list[idx])
    image = io.imread(img_path)

    # get image size
    im_size = np.asarray(image.shape)

    # convert to torch Variable
    image = np.expand_dims(image.transpose((2, 0, 1)), 0)
    image = torch.Tensor(image.astype(np.float32))
    image_var = Variable(image, requires_grad=False)

    # Resize image using bilinear sampling with identity affine tnf
    # image = affineTnf(image_var).data.squeeze(0)
    image = affineTnf(image_var).data


    im_size = torch.Tensor(im_size.astype(np.float32))

    return (image, im_size)

# Instantiate point transformer
pt = PointTnf(use_cuda=use_cuda)

# Instatiate image transformers
tpsTnf = GeometricTnf(geometric_model='tps', use_cuda=use_cuda)
affTnf = GeometricTnf(geometric_model='affine', use_cuda=use_cuda)


IMPATH_SOURCE = args.imgpath_source
IMPATH_TARGET = args.imgpath_target

src_image, src_im_size = get_image(IMPATH_SOURCE)
tgt_image, tgt_im_size = get_image(IMPATH_TARGET)

src_image = normalize_image(src_image, forward=True)
tgt_image = normalize_image(tgt_image, forward=True)

batch = {'source_image': src_image,
         'target_image': tgt_image,
         'source_im_size': src_im_size,
         'target_im_size': tgt_im_size}

batch = batchTensorToVars(batch)


source_im_size = batch['source_im_size']
target_im_size = batch['target_im_size']


# these are not needed, right?
# source_points = batch['source_points']
# target_points = batch['target_points']

# warp points with estimated transformations
# target_points_norm = PointsToUnitCoords(target_points, target_im_size)

if do_aff:
    model_aff.eval()
if do_tps:
    model_tps.eval()

# Evaluate models
if do_aff:
    theta_aff = model_aff(batch)
    warped_image_aff = affTnf(batch['source_image'], theta_aff.view(-1, 2, 3))

if do_tps:
    theta_tps = model_tps(batch)
    warped_image_tps = tpsTnf(batch['source_image'], theta_tps)

if do_aff and do_tps:
    theta_aff_tps = model_tps({'source_image': warped_image_aff, 'target_image': batch['target_image']})
    warped_image_aff_tps = tpsTnf(warped_image_aff, theta_aff_tps)

# Un-normalize images and convert to numpy
source_image = normalize_image(batch['source_image'], forward=False)
source_image = source_image.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()
target_image = normalize_image(batch['target_image'], forward=False)
target_image = target_image.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()

if do_aff:
    warped_image_aff = normalize_image(warped_image_aff, forward=False)
    warped_image_aff = warped_image_aff.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()

if do_tps:
    warped_image_tps = normalize_image(warped_image_tps, forward=False)
    warped_image_tps = warped_image_tps.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()

if do_aff and do_tps:
    warped_image_aff_tps = normalize_image(warped_image_aff_tps, forward=False)
    warped_image_aff_tps = warped_image_aff_tps.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()

# check if display is available
exit_val = os.system('python -c "import matplotlib.pyplot as plt;plt.figure()"  > /dev/null 2>&1')
display_avail = exit_val == 0
# display_avail = False

if display_avail:
    N_subplots = 2 + int(do_aff) + int(do_tps) + int(do_aff and do_tps)
    fig, axs = plt.subplots(1, N_subplots)
    # N.B. I had to recast all of these to np.uint8s because it was failing otherwise for some reason...
    axs[0].imshow(np.uint8(source_image))
    axs[0].set_title('src')
    axs[1].imshow(np.uint8(target_image))
    axs[1].set_title('tgt')
    subplot_idx = 2
    if do_aff:
        axs[subplot_idx].imshow(np.uint8(warped_image_aff))
        axs[subplot_idx].set_title('aff')
        subplot_idx += 1
    if do_tps:
        axs[subplot_idx].imshow(np.uint8(warped_image_tps))
        axs[subplot_idx].set_title('tps')
        subplot_idx += 1
    if do_aff and do_tps:
        axs[subplot_idx].imshow(np.uint8(warped_image_aff_tps))
        axs[subplot_idx].set_title('aff+tps')

    for i in range(N_subplots):
        axs[i].axis('off')
    print('Showing results. Close figure window to continue')
    plt.show()
else:
    print('No display found. Writing results to:')
    fn_src = 'source.png'
    print(fn_src)
    io.imsave(fn_src, source_image)
    fn_tgt = 'target.png'
    print(fn_tgt)
    io.imsave(fn_tgt, target_image)
    if do_aff:
        fn_aff = 'result_aff.png'
        print(fn_aff)
        io.imsave(fn_aff, warped_image_aff)
    if do_tps:
        fn_tps = 'result_tps.png'
        print(fn_tps)
        io.imsave(fn_tps, warped_image_tps)
    if do_aff and do_tps:
        fn_aff_tps = 'result_aff_tps.png'
        print(fn_aff_tps)
        io.imsave(fn_aff_tps, warped_image_aff_tps)

Which the user can invoke as follows:

$ python demo2.py /path/to/source/image.jpg /path/to/target/image.jpg

But I'm getting weird results. Such as even for a provided input of identical source and target images, it predicts some weird transform.

Perhaps I've messed up the modifications in some way...

cnn_geometric_matching

datasets

I want to know that in the train.csv file what the meaning that A22,A21,A12,A11 respectively represent?

Could not find trained_models!

Dear @ignacio-rocco,

The results are pretty amazing!.

I would like to run the demo on my own. So I cloned the code and in the source code, we need to read the file 'best_pascal_checkpoint_adam_affine_grid_loss_resnet_random.pth.tar' in the trained_models folder.

However, in readme file, there are no links to download: ### best_pascal_checkpoint_adam_affine_grid_loss_resnet_random

I only could find:

[Affine - VGG - StreetView] |
[Homography - VGG - StreetView] |
[TPS - VGG - StreetView]

Where are the pretrained with pascal?

Vy

Confusion about revert points

Hi bro,
I found that in get dataset, you set up format code is point = X, Y
But in the point_tnf.py:
Your code is:

def PointsToUnitCoords(P,im_size):
h,w = im_size[:,0],im_size[:,1]
P_norm = P.clone()
# normalize Y
P_norm[:,0,:] = normalize_axis(P[:,0,:],w.unsqueeze(1).expand_as(P[:,0,:]))
# normalize X
P_norm[:,1,:] = normalize_axis(P[:,1,:],h.unsqueeze(1).expand_as(P[:,1,:]))
return P_norm

So, i have a quesion is that; P_norm[:,0,:] is x-axis and P_norm[:,1,:] is y-axis ?

performance mismatch : newly trained model for 'tps'

Hi,

Thanks for sharing the code!

I would like to get a clarification regarding the performance mismatch during testing(eval_pf.py).
When tested with models from github, the performance is as below:

PCK affine: 0.4584
PCK tps: 0.5157
PCK affine+tps: 0.5702

When tested with models created using new training run (10 epochs), the performance is as below;

PCK affine: 0.4584
PCK tps: 0.4215
PCK affine+tps: 0.5634

I am not sure what is the reason for decrease in performance of 'tps'.
Am I missing some configurations?

caculate the pck

can you tell me why you warp target_point rather than source_point
image

about test_pairs_pf.csv

hi, when i run the demo.py, i can't download the test_pairs_pf. csv. So where can i get the test_pairs_pf.csv? Thanks a lot

Does the demo code runs on windows? + some questions about how to train in a new dataset

Hi, Thank you for sharing the code.

I would like to try your code on my machine. However, I am having problems trying to install pytorch in my windows PC. Actually, conda only has pytorch 0.1.12, not 0.2.0, torchvision as you said it was required. Any advice for me about this.

I am highly interesed on apply machine learning and cnn to solve traditional pattern recognition problems which were solved using generalized hough transform, ranscac and others. If the input image and dataset are easier samples such as brand logos in whiter or black background but with a little bit of noise for example, do you think the accuracy will be higher than the results obtained in your papers using images of clustered environments?

What will be the best way to create the annotations of the transformation matrix (train and test csv files) feature points (test_pairs_pf csv file) for a new dataset?

Also, it will be nice to see this code running on tensorflow. Do you think it will be possible to convert this code into tensorflow and get the same accuracy?

Thanks in advance.

Update:
I asked directly to pytorch support and they replied really fast and helped me to install everything ok in windows and finally demo code runs without problem. I hope you still can reply other questions.

PCK mismatch for ResNet

Hi!
Thanks for the source code!

I'm trying to train the model with ResNet101 feature extractor and affine transformations. I'd like to get the same PCK as the model mentioned in commit f24a83a has.

I use the following command:
python3 train.py --num-epochs=20 --feature-extraction-cnn=resnet101 --random-sample=True --trained-models-dir=trained_models/

But the max PCK that I managed to achieve is 0.5388 (vs 0.559 that your model gives).

What am I doing wrong and how can I obtain the same results as your model gives?

TypeError on CNNGeometric

Hi, I'm interested in your works.

When I cloned this git, and run "python demo.py".
I got this error "TypeError: init() got an unexpected keyword argument 'geometric_model'"

in the "cnn_geometric_model.py", I found the class "CNNGeometric". but there is no argument 'geometric_model'.

any suggession?

as_matrix problem

Hi, I'm working on your previous code because of the same problem with
'#19 (comment)'

But I have problem with this issue
Traceback (most recent call last): File "demo.py", line 111, in <module> theta_aff = model_aff(batch) File "/home/youngj/anaconda3/lib/python3.7/site-packages/torch/nn/modules/module.py", line 532, in __call__ result = self.forward(*input, **kwargs) File "/home/youngj/Desktop/cnngeometric_pytorch-4b9a5b7eedce503199a03147de2115ffe0444888/model/cnn_geometric_model.py", line 156, in forward theta = self.FeatureRegression(correlation) File "/home/youngj/anaconda3/lib/python3.7/site-packages/torch/nn/modules/module.py", line 532, in __call__ result = self.forward(*input, **kwargs) File "/home/youngj/Desktop/cnngeometric_pytorch-4b9a5b7eedce503199a03147de2115ffe0444888/model/cnn_geometric_model.py", line 110, in forward x = x.view(x.size(0), -1) RuntimeError: view size is not compatible with input tensor's size and stride (at least one dimension spans across two contiguous subspaces). Use .reshape(...) instead.

I think this is because I change as_matrix to to_numpy and values because as_matrix has been deprecated.
If you know solution of this problem, plz reply

FileNotFoundError trained_models/***.pth.tar

Creating CNN model...
Loading trained model weights...

FileNotFoundError Traceback (most recent call last)
in ()
13 print('Loading trained model weights...')
14 if do_aff:
---> 15 checkpoint = torch.load(model_aff_path, map_location=lambda storage, loc: storage)
16 checkpoint['state_dict'] = OrderedDict([(k.replace('vgg', 'model'), v) for k, v in checkpoint['state_dict'].items()])
17 model_aff.load_state_dict(checkpoint['state_dict'])

~/anaconda3/lib/python3.6/site-packages/torch/serialization.py in load(f, map_location, pickle_module)
257 (sys.version_info[0] == 3 and isinstance(f, pathlib.Path)):
258 new_fd = True
--> 259 f = open(f, 'rb')
260 try:
261 return _load(f, map_location, pickle_module)

FileNotFoundError: [Errno 2] No such file or directory: 'trained_models/best_pascal_checkpoint_adam_affine_grid_loss_resnet_random.pth.tar'

Different demo result

I ran the demo script to test the provided "Affine+VGG+StreetView" model on the demo duck image pair, but the result affine image is not as good as it is in the ReadMe. Could anyone tell me what is the problem? Thanks a lot!

I simplified the demo script to the following:

from __future__ import print_function, division
import os
import argparse
from torch.utils.data import DataLoader
from model.cnn_geometric_model import CNNGeometric
from data.pf_dataset import PFDataset
from image.normalization import NormalizeImageDict, normalize_image
from util.torch_util import BatchTensorToVars
from geotnf.transformation import GeometricTnf
from collections import OrderedDict
import cv2

use_cuda = True
model_aff = CNNGeometric(use_cuda=use_cuda, feature_extraction_cnn='vgg')
checkpoint = torch.load('trained_models/best_streetview_checkpoint_adam_affine_grid_loss.pth.tar',
                        map_location=lambda storage, loc: storage)
checkpoint['state_dict'] = OrderedDict([(k.replace('vgg', 'model'), v) for k, v in checkpoint['state_dict'].items()])
model_aff.load_state_dict(checkpoint['state_dict'])

dataset = PFDataset(csv_file='datasets/PF-dataset/test_pairs_pf.csv', dataset_path='datasets',
                    transform=NormalizeImageDict(['source_image', 'target_image']))
dataloader = DataLoader(dataset, batch_size=1, shuffle=True, num_workers=4)
batchTensorToVars = BatchTensorToVars(use_cuda=use_cuda)
affTnf = GeometricTnf(geometric_model='affine', use_cuda=use_cuda)

for i, batch in enumerate(dataloader):
    batch = batchTensorToVars(batch)
    model_aff.eval()
    theta_aff = model_aff(batch)
    warped_image_aff = affTnf(batch['source_image'], theta_aff.view(-1, 2, 3))

    source_image = normalize_image(batch['source_image'], forward=False)
    source_image = source_image.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()
    target_image = normalize_image(batch['target_image'], forward=False)
    target_image = target_image.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()
    warped_image_aff = normalize_image(warped_image_aff, forward=False)
    warped_image_aff = warped_image_aff.data.squeeze(0).transpose(0, 1).transpose(1, 2).cpu().numpy()

    source_image = cv2.cvtColor(source_image, cv2.COLOR_RGB2BGR)
    target_image = cv2.cvtColor(target_image, cv2.COLOR_RGB2BGR)
    warped_image_aff = cv2.cvtColor(warped_image_aff, cv2.COLOR_RGB2BGR)

    cv2.imwrite('res_img/'+str(i)+'_source.png', (255*source_image).astype(np.uint8))
    cv2.imwrite('res_img/'+str(i)+'_target.png', (255*target_image).astype(np.uint8))
    cv2.imwrite('res_img/'+str(i)+'_result_aff.png', (255*warped_image_aff).astype(np.uint8))

    res = input('Run for another example ([y]/n): ')
    if res == 'n':
        break

The source, target and affined image respectively are:
0_source
0_target
0_result_aff

How to visualize the feature point pairs' matching results ?

Hello, I have read your both your papers CNNGeometric and WeakAlign, very good works and thanks for the code!

When I tried the code, I want to check the feature points' matching results (like Inliers and Outliers matching in Fig. 1 in WeakAlign), but I have not found this function. Could you tell me how to do it . Sorry to bother you and thanks in advance.

Weights for the feature extractors?

Hi! Thanks for publishing the code so quickly 👍

The pretrained models include weights only for the regressor, right? I'm assuming that one also needs to train the feature extractor to get better results. Is this the case, or have you found that training the extractor is not that relevant?

cheers

Regarding datasets

@ignacio-rocco Hi,

I am trying to train the proposed network, I am having difficulty with the dataset.
The Pascal dataset is now not available, so I am training with the Caltech101 dataset.
It requires CSV files with image names and annotation files.
Moreover, if i use a synthetic dataset, it also requires a CSV file with images and transformation,
Where can I get it?
can you help me in this regard?
Thank you

Some question about the model

  1. Can you plot the model as a pdf document ?
  2. In my match model the Xception works better than the ResNet-101.
  3. At last Thanks for your share.

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.