Git Product home page Git Product logo

image's Introduction

Image

crates.io Documentation Build Status Gitter

Maintainers: @HeroicKatora, @fintelia

How to contribute

An Image Processing Library

This crate provides basic image processing functions and methods for converting to and from various image formats.

All image processing functions provided operate on types that implement the GenericImageView and GenericImage traits and return an ImageBuffer.

High level API

Load images using [io::Reader]:

use std::io::Cursor;
use image::io::Reader as ImageReader;

let img = ImageReader::open("myimage.png")?.decode()?;
let img2 = ImageReader::new(Cursor::new(bytes)).with_guessed_format()?.decode()?;

And save them using [save] or [write_to] methods:

img.save("empty.jpg")?;

let mut bytes: Vec<u8> = Vec::new();
img2.write_to(&mut Cursor::new(&mut bytes), image::ImageFormat::Png)?;

Supported Image Formats

With default features enabled, image provides implementations of many common image format encoders and decoders.

Format Decoding Encoding
AVIF Yes (8-bit only) * Yes (lossy only)
BMP Yes Yes
DDS Yes ---
Farbfeld Yes Yes
GIF Yes Yes
HDR Yes Yes
ICO Yes Yes
JPEG Yes Yes
EXR Yes Yes
PNG Yes Yes
PNM Yes Yes
QOI Yes Yes
TGA Yes Yes
TIFF Yes Yes
WebP Yes Yes (lossless only)
  • * Requires the avif-native feature, uses the libdav1d C library.

Image Types

This crate provides a number of different types for representing images. Individual pixels within images are indexed with (0,0) at the top left corner.

An image parameterised by its Pixel type, represented by a width and height and a vector of pixels. It provides direct access to its pixels and implements the GenericImageView and GenericImage traits.

A DynamicImage is an enumeration over all supported ImageBuffer<P> types. Its exact image type is determined at runtime. It is the type returned when opening an image. For convenience DynamicImage reimplements all image processing functions.

Traits that provide methods for inspecting (GenericImageView) and manipulating (GenericImage) images, parameterised over the image's pixel type.

A view into another image, delimited by the coordinates of a rectangle. The coordinates given set the position of the top left corner of the rectangle. This is used to perform image processing functions on a subregion of an image.

All image format decoders implement the ImageDecoder trait which provide basic methods for getting image metadata and decoding images. Some formats additionally provide ImageDecoderRect implementations which allow for decoding only part of an image at once.

The most important methods for decoders are...

  • dimensions: Return a tuple containing the width and height of the image.
  • color_type: Return the color type of the image data produced by this decoder.
  • read_image: Decode the entire image into a slice of bytes.

Pixels

image provides the following pixel types:

  • Rgb: RGB pixel
  • Rgba: RGB with alpha (RGBA pixel)
  • Luma: Grayscale pixel
  • LumaA: Grayscale with alpha

All pixels are parameterised by their component type.

Image Processing Functions

These are the functions defined in the imageops module. All functions operate on types that implement the GenericImage trait. Note that some of the functions are very slow in debug mode. Make sure to use release mode if you experience any performance issues.

  • blur: Performs a Gaussian blur on the supplied image.
  • brighten: Brighten the supplied image.
  • huerotate: Hue rotate the supplied image by degrees.
  • contrast: Adjust the contrast of the supplied image.
  • crop: Return a mutable view into an image.
  • filter3x3: Perform a 3x3 box filter on the supplied image.
  • flip_horizontal: Flip an image horizontally.
  • flip_vertical: Flip an image vertically.
  • grayscale: Convert the supplied image to grayscale.
  • invert: Invert each pixel within the supplied image This function operates in place.
  • resize: Resize the supplied image to the specified dimensions.
  • rotate180: Rotate an image 180 degrees clockwise.
  • rotate270: Rotate an image 270 degrees clockwise.
  • rotate90: Rotate an image 90 degrees clockwise.
  • unsharpen: Performs an unsharpen mask on the supplied image.

For more options, see the imageproc crate.

Examples

Opening and Saving Images

image provides the open function for opening images from a path. The image format is determined from the path's file extension. An io module provides a reader which offer some more control.

use image::GenericImageView;

// Use the open function to load an image from a Path.
// `open` returns a `DynamicImage` on success.
let img = image::open("tests/images/jpg/progressive/cat.jpg").unwrap();

// The dimensions method returns the images width and height.
println!("dimensions {:?}", img.dimensions());

// The color method returns the image's `ColorType`.
println!("{:?}", img.color());

// Write the contents of this image to the Writer in PNG format.
img.save("test.png").unwrap();

Generating Fractals

//! An example of generating julia fractals.
let imgx = 800;
let imgy = 800;

let scalex = 3.0 / imgx as f32;
let scaley = 3.0 / imgy as f32;

// Create a new ImgBuf with width: imgx and height: imgy
let mut imgbuf = image::ImageBuffer::new(imgx, imgy);

// Iterate over the coordinates and pixels of the image
for (x, y, pixel) in imgbuf.enumerate_pixels_mut() {
    let r = (0.3 * x as f32) as u8;
    let b = (0.3 * y as f32) as u8;
    *pixel = image::Rgb([r, 0, b]);
}

// A redundant loop to demonstrate reading image data
for x in 0..imgx {
    for y in 0..imgy {
        let cx = y as f32 * scalex - 1.5;
        let cy = x as f32 * scaley - 1.5;

        let c = num_complex::Complex::new(-0.4, 0.6);
        let mut z = num_complex::Complex::new(cx, cy);

        let mut i = 0;
        while i < 255 && z.norm() <= 2.0 {
            z = z * z + c;
            i += 1;
        }

        let pixel = imgbuf.get_pixel_mut(x, y);
        let image::Rgb(data) = *pixel;
        *pixel = image::Rgb([data[0], i as u8, data[2]]);
    }
}

// Save the image as “fractal.png”, the format is deduced from the path
imgbuf.save("fractal.png").unwrap();

Example output:

A Julia Fractal, c: -0.4 + 0.6i

Writing raw buffers

If the high level interface is not needed because the image was obtained by other means, image provides the function save_buffer to save a buffer to a file.

let buffer: &[u8] = unimplemented!(); // Generate the image data

// Save the buffer as "image.png"
image::save_buffer("image.png", buffer, 800, 600, image::ExtendedColorType::Rgb8).unwrap()

image's People

Contributors

andersk avatar arthmis avatar aschampion avatar birktj avatar bvssvni avatar ccgn avatar danieledapo avatar drewcassidy avatar fintelia avatar fzyzcjy avatar hauleth avatar heroickatora avatar johannesvollmer avatar johntitor avatar josh015 avatar kaj avatar lo48576 avatar martinlindhe avatar mbrubeck avatar nabijaczleweli avatar nwin avatar paolobarbolini avatar pedrocr avatar philipc avatar philippeitis avatar potpourri avatar red75prime avatar ruuda avatar tomaka avatar yvt 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  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

image's Issues

Release 0.1

This issue will serve as todo list for the preparation of the 0.1 release. Feel free to edit this comment.

Todo list

  • Update Cargo.toml
  • Put the release notes somewhere

Dual license under Apache/MIT?

Currently, we are using the MIT license.

The difference between Apache and MIT is that Apache grants you patent licenses to use the open source.

There is a concern that we would have to check if we got the right to grant the patents when using Apache. For example, if some compression is patent protected but released under MIT, it would be permissible to use it in this library under the MIT license, but not the Apache license.

This means that in addition to receiving permission from contributors that dual license is OK, we have to check the algorithms and see what conditions they are permitted. It would be nice to do this anyway, even if we decide to stick to the MIT license.

Personally I think it is fine with MIT license. If people want to dual license, we can use the list of contributors (https://github.com/PistonDevelopers/image/graphs/contributors) subtract those who have not touched any specific algorithm and ask them to confirm the re-license.

Space after doc comments?

I prefer a space after doc comments and it seems the standard library keeps this convention as well.

Resize broken? (PNG)

Hello,

Does this work for you?
The resized image seems broken at the bottom.

extern crate image;

use std::io::File;
use image::{
    imageops
};

fn main() {
    let image_path  = Path::new("./data/800x1200.png");
    let thumb_path  = Path::new("./data/test.png");
    let img         = image::open(&Path::new(image_path)).unwrap();
    let resized_img = img.resize(300, 450, imageops::Nearest);

    let fout = File::create(&thumb_path).unwrap();
    let _ = resized_img.save(fout, image::PNG);
}

This is the original image:
800x1200

This is the resized image:
test

Thanks,
Ollie

Bug loading grey scale png for image width not divisible by 8

Loading and saving the following 300x300 pixel png produces this output:
Input:
input
Output:
output

This occurs when the image width is not divisible by 8 . The png decoder will pad the end of each row with 0 bits to make it divisible by 8. However, the dynimage::decoder_to_image function does not respect that the last width % 8 pixels in each row are unused.

The fix for this is pretty easy, but it would be nice if we had some tools to work with images with low bit depths.

Use suffix `_mut`?

The standard library uses a suffix _mut.

mut_pixelbuf => pixelbuf_mut
mut_rawbuf => rawbuf_mut

Critical error in `ImageBuf::from_fn`

The body of ImageBuf::from_fn contains a critical programming error:

pub fn from_fn(width: u32, height: u32, f: | u32, u32 | -> P) -> ImageBuf<P> {
    let pixels = range(0, width).cycle()
                                .enumerate()
                                .take(height as uint) //vector becomes `height` units long
                                .map( |(y, x)| f(x, y as u32))
                                .collect();

    ImageBuf::from_pixels(pixels, width, height)
}

The oversight here is that pixels will only be height long, and not width * height long. This causes strange errors down the road when doing anything with a significant area of the image that should be completely within the vector bounds if the vector was the correct size.

When I first came across this bug I thought it was a problem in my code, because I made the fatal but (IMHO) reasonable assumption that the function was trustworthy. It doesn't help that the runtime error message is very cryptic.

Test code:

#[test]
fn index_bug() {
    use image::{ImageBuf, Luma};
    use image::imageops::grayscale;

    let img = ImageBuf::from_fn(32, 32, |x, y| Luma((x * y) as u8));

    grayscale(&img);
}

Error message:

---- index_bug stdout ----
    task 'index_bug' failed at 'index out of bounds: the len is 32 but the index is 32', C:\Users\Austin\Documents\GitHub\img_dup\src\main.rs:1

The path is the main module in my project. While it is the right file, it is obviously not the correct line number, and there is no further information to deduce where the bounding error might be.

It actually occurs in imageops::grayscale because the function operates on the whole image, but does so by assuming width * height is the correct size. So once it attempts to index past height, the error is thrown.

I will forward the issue of the vague error message to the Rust team so hopefully it can be made more informative.

A simple fix for from_fn that keeps the functional style would be the following:

let pixels = range(0, height)
    .flat_map( |y|  
        range(0, width)
             .map(|x| f(x, y))
             .collect::<Vec<P>>()
             .move_iter()
    )
    .collect::<Vec<P>>();

Unfortunately, collecting into a Vec is necessary as the compiler cannot properly infer the lifetime of the closure in the nested map call without keeping the Map object from leaving the closure. This is an extra allocation for each row of the image. There are alternatives, but they don't avoid allocation per row and are longer and harder to read.

The imperative version is much more straightforward and avoids allocating a Vec per row, though it is longer:

let mut pixels: Vec<Luma<u8>> = Vec::with_capacity((width * height) as uint);

for y in range(0, height) {
    for x in range(0, width) {
        pixels.insert((y * height + x) as uint, f(x, y));
    }
}

I would like to submit the PR for this fix, though I don't know which version I should use. I don't think the extra allocation of the functional version is a big issue since it doesn't stay around for long, and from_fn is an initialization function anyways so it shouldn't be called often.

Failing with latest Rust nightly

fn extend(v: i32, t: u8) -> i32 {
    let vt = 1 << t as uint - 1; // ERROR
    let vt = vt as i32;

    if v < vt {
        v + ((-1) << t as uint) + 1
    }
    else {
        v
    }
}
error: cannot determine a type for this local variable: cannot determine the type of this integer; add a suffix to specify the type explicitly
src/codecs/jpeg/decoder.rs:659     let vt = 1 << t as uint - 1;
                                       ^~~~~~~~~~~~~~~~~~~~~~~

Benchmark

I know, I know, 💃 early stage.

Although, I'm very very interested to know how it performs with jpg (or png).

I did some image manipulations (most resize things) in the past few days (see: https://github.com/daddye/vips) and I had to give up and use straight c with libturbojpeg.

Would be very nice to know that now is possible to use rust for that!

Anyway, not a rustler, but the code seems very clean, simple and concise. For what is worth, Congrats!

cannot borrow immutable argument `method`

I think this is triggered by a change in Rust where arguments must be declared 'mut'.

src/sample.rs:117:20: 117:26 error: cannot borrow immutable argument `method` as mutable
src/sample.rs:117   let method = &mut method;
                                      ^~~~~~
src/sample.rs:142:20: 142:26 error: cannot borrow immutable argument `method` as mutable
src/sample.rs:142   let method = &mut method;
                                      ^~~~~~

Static lifetime issues

When running cargo build, I get the following error:

Could not compile `image`.

--- stderr
src/image.rs:346:28: 346:30 error: only the 'static lifetime is accepted here.
src/image.rs:346 pub struct SubImage <'a, I:'a> {
                                            ^~
src/image.rs:123:25: 123:27 error: only the 'static lifetime is accepted here.
src/image.rs:123 pub struct Pixels<'a, I:'a> {
                                         ^~
src/image.rs:152:28: 152:30 error: only the 'static lifetime is accepted here.
src/image.rs:152 pub struct MutPixels<'a, I:'a> {
                                            ^~
error: aborting due to 3 previous errors

Compile error with latest rustc

$ rustc -v
rustc 0.12.0-pre (09abbbdaf 2014-09-11 00:05:41 +0000)

$ cargo build
src/png/decoder.rs:379:24: 379:25 error: mismatched types: expected `|<generic #1519>| -> <generic #1519>`, found `u8` (expected
 fn, found u8)
src/png/decoder.rs:379                 |v| v, v
                                              ^
src/png/decoder.rs:382:15: 382:29 error: the type of this value must be known in this context
src/png/decoder.rs:382         .map(|(shift, pixel)| (pixel & mask << shift as uint) >> shift as uint);
                                     ^~~~~~~~~~~~~~
src/png/decoder.rs:372:18: 382:80 error: cannot determine a type for this bounded type parameter: unconstrained type
src/png/decoder.rs:372     let pixels = data
src/png/decoder.rs:373         .iter()
src/png/decoder.rs:374         .rev() // Reverse iterator
src/png/decoder.rs:375         .flat_map(|&v|
src/png/decoder.rs:376             // This has to be reversed to
src/png/decoder.rs:377             iter::range_step(0, 8, bit_depth)
                       ...
src/png/decoder.rs:372:18: 381:11 error: cannot determine a type for this bounded type parameter: unconstrained type
src/png/decoder.rs:372     let pixels = data
src/png/decoder.rs:373         .iter()
src/png/decoder.rs:374         .rev() // Reverse iterator
src/png/decoder.rs:375         .flat_map(|&v|
src/png/decoder.rs:376             // This has to be reversed to
src/png/decoder.rs:377             iter::range_step(0, 8, bit_depth)
                       ...
src/png/decoder.rs:377:13: 381:10 error: cannot determine a type for this bounded type parameter: unconstrained type
src/png/decoder.rs:377             iter::range_step(0, 8, bit_depth)
src/png/decoder.rs:378             .zip(iter::iterate(
src/png/decoder.rs:379                 |v| v, v
src/png/decoder.rs:380             )
src/png/decoder.rs:381         ))
src/png/decoder.rs:378:18: 378:31 error: cannot determine a type for this bounded type parameter: unconstrained type
src/png/decoder.rs:378             .zip(iter::iterate(
                                        ^~~~~~~~~~~~~
src/png/decoder.rs:383:59: 385:6 error: cannot determine a type for this bounded type parameter: unconstrained type
src/png/decoder.rs:383     for (chunk, (r, g, b)) in buf.mut_chunks(3).rev().zip(pixels.map(|i|
src/png/decoder.rs:384         palette[i as uint]
src/png/decoder.rs:385     )) {
src/dynimage.rs:448:39: 448:40 error: mismatched types: expected `|<generic #1397>| -> <generic #1397>`, found `u8` (expected fn
, found u8)
src/dynimage.rs:448                                |v| v, v
                                                          ^
src/dynimage.rs:451:30: 451:44 error: the type of this value must be known in this context
src/dynimage.rs:451                        .map(|(shift, pixel)|
                                                 ^~~~~~~~~~~~~~
src/dynimage.rs:443:21: 454:79 error: cannot determine a type for this bounded type parameter: unconstrained type
src/dynimage.rs:443             let p = buf.as_slice()
src/dynimage.rs:444                        .iter()
src/dynimage.rs:445                        .flat_map(|&v|
src/dynimage.rs:446                            iter::range_step_inclusive(8i8-(bit_depth as i8), 0, -(bit_depth as i8))
src/dynimage.rs:447                            .zip(iter::iterate(
src/dynimage.rs:448                                |v| v, v
                    ...
src/dynimage.rs:443:21: 453:25 error: cannot determine a type for this bounded type parameter: unconstrained type
src/dynimage.rs:443             let p = buf.as_slice()
src/dynimage.rs:444                        .iter()
src/dynimage.rs:445                        .flat_map(|&v|
src/dynimage.rs:446                            iter::range_step_inclusive(8i8-(bit_depth as i8), 0, -(bit_depth as i8))
src/dynimage.rs:447                            .zip(iter::iterate(
src/dynimage.rs:448                                |v| v, v
                    ...
src/dynimage.rs:443:21: 450:26 error: cannot determine a type for this bounded type parameter: unconstrained type
src/dynimage.rs:443             let p = buf.as_slice()
src/dynimage.rs:444                        .iter()
src/dynimage.rs:445                        .flat_map(|&v|
src/dynimage.rs:446                            iter::range_step_inclusive(8i8-(bit_depth as i8), 0, -(bit_depth as i8))
src/dynimage.rs:447                            .zip(iter::iterate(
src/dynimage.rs:448                                |v| v, v
                    ...
src/dynimage.rs:446:28: 450:25 error: cannot determine a type for this bounded type parameter: unconstrained type
src/dynimage.rs:446                            iter::range_step_inclusive(8i8-(bit_depth as i8), 0, -(bit_depth as i8))
src/dynimage.rs:447                            .zip(iter::iterate(
src/dynimage.rs:448                                |v| v, v
src/dynimage.rs:449                            )
src/dynimage.rs:450                        ))
src/dynimage.rs:447:33: 447:46 error: cannot determine a type for this bounded type parameter: unconstrained type
src/dynimage.rs:447                            .zip(iter::iterate(
                                                    ^~~~~~~~~~~~~

Decoding of 1 bit PNGs is broken

Somehow every 8 bits get mirrored. Probably this is also the case of the 4 and 8 bit images. It is just not visible in the test images.

Support progressive JPEG

Currently, this library doesn't support progressive JPEG images, which are very common and are the default output format for some programs, such as GIMP.

Possible issue with animated GIFs

I don't have the error message on hand (wasn't much use anyways), but it was an index out of bounds error when iterating the pixels of an image. For some reason, the internal vector length of the ImageBuf did not match width * height and so the iterator was going past the end. I was able to reduce the problem domain to certain animated GIFs, though I cannot provide the images I used for personal reasons.

Integer type inference breaks decoder.rs

src/codecs/jpeg/decoder.rs:659:9: 659:32 error: cannot determine a type for this local variable: cannot determine the type of this integer; add a suffix to specify the type explicitly     
src/codecs/jpeg/decoder.rs:659     let vt = 1 << t as uint - 1;                                                                                                                             
                                       ^~~~~~~~~~~~~~~~~~~~~~~                                                                                                                              

Decoding api

Sequential Decoding

This is a proposed trait that all image decoders will implement:

trait ImageDecoder {
        //Return a tuple containing the width and height of the image
        fn dimensions(&mut self) -> ImageResult<(u32, u32)>;

        //Return the color type of the image e.g RGB(8) (8bit RGB)
        fn colortype(&mut self) -> ImageResult<ColorType>;

        //Returns the length in bytes of one decoded row of the image
        fn row_len(&mut self) -> ImageResult<uint>;

        //Read one row from the image into buf
        //Returns the row index
        fn read_scanline(&mut self, buf: &mut [u8]) -> ImageResult<u32>;

        //Decode the entire image and return it as a Vector
        fn read_image(&mut self) -> ImageResult<Vec<u8>>;
}

Where the ImageResult is the type:

        type ImageResult<T> = Result<T, ImageError>;

And ImageError is an enumeration of common errors that can occur while decoding.

Build error: match arms have incompatible types

A match block in src/png/decoder.rs causes rust-image build to fail. Noticed this when trying to build Piston.

relevant stderr:

src/png/decoder.rs:101:9: 104:10 error: match arms have incompatible types: expected `&[(u8,u8,u8)]`, found `&'static [<generic #42>, .. 0]` (expected vector, found vector)
src/png/decoder.rs:101         match self.palette {
src/png/decoder.rs:102             Some(ref p) => p.as_slice(),
src/png/decoder.rs:103             None        => &[]
src/png/decoder.rs:104         }
src/png/decoder.rs:103:28: 103:31 note: match arm with an incompatible type
src/png/decoder.rs:103             None        => &[]
                                              ^~~

Fix compiler warnings

src/png/decoder.rs:37:5: 37:17 warning: variant is never used: `HaveLastIDat`, #[warn(dead_code)] on by default
src/png/decoder.rs:37     HaveLastIDat,
                          ^~~~~~~~~~~~
src/png/decoder.rs:38:5: 38:13 warning: variant is never used: `HaveIEND`, #[warn(dead_code)] on by default
src/png/decoder.rs:38     HaveIEND
                          ^~~~~~~~
src/jpeg/decoder.rs:102:5: 102:8 warning: variant is never used: `End`, #[warn(dead_code)] on by default
src/jpeg/decoder.rs:102     End
                            ^~~

Perhaps by adding a #[allow(dead_code)] on enum?

Better API design

I really don't like our current API. I has a lot of code-duplication because of this DynamicImage enum and is not very ergonomic. At the moment this is ok but just image what would happen if we finally add 16bit and CYMK image types. This would blow up our enum to at least 20 variants.

Could a trait + associated type help with this?

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.