DICOM.jl: Julia Package for Reading and Writing DICOM Files

DICOM.jl

Julia interface for parsing/writing DICOM (Digital Imaging and Communications in Medicine) files 

Usage

Installation

To install the package:

julia> using Pkg
julia> Pkg.add("DICOM")

Load the package by

julia> using DICOM

Reading Data

Read a DICOM file by

julia> dcm_data = dcm_parse("path/to/dicom/file")

The data in dcm_data is structured as a dictionary, and individual DICOM elements can be accessed by their hex tag. For example, the hex tag of "Pixel Data" is 7FE0,0010, and it can be accessed in Julia by dcm_data[(0x7FE0,0x0010)] or by dcm_data[tag"PixelData"].

Multiple DICOM files in a folder can be read by

julia> dcm_data_array = dcmdir_parse("path/to/dicom/folder")

Writing Data

Data can be written to a DICOM file by

julia> dcm_write("path/to/output/file", dcm_data)

Additional Notes

DICOM files should begin with a 128-bytes (which are ignored) followed by the string DICM. If this preamble is missing, then the file can be parsed by dcm_parse(path/to/file, preamble = false).

DICOM files use either explicit or implicit value representation (VR). For implicit files, DICOM.jl will use a lookup table to guess the VR from the DICOM element's hex tag. For explicit files, DICOM.jl will read the VRs from the file.

An auxiliary user-defined dictionary can be supplied to override the default lookup table For example, the "Instance Number" - tag (0x0020,0x0013) - is an integer (default VR = "IS"). We can read this as a float by setting the VR to "DS" by:

my_vrs = Dict( (0x0020,0x0013) => "DS" )
dcm_data = dcm_parse("path/to/dicom/file", aux_vr = my_vrs)

Now dcm_data[(0x0020,0x0013)] will return a float instead of an integer.

  • The parsed VRs are stored in dcm_data.vr

It is possible to skip an element by setting its VR to "". For example, we can skip reading the Instance Number by

my_vrs = Dict( (0x0020,0x0013) => "" )
dcm_data = dcm_parse("path/to/dicom/file", aux_vr = my_vr)

and now dcm_data[(0x0020,0x0013)] will return an error because the key (0x0020,0x0013) doesn't exist - it was skipped during reading.

The user-supplied VR can contain a master VR with the tag (0x0000,0x0000) which will be used whenever DICOM.jl is unable to guess the VR on its own. This is convenient for reading older dicom files and skipping retired elements - i.e. where the VR lookup fails - by:

my_vrs = Dict( (0x0000,0x0000) => "" )
dcm_data = dcm_parse("path/to/dicom/file", aux_vr = my_vrs)

A user-supplied VR can also be supplied during writing, e.g.:

julia> dcm_write("path/to/output/file", dcm_data, aux_vr = user_defined_vr)

where user_defined_vr is a dictionary which maps the hex tag to the VR.

Download Details:

Author: JuliaHealth
Source Code: https://github.com/JuliaHealth/DICOM.jl 
License: MIT license

#julia #digital #images 

DICOM.jl: Julia Package for Reading and Writing DICOM Files

6 Favorite PHP Libraries for Manipulating Images

In today's post we will learn about 6 Favorite PHP Libraries for Manipulating Images. 

What is Manipulating Images?

Image Manipulation refers to the act of transforming images to arrive at the desired output. Through the art of photo manipulation, you are able to transcend your image into a true piece of art, Whereas, image manipulation can be done through the use of various image editing tools or software.

It is a blend between photography and graphic design. Designers use numerous effects to transform an image into a remarkable one, their aim to bring realism into an unreal picture.

The magic about high-quality manipulated images is that although one can understand that it does not portray reality, the plausibility of a picture makes it worth pondering about!

Table of contents:

  • Color Extractor - A library for extracting colours from images.
  • Glide - An on-demand image manipulation library.
  • Image Hash - A library for generating perceptual image hashes.
  • Image Optimizer - A library for optimizing images.
  • Intervention Image - Another image manipulation library.
  • PHP Image Workshop - Another image manipulation library.

1 - Color Extractor:

A library for extracting colours from images.

Install

Via Composer

$ composer require league/color-extractor:0.3.*

Usage

require 'vendor/autoload.php';

use League\ColorExtractor\Color;
use League\ColorExtractor\ColorExtractor;
use League\ColorExtractor\Palette;

$palette = Palette::fromFilename('./some/image.png');

// $palette is an iterator on colors sorted by pixel count
foreach($palette as $color => $count) {
    // colors are represented by integers
    echo Color::fromIntToHex($color), ': ', $count, "\n";
}

// it offers some helpers too
$topFive = $palette->getMostUsedColors(5);

$colorCount = count($palette);

$blackCount = $palette->getColorCount(Color::fromHexToInt('#000000'));


// an extractor is built from a palette
$extractor = new ColorExtractor($palette);

// it defines an extract method which return the most “representative” colors
$colors = $extractor->extract(5);

Handling transparency

By default any pixel with alpha value greater than zero will be discarded. This is because transparent colors are not perceived as is. For example fully transparent black would be seen white on a white background. So if you want to take transparency into account when building a palette you have to specify this background color. You can do this with the second argument of Palette constructors. Its default value is null, meaning a color won't be added to the palette if its alpha component exists and is greater than zero.

You can set it as an integer representing the color, then transparent colors will be blended before addition to the palette.

// we set a white background so fully transparent colors will be added as white in the palette
// pure red #FF0000 at 50% opacity will be stored as #FF8080 as it would be perceived
$palette = Palette::fromFilename('./some/image.png', Color::fromHexToInt('#FFFFFF'));

View on Github

2 - Glide:

An on-demand image manipulation library.

Glide is a wonderfully easy on-demand image manipulation library written in PHP. Its straightforward API is exposed via HTTP, similar to cloud image processing services like Imgix and Cloudinary. Glide leverages powerful libraries like Intervention Image (for image handling and manipulation) and Flysystem (for file system abstraction).

Highlights

  • Adjust, resize and add effects to images using a simple HTTP based API.
  • Manipulated images are automatically cached and served with far-future expires headers.
  • Create your own image processing server or integrate Glide directly into your app.
  • Supports both the GD library and the Imagick PHP extension.
  • Supports many response methods, including PSR-7, HttpFoundation and more.
  • Ability to secure image URLs using HTTP signatures.
  • Works with many different file systems, thanks to the Flysystem library.
  • Powered by the battle tested Intervention Image image handling and manipulation library.
  • Framework-agnostic, will work with any project.
  • Composer ready and PSR-2 compliant.

Installation

Glide is available via Composer:

$ composer require league/glide

Testing

Glide has a PHPUnit test suite. To run the tests, run the following command from the project folder:

$ phpunit

Contributing

Contributions are welcome and will be fully credited. Please see CONTRIBUTING for details.

View on Github

3 - Image Hash:

A library for generating perceptual image hashes.

A perceptual hash is a fingerprint of a multimedia file derived from various features from its content. Unlike cryptographic hash functions which rely on the avalanche effect of small changes in input leading to drastic changes in the output, perceptual hashes are "close" to one another if the features are similar.

Installation

This package has not reached a stable version yet, backwards compatibility may be broken between 0.x releases. Make sure to lock your version if you intend to use this in production!

Install using composer:

composer require jenssegers/imagehash

Usage

The library comes with 4 built-in hashing implementations:

  • Jenssegers\ImageHash\Implementations\AverageHash - Hash based the average image color
  • Jenssegers\ImageHash\Implementations\DifferenceHash - Hash based on the previous pixel
  • Jenssegers\ImageHash\Implementations\BlockHash - Hash based on blockhash.io Still under development
  • Jenssegers\ImageHash\Implementations\PerceptualHash - The original pHash Still under development

Choose one of these implementations. If you don't know which one to use, try the DifferenceHash implementation. Some implementations allow some configuration, be sure to check the constructor.

use Jenssegers\ImageHash\ImageHash;
use Jenssegers\ImageHash\Implementations\DifferenceHash;

$hasher = new ImageHash(new DifferenceHash());
$hash = $hasher->hash('path/to/image.jpg');

echo $hash;
// or
echo $hash->toHex();

The resulting Hash object, is a hexadecimal image fingerprint that can be stored in your database once calculated. The hamming distance is used to compare two image fingerprints for similarities. Low distance values will indicate that the images are similar or the same, high distance values indicate that the images are different. Use the following method to detect if images are similar or not:

$distance = $hasher->distance($hash1, $hash2);
// or
$distance = $hash1->distance($hash2);

Equal images will not always have a distance of 0, so you will need to decide at which distance you will evaluate images as equal. For the image set that I tested, a max distance of 5 was acceptable. But this will depend on the implementation, the images and the number of images. For example; when comparing a small set of images, a lower maximum distances should be acceptable as the chances of false positives are quite low. If however you are comparing a large amount of images, 5 might already be too much.

The Hash object can return the internal binary hash in a couple of different format:

echo $hash->toHex(); // 7878787c7c707c3c
echo $hash->toBits(); // 0111100001111000011110000111110001111100011100000111110000111100
echo $hash->toInt(); // 8680820757815655484
echo $hash->toBytes(); // "\x0F\x07ƒƒ\x03\x0F\x07\x00"

Choose your preference for storing your hashes in your database. If you want to reconstruct a Hash object from a previous calculated value, use:

$hash = Hash::fromHex('7878787c7c707c3c');
$hash = Hash::fromBin('0111100001111000011110000111110001111100011100000111110000111100');
$hash = Hash::fromInt('8680820757815655484');

View on Github

4 - Image Optimizer:

A library for optimizing images.

This library is handy and very easy to use optimizer for image files. It uses optipng, pngquant, jpegoptim, svgo and few more libraries, so before use it you should install proper libraries on your server. Project contains Vagrantfile that defines testing virtual machine with all libraries installed, so you can check Vagrantfile how to install all those stuff.

Thanks to ImageOptimizer and libraries that it uses, your image files can be 10%-70% smaller.

Installation

Using composer:

composer require ps/image-optimizer

Basic usage

$factory = new \ImageOptimizer\OptimizerFactory();
$optimizer = $factory->get();

$filepath = /* path to image */;

$optimizer->optimize($filepath);
//optimized file overwrites original one

Configuration

By default optimizer does not throw any exception, if file can not be optimized or optimizing library for given file is not installed, optimizer will not touch original file. This behaviour is ok when you want to eventually optimize files uploaded by user. When in your use case optimization fault should cause exception, ignore_errors option was created especially for you.

This library is very smart, you do not have to configure paths to all binaries of libraries that are used by ImageOptimizer, library will be looking for those binaries in few places, so if binaries are placed in standard places, it will be found automatically.

Supported options:

  • ignore_errors (default: true)
  • single_optimizer_timeout_in_seconds (default: 60) - useful when you want to have control how long optimizing lasts. For example in some cases optimizing may not be worth when it takes big amount of time. Pass null in order to turn off timeout.
  • output_filepath_pattern (default: %basename%/%filename%%ext%) - destination where optimized file will be stored. By default it overrides original file. There are 3 placehoders: %basename%, %filename% (without extension and dot) and %ext% (extension with dot) which will be replaced by values from original file.
  • execute_only_first_png_optimizer (default: true) - execute the first successful or all png optimizers
  • execute_only_first_jpeg_optimizer (default: true) - execute the first successful or all jpeg optimizers
  • optipng_options (default: array('-i0', '-o2', '-quiet')) - an array of arguments to pass to the library
  • pngquant_options (default: array('--force'))
  • pngcrush_options (default: array('-reduce', '-q', '-ow'))
  • pngout_options (default: array('-s3', '-q', '-y'))
  • advpng_options (default: array('-z', '-4', '-q'))
  • gifsicle_options (default: array('-b', '-O5'))
  • jpegoptim_options (default: array('--strip-all', '--all-progressive'))
  • jpegtran_options (default: array('-optimize', '-progressive'))
  • svgo_options (default: array('--disable=cleanupIDs'))
  • custom_optimizers (default array())
  • optipng_bin (default: will be guessed) - you can enforce paths to binaries, but by default it will be guessed
  • pngquant_bin
  • pngcrush_bin
  • pngout_bin
  • advpng_bin
  • gifsicle_bin
  • jpegoptim_bin
  • jpegtran_bin
  • svgo_bin

You can pass array of options as first argument of ImageOptimizer\OptimizerFactory constructor. Second argument is optionally Psr\LoggerInterface.

$factory = new \ImageOptimizer\OptimizerFactory(array('ignore_errors' => false), $logger);

View on Github

5 - Intervention Image:

Another image manipulation library.

Intervention Image is a PHP image handling and manipulation library providing an easier and expressive way to create, edit, and compose images. The package includes ServiceProviders and Facades for easy Laravel integration.

Requirements

  • PHP >=5.4
  • Fileinfo Extension

Supported Image Libraries

  • GD Library (>=2.0)
  • Imagick PHP extension (>=6.5.7)

Getting started

Code Examples

// open an image file
$img = Image::make('public/foo.jpg');

// resize image instance
$img->resize(320, 240);

// insert a watermark
$img->insert('public/watermark.png');

// save image in desired format
$img->save('public/bar.jpg');

Refer to the official documentation to learn more about Intervention Image.

Contributing

Contributions to the Intervention Image library are welcome. Please note the following guidelines before submitting your pull request.

  • Follow PSR-2 coding standards.
  • Write tests for new functions and added features
  • API calls should work consistently with both GD and Imagick drivers

View on Github

6 - PHP Image Workshop:

Another image manipulation library.

Summary and features

Really flexible and easy-to-use PHP class to work with images using the GD Library

http://phpimageworkshop.com/

Current master branch correspond to the next major release (v3) which only support PHP 8.0+.

Installation

The class is designed for PHP 8.0+... Check how to install the class here: http://phpimageworkshop.com/installation.html

For older PHP versions support, install the 2.x version branch.

Usage

What's new in the doc' ?

@todo

  • Adding a method to add easily borders to a layer (external, inside and middle border)
  • Check given hexa' color and remove if exists.

View on Github

Thank you for following this article.

Related videos:

Codeigniter - Image Manipulation

#php #images 

6 Favorite PHP Libraries for Manipulating Images

10 Best Libraries for Manipulating Images in Go

In today's post we will learn about 10 Best Libraries for Manipulating Images in Go.

What is a Manipulating Images?

Image Manipulation refers to the act of transforming images to arrive at the desired output. Through the art of photo manipulation, you are able to transcend your image into a true piece of art, Whereas, image manipulation can be done through the use of various image editing tools or software.

Table of contents:

  • Bild - Collection of image processing algorithms in pure Go.
  • Bimg - Small package for fast and efficient image processing using libvips.
  • Cameron - An avatar generator for Go.
  • Canvas - Vector graphics to PDF, SVG or rasterized image.
  • Darkroom - An image proxy with changeable storage backends and image processing engines with focus on speed and resiliency.
  • Draft - Generate High Level Microservice Architecture diagrams for GraphViz using simple YAML syntax.
  • Geopattern - Create beautiful generative image patterns from a string.
  • GG - 2D rendering in pure Go.
  • Gift - Package of image processing filters.
  • Gltf - Efficient and robust glTF 2.0 reader, writer and validator.

1 - Bild: Collection of image processing algorithms in pure Go.

A collection of parallel image processing algorithms in pure Go.

The aim of this project is simplicity in use and development over absolute high performance, but most algorithms are designed to be efficient and make use of parallelism when available.

It uses packages from the standard library whenever possible to reduce dependency use and development abstractions.

All operations return image types from the standard library.

CLI usage

Download and compile from sources:

go get github.com/anthonynsimon/bild

Or get the pre-compiled binaries for your platform on the releases page

bild

A collection of parallel image processing algorithms in pure Go

Usage:
  bild [command]

Available Commands:
  adjust      adjust basic image features like brightness or contrast
  blend       blend two images together
  blur        blur an image using the specified method
  channel     channel operations on images
  effect      apply effects on images
  help        Help about any command
  histogram   histogram operations on images
  imgio       i/o operations on images
  noise       noise generators
  segment     segment an image using the specified method

Flags:
  -h, --help      help for bild
      --version   version for bild

Use "bild [command] --help" for more information about a command.

For example, to apply a median effect with a radius of 1.5 on the image input.png, writing the result into a new file called output.png:

bild effect median --radius 1.5 input.png output.png

Install package

bild requires Go version 1.11 or greater.

go get github.com/anthonynsimon/bild/...

Basic package usage example:

package main

import (
    "github.com/anthonynsimon/bild/effect"
    "github.com/anthonynsimon/bild/imgio"
    "github.com/anthonynsimon/bild/transform"
)

func main() {
    img, err := imgio.Open("input.jpg")
    if err != nil {
        fmt.Println(err)
        return
    }

    inverted := effect.Invert(img)
    resized := transform.Resize(inverted, 800, 800, transform.Linear)
    rotated := transform.Rotate(resized, 45, nil)

    if err := imgio.Save("output.png", rotated, imgio.PNGEncoder()); err != nil {
        fmt.Println(err)
        return
    }
}

View on Github

2 - Bimg: Small package for fast and efficient image processing using libvips.

Small Go package for fast high-level image processing using libvips via C bindings, providing a simple programmatic API.

bimg was designed to be a small and efficient library supporting common image operations such as crop, resize, rotate, zoom or watermark. It can read JPEG, PNG, WEBP natively, and optionally TIFF, PDF, GIF and SVG formats if libvips@8.3+ is compiled with proper library bindings. Lastly AVIF is supported as of libvips@8.9+. For AVIF support libheif needs to be compiled with an applicable AVIF en-/decoder.

bimg is able to output images as JPEG, PNG and WEBP formats, including transparent conversion across them.

bimg uses internally libvips, a powerful library written in C for image processing which requires a low memory footprint and it's typically 4x faster than using the quickest ImageMagick and GraphicsMagick settings or Go native image package, and in some cases it's even 8x faster processing JPEG images.

If you're looking for an HTTP based image processing solution, see imaginary.

bimg was heavily inspired in sharp, its homologous package built for node.js. bimg is used in production environments processing thousands of images per day.

v1 notice: bimg introduces some minor breaking changes in v1 release. If you're using gopkg.in, you can still rely in the v0 without worrying about API breaking changes.

Prerequisites

  • libvips 8.3+ (8.8+ recommended)
  • C compatible compiler such as gcc 4.6+ or clang 3.0+
  • Go 1.3+

Note:

  • libvips v8.3+ is required for GIF, PDF and SVG support.
  • libvips v8.9+ is required for AVIF support. libheif compiled with a AVIF en-/decoder also needs to be present.

Installation

go get -u github.com/h2non/bimg

libvips

Follow libvips installation instructions:

https://libvips.github.io/libvips/install.html

Installation script

Note: install script is officially deprecated, it might not work as expected. We recommend following libvips install instructions.

Run the following script as sudo (supports OSX, Debian/Ubuntu, Redhat, Fedora, Amazon Linux):

curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -

If you want to take the advantage of OpenSlide, simply add --with-openslide to enable it:

curl -s https://raw.githubusercontent.com/h2non/bimg/master/preinstall.sh | sudo bash -s --with-openslide

The install script requires curl and pkg-config.

View on Github

3 - Cameron: An avatar generator for Go.

An avatar generator for Go.

Oh, by the way, the name of this project came from the Avatar's director James Cameron.

Installation

Open your terminal and execute

$ go get github.com/aofei/cameron

done.

The only requirement is the Go, at least v1.13.

Quick Start

Create a file named cameron.go

package main

import (
	"bytes"
	"image/png"
	"net/http"

	"github.com/aofei/cameron"
)

func main() {
	http.ListenAndServe("localhost:8080", http.HandlerFunc(identicon))
}

func identicon(rw http.ResponseWriter, req *http.Request) {
	buf := bytes.Buffer{}
	png.Encode(&buf, cameron.Identicon([]byte(req.RequestURI), 540, 60))
	rw.Header().Set("Content-Type", "image/png")
	buf.WriteTo(rw)
}

and run it

$ go run cameron.go

then visit http://localhost:8080 with different paths.

Community

If you want to discuss Cameron, or ask questions about it, simply post questions or ideas here.

View on Github

4 - Canvas: Vector graphics to PDF, SVG or rasterized image.

Canvas is a common vector drawing target that can output SVG, PDF, EPS, raster images (PNG, JPG, GIF, ...), HTML Canvas through WASM, OpenGL, and Gio. It has a wide range of path manipulation functionality such as flattening, stroking and dashing implemented. Additionally, it has a text formatter and embeds and subsets fonts (TTF, OTF, WOFF, WOFF2, or EOT) or converts them to outlines. It can be considered a Cairo or node-canvas alternative in Go. See the example below in Figure 1 for an overview of the functionality.

Figure 1: top-left you can see text being fitted into a box, justified using Donald Knuth's linea breaking algorithm to stretch the spaces between words to fill the whole width. You can observe a variety of styles and text decorations applied, as well as support for LTR/RTL mixing and complex scripts. In the bottom-right the word "stroke" is being stroked and drawn as a path. Top-right we see a LaTeX formula that has been converted to a path. Left of that we see an ellipse showcasing precise dashing, notably the length of e.g. the short dash is equal wherever it is on the curve. Note that the dashes themselves are elliptical arcs as well (thus exactly precise even if magnified greatly). To the right we see a closed polygon of four points being smoothed by cubic Béziers that are smooth along the whole path, and the blue line on the left shows a smoothed open path. On the bottom you can see a rotated rasterized image. The result is equivalent for all renderers (PNG, PDF, SVG, etc.).

Recent changes

  • Renderers have been moved from github.com/tdewolff/canvas/. to github.com/tdewolff/canvas/renderers/.
  • FontFamily.Use() is deprecated, use FontFamily.SetFeatures() (not yet used)
  • DPMM is now a function just like DPI: rasterizer.PNGWriter(5.0 * canvas.DPMM) => rasterizer.PNGWriter(canvas.DPMM(5.0))
  • FontFace is now passed around as a pointer
  • NewRichText now requires a default *FontFace to be passed
  • Use the latex build tag to use the original LaTeX expression parser
  • Renderer writers have been moved from renderers/ABC/abc.Writer to renderers/ABC
  • rasterizer.New is renamed to rasterizer.FromImage

Features

  • Path segment types: MoveTo, LineTo, QuadTo, CubeTo, ArcTo, Close
  • Precise path flattening, stroking, and dashing for all segment type uing papers (see below)
  • Smooth spline generation through points for open and closed paths
  • LaTeX to path conversion (native Go and CGO implementations available)
  • Font formats support
  •  
    • SFNT (such as TTF, OTF, WOFF, WOFF2, EOT) supporting TrueType, CFF, and CFF2 tables
  • HarfBuzz for text shaping (native Go and CGO implementations available)
  • FriBidi for text bidirectionality (native Go and CGO implementations available)
  • Donald Knuth's line breaking algorithm for text layout
  • sRGB compliance (use SRGBColorSpace, only available for rasterizer)
  • Font rendering with gamma correction of 1.43 (WIP)
  • Rendering targets

Raster images (PNG, GIF, JPEG, TIFF, BMP, WEBP)

PDF

SVG and SVGZ

PS and EPS

HTMLCanvas

OpenGL

Gio

Fyne (WIP)

Rendering sources

Canvas itself

go-chart

gonum/plot

View on Github

5 - Darkroom: An image proxy with changeable storage backends and image processing engines with focus on speed and resiliency.

Introduction

Darkroom combines the storage backend and the image processor and acts as an Image Proxy on your image source.
You may implement your own Storage and Processor interfaces to gain custom functionality while still keeping other Darkroom Server functionality.
The native implementations focus on speed and resiliency.

Features

Darkroom supports several image operations which are documented here.

Installation

go get -u github.com/gojek/darkroom

Other ways to run can be found here.

Metrics Support

Darkroom supports Prometheus and StatsD for tracking and monitoring metrics. You need to specify the metrics system by adding an environment variable, METRICS_SYSTEM=prometheus/statsd

Prometheus

The application exposes the metrics at "http://<application_url>/metrics" endpoint. Since it's a pull based system, Prometheus server that is set up from docker-compose scrapes metrics from the application endpoint and its configuration can be changed in prometheus.yml.

StatsD

In order to use StatsD as your metrics system, you also need to add the following env variables,

METRICS_STATSD_STATSDADDR=hostname:port
METRICS_STATSD_PREFIX=client-prefix
METRICS_STATSD_SAMPLERATE=sample-rate
METRICS_STATSD_FLUSHBYTES=flushbytes

These are used to set up the StatsD client.

View on Github

6 - Draft: Generate High Level Microservice Architecture diagrams for GraphViz using simple YAML syntax.

A commandline tool that generate High Level microservice & serverless Architecture diagrams using a declarative syntax defined in a YAML file.

Why?

I prefer to think in terms of capabilities rather than specific vendor services.

  • "do we need a DNS?" instead of "do we need Route 53?"
  • "do we need a CDN?" instead of "do we need Cloudfront?"
  • "do we need a database? if yes? what type? Relational? No SQL" instead of "do we need Google Cloud Datastore?"_
  • "do we need some serverless function?" instead of "do we need an Azure Function"

...and so on.

How draft works?

draft takes in input a declarative YAML file and generates a dot script for Graphviz

draft backend-for-frontend.yml | dot -Tpng -Gdpi=200 > backend-for-frontend.png 

Piping the draft output to GraphViz dot you can generate different output formats:

formatcommand
PNGdraft input.yml | dot -Tpng > output.png
JPEGdraft input.yml | dot -Tjpg > output.jpg
PostScriptdraft input.yml | dot -Tps > output.ps
SVGdraft input.yml | dot -Tsvg > output.svg

To install GraphViz to your favorite OS, please, follow this link https://graphviz.gitlab.io/download/.

Installation Steps

To build the binaries by yourself, assuming that you have Go installed, here the steps:

Clone the repo,

git clone https://github.com/lucasepe/draft.git

Move to the 'cmd' directory:

cd draft/cmd

Generate the static assets

go generate ../...

Build the binary tool

go build -o draft

View on Github

7 - Geopattern: Create beautiful generative image patterns from a string.

Create beautiful generative image patterns from a string in golang.

Go port of Jason Long's awesome GeoPattern library.

Generate beautiful tiling SVG patterns from a string. The string is converted into a SHA and a color and pattern are determined based on the values in the hash. The color is determined by shifting the hue from a default (or passed in) base color. One of 16 patterns is used (or you can specify one) and the sizing of the pattern elements is also determined by the hash values.

You can use the generated pattern as the background-image for a container. Using the base64 representation of the pattern still results in SVG rendering, so it looks great on retina displays.

See the GitHub Guides site as an example of this library in action. GitHub Guides use Original Ruby implementation.

Installation

go get github.com/pravj/geopattern

Usage

Example directory contains sample go programs that explains use of geopattern

API

Arguments for functions returning pattern's string

phrase : custom pattern phrase

args := map[string]string{"phrase": "My Custom Phrase"}

generator : custom pattern type

args := map[string]string{"generator": "plaid"}

color : custom background color

args := map[string]string{"color": "#3b5998"}

baseColor : custom base color that decides background color

args := map[string]string{"baseColor": "#ffcc00"}

View on Github

8 - GG: 2D rendering in pure Go.

Go Graphics

gg is a library for rendering 2D graphics in pure Go.

Installation

go get -u github.com/fogleman/gg

Alternatively, you may use gopkg.in to grab a specific major-version:

go get -u gopkg.in/fogleman/gg.v1

Hello, Circle!

Look how easy!

package main

import "github.com/fogleman/gg"

func main() {
    dc := gg.NewContext(1000, 1000)
    dc.DrawCircle(500, 500, 400)
    dc.SetRGB(0, 0, 0)
    dc.Fill()
    dc.SavePNG("out.png")
}

Examples

There are lots of examples included. They're mostly for testing the code, but they're good for learning, too.

Examples

Creating Contexts

There are a few ways of creating a context.

NewContext(width, height int) *Context
NewContextForImage(im image.Image) *Context
NewContextForRGBA(im *image.RGBA) *Context

View on Github

9 - Gift: Package of image processing filters.

GO IMAGE FILTERING TOOLKIT (GIFT)

Package gift provides a set of useful image processing filters.

Pure Go. No external dependencies outside of the Go standard library.

INSTALLATION / UPDATING

go get -u github.com/disintegration/gift

QUICK START

// 1. Create a new filter list and add some filters.
g := gift.New(
	gift.Resize(800, 0, gift.LanczosResampling),
	gift.UnsharpMask(1, 1, 0),
)

// 2. Create a new image of the corresponding size.
// dst is a new target image, src is the original image.
dst := image.NewRGBA(g.Bounds(src.Bounds()))

// 3. Use the Draw func to apply the filters to src and store the result in dst.
g.Draw(dst, src)

USAGE

To create a sequence of filters, the New function is used:

g := gift.New(
	gift.Grayscale(),
	gift.Contrast(10),
)

Filters also can be added using the Add method:

g.Add(GaussianBlur(2))

The Bounds method takes the bounds of the source image and returns appropriate bounds for the destination image to fit the result (for example, after using Resize or Rotate filters).

dst := image.NewRGBA(g.Bounds(src.Bounds()))

There are two methods available to apply these filters to an image:

  • Draw applies all the added filters to the src image and outputs the result to the dst image starting from the top-left corner (Min point).
g.Draw(dst, src)
  • DrawAt provides more control. It outputs the filtered src image to the dst image at the specified position using the specified image composition operator. This example is equivalent to the previous:
g.DrawAt(dst, src, dst.Bounds().Min, gift.CopyOperator)

View on Github

10 - Gltf: Efficient and robust glTF 2.0 reader, writer and validator.

A Go module for efficient and robust serialization/deserialization of glTF 2.0, a royalty-free specification for the efficient transmission and loading of 3D scenes and models by applications, also known as "the JPEG of 3D".

📜 Getting started

Data Model

qmuntal/gltf implements the whole glTF 2.0 specification. The top level element is the gltf.Document and it contains all the information to hold a gltf document in memory:

// This document does not produce any valid glTF, it is just an example.
gltf.Document{
  Accessors: []*gltf.Accessor{
      {BufferView: gltf.Index(0), ComponentType: gltf.ComponentUshort, Type: gltf.AccessorScalar},
  },
  Asset: gltf.Asset{Version: "2.0", Generator: "qmuntal/gltf"},
  BufferViews: []*gltf.BufferView{
      {ByteLength: 72, ByteOffset: 0, Target: gltf.TargetElementArrayBuffer},
  },
  Buffers: []*gltf.Buffer{{ByteLength: 1033, URI: bufferData}},
  Meshes: []*gltf.Mesh{{
    Name: "Cube",
  }},
  Nodes: []*gltf.Node{{Name: "Cube", Mesh: gltf.Index(0)}},
  Scene:    gltf.Index(0),
  Scenes:   []*gltf.Scene{{Name: "Root Scene", Nodes: []uint32{0}}},
}

Optional parameters

All optional properties whose default value does not match with the golang type zero value are defines as pointers. Take the following guidelines into account when working with optional values:

  • It is safe to not define them when writing the glTF if the desired value is the default one.
  • It is safe to expect that the optional values are not nil when reading a glTF.
  • When assigning values to optional properties it may be helpful to use these utility functions:
    • gltf.Index(1)
    • gltf.Float(0.5)

Reading a document

A gltf.Document can be decoded from any io.Reader by using gltf.Decoder:

resp, _ := http.Get("https://example.com/static/foo.gltf")
var doc gltf.Document
gltf.NewDecoder(resp.Body).Decode(&doc)
fmt.Print(doc.Asset)

When working with the file system it is more convenient to use gltf.Open as it automatically manages relative external buffers:

doc, _ := gltf.Open("./foo.gltf")
fmt.Print(doc.Asset)

In both cases the decoder will automatically detect if the file is JSON/ASCII (gltf) or Binary (glb) based on its content.

View on Github

Thank you for following this article.

Related videos:

Image Processing in Go - CEO, SP Digital

#go #golang #images 

10 Best Libraries for Manipulating Images in Go
Dexter  Goodwin

Dexter Goodwin

1661883420

Next-optimized-images Auto Optimizes Images Used in Next.js Projects

🌅 next-optimized-images

💡 Version 3 is coming! It introduces a complete rewrite with many new features and bugfixes. If you want to help developing and testing the upcoming major version, please check out the canary branch for installation instructions and more information about the new features. (RFC issue)


Automatically optimize images used in next.js projects (jpeg, png, svg, webp and gif).

Image sizes can often get reduced between 20-60%, but this is not the only thing next-optimized-images does:

  • Reduces image size by optimizing images during build
  • Improves loading speed by providing progressive images (for formats that support it)
  • Inlines small images to save HTTP requests and additional roundtrips
  • Adds a content hash to the file name so images can get cached on CDN level and in the browser for a long time
  • Same image URLs over multiple builds for long time caching
  • Provides query params for file-specific handling/settings
  • jpeg/png images can be converted to webp on the fly for an even smaller size
  • Provides the possibility to use SVG sprites for a better performance when using the same icons multiple times (e.g. in a list)
  • Can resize images or generate different placeholders while lazy loading images: low quality images, dominant colors or image outlines

Installation

npm install next-optimized-images

Node >= 8 is required for version 2. If you need to support older node versions, you can still use version 1 of next-optimized-images.

Enable the plugin in your Next.js configuration file:

// next.config.js
const withPlugins = require('next-compose-plugins');
const optimizedImages = require('next-optimized-images');

module.exports = withPlugins([
  [optimizedImages, {
    /* config for next-optimized-images */
  }],

  // your other plugins here

]);

See the configuration section for all available options.

:warning: From version 2 on, images won't get optimized out of the box anymore. You have to install the optimization packages you really need in addition to this plugin. This doesn't force you to download big optimization libraries you don't even use. Please check out the table of all optional optimization packages.

The example above uses next-compose-plugins for a cleaner API when using many plugins, see its readme for a more detailed example. next-optimized-images also works with the standard plugin api:

// next.config.js
const withOptimizedImages = require('next-optimized-images');

module.exports = withOptimizedImages({
  /* config for next-optimized-images */

  // your config for other plugins or the general next.js here...
});

Optimization Packages

Starting from version 2, you have to install the optimization packages you need in your project in addition to this plugin. next-optimized-images then detects all the supported packages and uses them.

So you only have to install these packages with npm, there is no additional step needed after that.

The following optimization packages are available and supported:

Optimization PackageDescriptionProject Link
imagemin-mozjpegOptimizes JPEG images.Link
imagemin-optipngOptimizes PNG images.Link
imagemin-pngquantAlternative for optimizing PNG images.Link
imagemin-gifsicleOptimizes GIF images.Link
imagemin-svgoOptimizes SVG images and icons.Link
svg-sprite-loaderAdds the possibility to use svg sprites for a better performance. Read the sprite section for more information.Link
webp-loaderOptimizes WebP images and can convert JPEG/PNG images to WebP on the fly (webp resource query).Link
lqip-loaderGenerates low quality image placeholders and can extract the dominant colors of an image (lqip resource query)Link
responsive-loaderCan resize images on the fly and create multiple versions of it for a srcset.
Important: You need to additionally install either jimp (node implementation, slower) or sharp (binary, faster)
Link
image-trace-loaderGenerates SVG image outlines which can be used as a placeholder while loading the original image (trace resource query).Link

Example: If you have JPG, PNG, and SVG images in your project, you would then need to run

npm install imagemin-mozjpeg imagemin-optipng imagemin-svgo

To install all optional packages, run:

npm install imagemin-mozjpeg imagemin-optipng imagemin-gifsicle imagemin-svgo svg-sprite-loader webp-loader lqip-loader responsive-loader jimp image-trace-loader

:warning: Please note that by default, images are only optimized for production builds, not development builds. However, this can get changed with the optimizeImagesInDev config.

:bulb: Depending on your build/deployment setup, it is also possibile to install these as devDependencies. Just make sure that the packages are available when you build your project.

:information_source: Since version 2.5, ico files are also optionally supported but need to be enabled in the handleImages config.

Usage

You can now import or require your images directly in your react components:

import React from 'react';

export default () => (
  <div>
    <img src={require('./images/my-image.jpg')} />
    <img src={require('./images/my-small-image.png')} />
    <img src={require('./images/my-icon.svg')} />
  </div>
);

/**
 * Results in:
 *
 * <div>
 *   <img src="/_next/static/images/my-image-5216de428a8e8bd01a4aa3673d2d1391.jpg" />
 *   <img src="data:image/png;base64,..." />
 *   <img src="/_next/static/images/my-icon-572812a2b04ed76f93f05bf57563c35d.svg" />
 * </div>
 */

Please be aware that images only get optimized in production by default to reduce the build time in your development environment.

If you are using CSS modules, this package also detects images and optimized them in url() values in your css/sass/less files:

.Header {
  background-image: url('./images/my-image.jpg');
}

/**
 * Results in:
 *
 * .Header {
 *   background-image: url('/_next/static/images/my-image-5216de428a8e8bd01a4aa3673d2d1391.jpg');
 * }
 */

If the file is below the limit for inlining images, the require(...) will return a base64 data-uri (data:image/jpeg;base64,...).

Otherwise, next-optimized-images will copy your image into the static folder of next.js and the require(...) returns the path to your image in this case (/_next/static/images/my-image-5216de428a8e8bd01a4aa3673d2d1391.jpg).

You can use both variants directly on an image in the src attribute or in your CSS file inside an url() value.

Query params

If you are using flow or eslint-plugin-import and are experiencing some issues with query params, check out the solution posted by @eleith.

There are some cases where you don't want to reference a file or get a base64 data-uri but you actually want to include the raw file directly into your HTML. Especially for SVGs because you can't style them with CSS if they are in an src attribute on an image.

So there are additional options you can specify as query params when you import the images.

  • ?include: Include the raw file directly (useful for SVG icons)
  • ?webp: Convert a JPEG/PNG image to WebP on the fly
  • ?inline: Force inlining an image (data-uri)
  • ?url: Force an URL for a small image (instead of data-uri)
  • ?original: Use the original image and do not optimize it
  • ?lqip: Generate a low quality image placeholder
  • ?lqip-colors: Extract the dominant colors of an image
  • ?trace: Use traced outlines as loading placeholder
  • ?resize: Resize an image
  • ?sprite: Use SVG sprites

?include

The image will now directly be included in your HTML without a data-uri or a reference to your file.

As described above, this is useful for SVGs so you can style them with CSS.

import React from 'react';

export default () => (
  <div dangerouslySetInnerHTML={{__html: require('./images/my-icon.svg?include')}} />
);

/**
 * Results in:
 *
 * <div>
 *   <svg width="16" height="16" xmlns="http://www.w3.org/2000/svg">
 *     <path d="M8 0C3.589 0 0 3.589 0 8s3.589 ..." style="filled-opacity:1" fill-rule="evenodd">
 *     </path>
 *   </svg>
 * </div>
 */

The image will still get optimized, even if it is directly included in your content (but by default only in production).

?webp

Requires the optional optimization package webp-loader (npm install webp-loader)

WebP is an even better and smaller image format but it is still not that common yet and developers often only receive jpeg/png images.

If this ?webp query parameter is specified, next-optimized-images automatically converts a JPEG/PNG image to the new WebP format.

For browsers that don't yet support WebP, you can also provide a fallback using the <picture> tag:

import React from 'react';

export default () => (
  <picture>
    <source srcSet={require('./images/my-image.jpg?webp')} type="image/webp" />
    <source srcSet={require('./images/my-image.jpg')} type="image/jpeg" />
    <img src={require('./images/my-image.jpg')} />
  </picture>
);

/**
 * Results in:
 * <picture>
 *   <source srcset="/_next/static/images/my-image-d6816ecc28862cf6f725b29b1d6aab5e.jpg.webp" type="image/webp" />
 *   <source srcset="/_next/static/images/my-image-5216de428a8e8bd01a4aa3673d2d1391.jpg" type="image/jpeg" />
 *   <img src="/_next/static/images/my-image-5216de428a8e8bd01a4aa3673d2d1391.jpg" />
 * </picture>
 */

?inline

You can specify a limit for inlining images which will include it as a data-uri directly in your content instead of referencing a file if the file size is below that limit.

You usually don't want to specify a too high limit but there may be cases where you still want to inline larger images.

In this case, you don't have to set the global limit to a higher value but you can add an exception for a single image using the ?inline query options.

import React from 'react';

export default () => (
  <img src={require('./images/my-image.jpg?inline')} />
);

/**
 * Results in:
 *
 * <img src="data:image/png;base64,..." />
 *
 * Even if the image size is above the defined limit.
 */

The inlining will only get applied to exactly this import, so if you import the image a second time without the ?inline option, it will then get normally referenced as a file if it is above your limit.

?url

When you have an image smaller than your defined limit for inlining, it normally gets inlined automatically. If you don't want a specific small file to get inlined, you can use the ?url query param to always get back an image URL, regardless of the inline limit.

If you are using this option a lot, it could also make sense to disable the inlining completely and use the ?inline param for single files.

import React from 'react';

export default () => (
  <img src={require('./images/my-image.jpg?url')} />
);

/**
 * Results in:
 *
 * <img src="/_next/static/images/my-image-5216de428a8e8bd01a4aa3673d2d1391.jpg" />
 *
 * Even if the image size is below the defined inlining limit.
 */

The inlining will only get disabled for exactly this import, so if you import the image a second time without the ?url option, it will then get inlined again if it is below your limit.

?original

The image won't get optimized and used as it is. It makes sense to use this query param if you know an image already got optimized (e.g. during export) so it doesn't get optimized again a second time.

import React from 'react';

export default () => (
  <img src={require('./images/my-image.jpg?original')} />
);

This can also be combined with the ?url or ?inline resource query (e.g. ?original&inline).

?lqip

Requires the optional package lqip-loader (npm install lqip-loader)

When using this resource query, a very small (about 10x7 pixel) image gets created. You can then display this image as a placeholder until the real (big) image has loaded.

You will normally stretch this tiny image to the same size as the real image is, like medium.com does. To make the stretched image look better in chrome, check out this solution and add a blur filter to your image.

import React from 'react';

export default () => (
  <img src={require('./images/my-image.jpg?lqip')} />
);

/**
 * Replaces the src with a tiny image in base64.
 */

?lqip-colors

Requires the optional package lqip-loader (npm install lqip-loader)

This resource query returns you an array with hex values of the dominant colors of an image. You can also use this as a placeholder until the real image has loaded (e.g. as a background) like the Google Picture Search does.

The number of colors returned can vary and depends on how many different colors your image has.

import React from 'react';

export default () => (
  <div style={{ backgroundColor: require('./images/my-image.jpg?lqip-colors')[0] }}>...</div>
);

/**
 * require('./images/my-image.jpg?lqip-colors')
 *
 * returns for example
 *
 * ['#0e648d', '#5f94b5', '#a7bbcb', '#223240', '#a4c3dc', '#1b6c9c']
 */

?trace

Requires the optional package image-trace-loader (npm install image-trace-loader)

With the ?trace resource query, you can generate SVG image outlines which can be used as a placeholder while loading the original image.

import React from 'react';
import MyImage from './images/my-image.jpg?trace';

export default () => (
  <div>
    <img src={MyImage.trace} />   {/* <-- SVG trace */}
    <img src={MyImage.src} />     {/* <-- Normal image which you want to lazy load */}
  </div>
);

/**
 * Results in:
 *
 * <div>
 *  <img src="data:image/svg+xml,...">
 *  <img src="/_next/static/images/image-trace-85bf5c58ce3d91fbbf54aa03c44ab747.jpg">
 * </div>
 */

require('./images/my-image.jpg?trace') returns an object containing the trace (trace) as an inlined SVG and the normal image (src) which also gets optimized.

The trace will have exactly the same width and height as your normal image.

Options for the loader can be set in the plugin configuration.

?resize

Requires the optional package responsive-loader (npm install responsive-loader) and either jimp (node implementation, slower) or sharp (binary, faster)

After the ?resize resource query, you can add any other query of the responsive-loader which allows you to resize images and create whole source sets.

import React from 'react';

const oneSize = require('./images/my-image.jpg?resize&size=300');
const multipleSizes = require('./images/my-image.jpg?resize&sizes[]=300&sizes[]=600&sizes[]=1000');

export default () => (
  <div>
    {/* Single image: */}
    <img src={oneSize.src} />

    {/* Source set with multiple sizes: */}
    <img srcSet={multipleSizes.srcSet} src={multipleSizes.src} />
  </div>
);

If only the size or sizes param is used, the ?resize param can also be omitted (e.g. my-image.jpg?size=300). But it is required for all other parameters of responsive-loader.

You can also set global configs in the responsive property (in the next.config.js file) and define, for example, default sizes which will get generated when you don't specify one for an image (e.g. only my-image.jpg?resize).

?sprite

Requires the optional optimization packages imagemin-svgo and svg-sprite-loader (npm install imagemin-svgo svg-sprite-loader)

If you need to style or animate your SVGs ?include might be the wrong option, because that ends up in a lot of DOM elements, especially when using the SVG in list-items etc. In that case, you can use ?sprite which uses svg-sprite-loader to render and inject an SVG sprite in the page automatically.

import React from 'react';
import MyIcon from './icons/my-icon.svg?sprite';

export default () => (
  <div>
    my page..
    <MyIcon />
  </div>
);

All props passed to the imported sprite will get applied to the <svg> element, so you can add a class normally with <MyIcon className="icon-class" />.

The svg-sprite-loader object also gets exposed if you want to build your own component:

import React from 'react';
import icon from './icons/icon.svg?sprite';

export default () => (
  <div>
    my page..
    <svg viewBox={icon.viewBox}>
      <use xlinkHref={`#${icon.id}`} />
    </svg>
  </div>
);

To also make this work for server-side rendering, you need to add these changes to your _document.jsx file (read here if you don't have this file yet):

// ./pages/_document.js
import Document, { Head, Main, NextScript } from 'next/document';
import sprite from 'svg-sprite-loader/runtime/sprite.build';

export default class MyDocument extends Document {
  static async getInitialProps(ctx) {
    const initialProps = await Document.getInitialProps(ctx);
    const spriteContent = sprite.stringify();

    return {
      spriteContent,
      ...initialProps,
    };
  }

  render() {
    return (
      <html>
        <Head>{/* your head if needed */}</Head>
        <body>
          <div dangerouslySetInnerHTML={{ __html: this.props.spriteContent }} />
          <Main />
          <NextScript />
        </body>
      </html>
    );
  }
}

Configuration

This plugin uses img-loader under the hood which is based on mozjpeg, optipng, gifsicle and svgo.

The default options for these optimizers should be enough in most cases, but you can overwrite every available option if you want to.

handleImages

Type: string[]
Default: ['jpeg', 'png', 'svg', 'webp', 'gif']

next-optimized-images registers the webpack loader for all these file types. If you don't want one of these handled by next-optimized-images because you, for example, have another plugin or custom loader rule, simply remove it from the array.

Please note that an image being handled does not mean it also gets automatically optimized. The required optimization package for that image also has to be installed. Please read the optimization packages section for more information.

If an image gets handled but not optimized, it means that the original image will get used and copied for the build.

:information_source: Since version 2.5, ico files are also supported but for backwards compatibility, they need to be manually enabled. By adding 'ico' to the handleImages array, the plugin will also handle ico files.

inlineImageLimit

Type: number
Default: 8192

Smaller files will get inlined with a data-uri by url-loader. This number defines the maximum file size (in bytes) for images to get inlined. If an image is bigger, it will get copied to the static folder of next.

Images will get optimized in both cases.

To completely disable image inlining, set this value to -1. You will then always get back an image URL.

imagesFolder

Type: string
Default: 'images'

Folder name inside /static/ in which the images will get copied to during build.

imagesPublicPath

Type: string
Default: `/_next/static/${imagesFolder}/`

The public path that should be used for image URLs. This can be used to serve the optimized image from a cloud storage service like S3.

From version 2 on, next-optimized-images uses the assetPrefx config of next.js by default, but you can overwrite it with imagesPublicPath specially for images.

imagesOutputPath

Type: string
Default: `static/${imagesFolder}/`

The output path that should be used for images. This can be used to have a custom output folder.

imagesName

Type: string
Default: '[name]-[hash].[ext]'

The filename of the optimized images. Make sure you keep the [hash] part so they receive a new filename if the content changes.

removeOriginalExtension

Type: boolean
Default: false

When images converted to WebP on the fly, .webp was append to the filename. For example, test.png became test.png.webp. If you want to have only one filename extension like test.webp, you can set this option to true.

optimizeImagesInDev

Type: boolean
Default: false

For faster development builds and HMR, images will not get optimized by default when running in development mode. In production, images will always get optimized, regardless of this setting.

mozjpeg

Requires the optional optimization package imagemin-mozjpeg (npm install imagemin-mozjpeg)

Type: object
Default: {}

mozjpeg is used for optimizing jpeg images. You can specify the options for it here. The default options of mozjpeg are used if you omit this option.

optipng

Requires the optional optimization package imagemin-optipng (npm install imagemin-optipng)

Type: object
Default: {}

optipng is used for optimizing png images by default. You can specify the options for it here. The default options of optipng are used if you omit this option.

pngquant

Requires the optional optimization package imagemin-pngquant (npm install imagemin-pngquant)

Type: object
Default: {}

pngquant is an alternative way for optimizing png images. The default options of pngquant are used if you omit this option.

gifsicle

Requires the optional optimization package imagemin-gifsicle (npm install imagemin-gifsicle)

Type: object
Default:

{
    interlaced: true,
    optimizationLevel: 3,
}

gifsicle is used for optimizing gif images. You can specify the options for it here. The default options of gifsicle are used if you omit this option.

svgo

Requires the optional optimization package imagemin-svgo (npm install imagemin-svgo)

Type: object
Default: {}

svgo is used for optimizing svg images and icons. You can specify the options for it here. The default options of svgo are used if you omit this option.

Single svgo plugins can get disabled/enabled in the plugins array:

{
  svgo: {
    plugins: [
      { removeComments: false }
    ]
  }
}

svgSpriteLoader

Requires the optional optimization packages imagemin-svgo and svg-sprite-loader (npm install imagemin-svgo svg-sprite-loader)

Type: object
Default:

{
  runtimeGenerator: require.resolve(path.resolve('node_modules', 'next-optimized-images', 'svg-runtime-generator.js')),
}

When using the svg sprite option, svg-sprite-loader gets used internally. You can overwrite the configuration passed to this loader here.

webp

Requires the optional optimization package webp-loader (npm install webp-loader)

Type: object
Default: {}

imagemin-webp is used for optimizing webp images and converting other formats to webp. You can specify the options for it here. The default options of imagemin-webp are used if you omit this option.

imageTrace

Requires the optional package image-trace-loader (npm install image-trace-loader)

Type: object
Default: {}

When using image-trace-loader for the ?trace resource query, you can define all options for the image trace loader in this object. The default options of image-trace-loader are used if you omit this option.

responsive

Requires the optional optimization package responsive-loader (npm install responsive-loader)

Type: object
Default: {}

The configuration for the responsive-loader can be defined here.

defaultImageLoader

Requires the optional optimization package responsive-loader (npm install responsive-loader)

Type: string
Default: 'img-loader'

By default, img-loader handles most of the requests. However, if you use the responsive-loader a lot and don't want to add the ?resize query param to every require, you can set this value to 'responsive-loader'.

After that, responsive-loader will handle all JPEG and PNG images per default, even without an additional query param. Just be aware that you can't use any of the query params next-optimized-images provides anymore on these images because the request just gets forwarded and not modified anymore. All other formats (SVG, WEBP and GIF) still work as before with the img-loader and so have all query params available.

optimizeImages

Type: boolean
Default: true

If you don't have any optimization package installed, no image will get optimized. In this case, a warning gets written to the console during build to inform you about a possible misconfiguration. If this config is intended and you indeed don't want the images to be optimized, you can set this value to false and you won't get the warning anymore.

Example

The options specified here are the default values.

So if they are good enough for your use-case, you don't have to specify them to have a shorter and cleaner next.config.js file.

// next.config.js
const withPlugins = require('next-compose-plugins');
const optimizedImages = require('next-optimized-images');

module.exports = withPlugins([
  [optimizedImages, {
    // these are the default values so you don't have to provide them if they are good enough for your use-case.
    // but you can overwrite them here with any valid value you want.
    inlineImageLimit: 8192,
    imagesFolder: 'images',
    imagesName: '[name]-[hash].[ext]',
    handleImages: ['jpeg', 'png', 'svg', 'webp', 'gif'],
    removeOriginalExtension: false,
    optimizeImages: true,
    optimizeImagesInDev: false,
    mozjpeg: {
      quality: 80,
    },
    optipng: {
      optimizationLevel: 3,
    },
    pngquant: false,
    gifsicle: {
      interlaced: true,
      optimizationLevel: 3,
    },
    svgo: {
      // enable/disable svgo plugins here
    },
    webp: {
      preset: 'default',
      quality: 75,
    },
  }],
]);

See also

  • next-images if you just want images and not optimize them
  • next-compose-plugins for a cleaner plugins API when you have many plugins in your next.config.js file
  • next-plugins for a list of official and community made plugins

Download Details:

Author: cyrilwanner
Source Code: https://github.com/cyrilwanner/next-optimized-images 
License: MIT 

#javascript #next #images #react 

Next-optimized-images Auto Optimizes Images Used in Next.js Projects

A Flutter Plugin for Picking Images From The Image Library

Description

A Flutter plugin for iOS and Android for picking images from the image library, and taking new pictures with the camera. - Use with love

SetUp

Add the following to your "gradle.properties" file:

android.useAndroidX=true
android.enableJetifier=true

Make sure you set the compileSdkVersion in your "android/app/build.gradle" file to 31:

android {
  compileSdkVersion 31
  ...
}

Make sure you set the minSdkVersion in your "android/app/build.gradle" file to 21:

android {
  minSdkVersion 21
  ...
}

Set android.permission

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

Screenshots

select image: screanshott show skeleton: screanshott show folders: screanshott show camera: screanshott

Getting started

add package URL in your pubspec file.

Usage

Just Navigator push to FBImagePicker

TextButton(
        child: const Text(
        'FB Image Picker',
            ),
            onPressed: () {
                Navigator.push(
                    context,
                    MaterialPageRoute(
                        builder: (context) => FBImagePicker(
                            btnColor: Colors.blue,
                            title: 'FB Image Picker',
                            afterNextBTN: () {
                                Navigator.pop(context);
                            },
                            backOnSystem: () {
                                Navigator.pop(context);
                            },
                            onComplete: (final imagePickerList) {
                            setState(() {
                            imageList = imagePickerList;
                        });
                    },
                )),
            );
        })

All params

        backgroundColor: Colors.white,
        btnColor: Colors.blue,
        textStyle: TextStyle(color: Colors.black),
        title: 'FB Image Picker',
        enableCamera: true,
        afterNextBTN: () {},
        backOnSystem: () {},
        onComplete: (imagePickerList) {},

Additional information

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add fb_image_picker

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  fb_image_picker: ^0.0.1

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:fb_image_picker/fb_image_picker.dart'; 

example/main.dart

import 'dart:io';

import 'package:fb_image_picker/fb_image_picker.dart';
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'FB Image Picker',
      home: Example(),
    );
  }
}

class Example extends StatefulWidget {
  const Example({Key? key}) : super(key: key);

  @override
  State<Example> createState() => _ExampleState();
}

class _ExampleState extends State<Example> {
  List imageList = [];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: SingleChildScrollView(
            child: Container(
      width: MediaQuery.of(context).size.width,
      // height: MediaQuery.of(context).size.height,
      color: Colors.black.withOpacity(0.9),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          const Padding(
              padding: EdgeInsets.only(top: 60, bottom: 20),
              child: Text(
                'FB Image Picker',
                style: TextStyle(color: Colors.white, fontSize: 18),
              )),
          Padding(
              padding: const EdgeInsets.only(bottom: 15),
              child: TextButton(
                  child: const Text(
                    'FB Image Picker',
                    style: TextStyle(color: Colors.green, fontSize: 16),
                  ),
                  onPressed: () {
                    Navigator.push(
                      context,
                      MaterialPageRoute(
                          builder: (context) => FBImagePicker(
                                btnColor: Colors.blue,
                                title: 'FB Image Picker',
                                afterNextBTN: () {
                                  Navigator.pop(context);
                                },
                                backOnSystem: () {
                                  Navigator.pop(context);
                                },
                                onComplete: (final imagePickerList) {
                                  setState(() {
                                    imageList = imagePickerList;
                                  });
                                },
                              )),
                    );
                  })),
          if (imageList.length == 1)
            Image.file(
              File(imageList[0]),
              fit: BoxFit.cover,
            ),
          if (imageList.length == 2)
            Column(
              children: [
                Image.file(
                  File(imageList[0]),
                  fit: BoxFit.cover,
                ),
                Image.file(
                  File(imageList[1]),
                  fit: BoxFit.cover,
                ),
              ],
            )
        ],
      ),
    )));
  }
} 

Download Details:

Author: 

Source Code: https://pub.dev/packages/fb_image_picker

#flutter #images #picker 

A Flutter Plugin for Picking Images From The Image Library
Monty  Boehm

Monty Boehm

1660311060

MicroscopyLabels.jl: Easily Embed annotations in Your Microscopy Image

MicroscopyLabels.jl

Easily embed sane annotations in your microscopy images.

The reality is that you don't need this library, but it can make your life easier if you have microscopy images and want to quickly annotate things. It aims to be sane over being customizable because really it's just a wrapper around FreeTypeAbstraction.jl.

labeled_image.gif

Installation

This package is available through the Julia registry and can be installed via the Julia REPL:

] add MicroscopyLabels

Download Details:

Author: tlnagy
Source Code: https://github.com/tlnagy/MicroscopyLabels.jl 
License: MIT license

#julia #images 

MicroscopyLabels.jl: Easily Embed annotations in Your Microscopy Image
Monty  Boehm

Monty Boehm

1660067520

LeafAreaIndex.jl: Calculate Leaf Area Index from Hemisperical Images

LeafAreaIndex 

Tools to work with hemispherical pictures for the determination of Leaf Area Index (LAI).

Quick introduction

Install the package through

Pkg.clone("https://github.com/ETC-UA/LeafAreaIndex.jl")

The basic type used by this package is a PolarImage. You construct a PolarImage from a CameraLens type and an Image (or in general, an AbstractMatrix). Note that for LAI calculations typically only the blue channel of the image is used.

You can load the image eg. with the Images package:

using Images
img = imread("image.jpg")
imgblue = blue(img) #take the blue channel

or in case you have the raw image from the camera, we provide a more accurate, dedicated function to extract the pixels from the blue channel (using dcraw under the hood):

using LeafAreaIndex
imgblue = rawblueread("image.NEF")

Because the mapping of pixels on the image to coordinates in the scene is dependent on your camera setup, you must construct a configuration object with this information. A CameraLens type is constructed given an image size, the coordinates of the lens center and the (inverse) projection function. The projection function maps polar distance ρ [in pixels] on the image to the zenith angle θ [in radians] of the scene and is usually not linear. This project function depends on the specific (fish-eye) used and is usually polynomial approximated up to 2nd order as f(ρ/ρmax) = a₁θ + a₂θ² with ρmax the maximum visible radius. More general you can submit a vector A with the polynomial coefficients. The maximum radius ρmax and the lens center depends on the combination of camera together with the lens (and the image size depends obviously on the camera).

using LeafAreaIndex
mycameralens = CameraLens( (height, width), (centeri, centerj), ρmax, A)

The basic PolarImage type is then constructed:

polarimg = PolarImage(imgblue, mycameralens)

The first processing step is automatic thresholding (default method Ridler Calvard):

thresh = threshold(polarimg)

In the second step the (effective) LAI is estimated through the inversion model. The default method assumes an ellipsoidal leave angle distribution and uses a non-linear optimization method.

LAIe = inverse(polarimg, thresh)

Finally, the clumping factor can be estimated with the method of Lang Xiang (default with 45ᵒ segments in full view angle):

clump = langxiang(polarimg, thresh)

With clumping correction we obtain LAI = LAIe / clump.

Further methods

For images taken (always vertically upwards) on a domain with a slope of eg 10ᵒ and sloping downward to the East, you must include this information in your PolarImage with the Slope(inclination, direction) function:

myslope = SlopeParams(10/180*pi, pi/2)
polarimg = PolarImage(imgblue, mycameralens, myslope)

For downward taken (crop) images, create a mask to cut out the photographer's shoes and use the RedMax() method instead of thresholding to separate soil from (green) plant material

mymask = MaskParams(pi/3, -2*pi/3, -pi/3)
polarimg = PolarImage(imgblue, mycameralens, mymask)
LAIe = inverse(polarimg, RedMax())

Besides the default Ridler Calvard method, two more automatic thresholdingmethods Edge Detection and Minimum algorithm can be used:

thresh  = threshold(polarimg, RidlerCalvard())
thresh2 = threshold(polarimg, EdgeDetection())
thresh3 = threshold(polarimg, MinimumThreshold())

Further LAI estimation methods for the inversion model are available: * The EllipsLUT also assumes an ellipsoidal leaf angle distribution, but uses a Lookup Table approach instead of optimization approach. * The Zenith57 method uses a ring around the view angle of 57ᵒ (1 rad) where the ALIA influence is minimal; * The Miller method integrates several zenith rings assuming a constant leaf angle; and * The Lang method uses a first order regression on the Miller method.

LAI  = inverse(polarimg, thresh, EllipsOpt())
LAI2 = inverse(polarimg, thresh, EllipsLUT())
LAI3 = inverse(polarimg, thresh, Zenith57())
LAI4 = inverse(polarimg, thresh, Miller())
LAI5 = inverse(polarimg, thresh, Lang())

For the clumping factor, besides the method from Lang & Xiang, also the (experimental) method from Chen & Chilar is available:

clump2 = chencihlar(polarimg, thresh, 0, pi/2)

Lower level methods

Under the hood several lower level methods are used to access pixels and calculated gapfractions. We suggest to look at the code for their definition and usage.

To access the pixels in a particular zenith range, pixels(polarimg, pi/6, pi/3) will return a vector with pixels quickly, sorted by increasing ρ (and then by polar angles ϕ for identical ρ). A shortcut pixels(polarimg) is translated to pixels(polarimg, 0, pi/2).

The segments function can further split these ring pixels in n segments (eg. for clumping calculation). It returns a vector with n elements, each again a vector with the segment pixels.

For the gapfraction, we suggest (see online documentation) to use the contact frequencies $K(\theta_V) = -\ln[T(\theta_v)] \cos\theta_V$ for LAI inversion calculations, with $T$ the gapfraction and $\theta_V$ the view angle. The input N determines the number of rings between view angles θ1 and θ2 for a polar image with a certain threshold. The function returns a vector with angle edges of the rings, the weighted average midpoint angle for each ring and the contact frequency for each ring.

θedges, θmid, K = contactfreqs(polimg, θ1, θ2, N, thresh)

In case of problems or suggestion, don't hesitate to submit an issue through the issue tracker or code suggestions through a pull request.

Documentation

View the full documentation on (https://etc-ua.github.io/LeafAreaIndex.jl).

Download Details:

Author: ETC-UA
Source Code: https://github.com/ETC-UA/LeafAreaIndex.jl 
License: View license

#julia #images 

LeafAreaIndex.jl: Calculate Leaf Area Index from Hemisperical Images

Flutter Plugin to Render & Show PDF Pages As Images

PDFx

Flutter Render & show PDF documents on Web, MacOs 10.11+, Android 5.0+, iOS and Windows.

Includes 2 api:

  • renderer Work with Pdf document, pages, render page to image
  • viewer Set of flutter widgets & controllers for show renderer result

Showcase

PdfViewPinchPdfView

Getting Started

In your flutter project add the dependency:

flutter pub add pdfx

For web run tool for automatically add pdfjs library (CDN) in index.html:

flutter pub run pdfx:install_web

For windows run tool automatically add override for pdfium version property in CMakeLists.txt file:

flutter pub run pdfx:install_windows

Usage example

import 'package:pdfx/pdfx.dart';

final pdfPinchController = PdfControllerPinch(
  document: PdfDocument.openAsset('assets/sample.pdf'),
);

// Pdf view with re-render pdf texture on zoom (not loose quality on zoom)
// Not supported on windows
PdfViewPinch(
  controller: pdfPinchController,
);

//-- or --//

final pdfController = PdfController(
  document: PdfDocument.openAsset('assets/sample.pdf'),
);

// Simple Pdf view with one render of page (loose quality on zoom)
PdfView(
  controller: pdfController,
);

Viewer Api

PdfController & PdfControllerPinch

ParameterDescriptionDefault
documentThe document to be displayed-
initialPageThe page to show when first creating the [PdfView]1
viewportFractionThe fraction of the viewport that each page should occupy.1.0

PdfView & PdfViewPinch

ParameterDescriptionPdfViewPinch / PdfView
controllerPages control. See page control and additional pdf info+ / +
onPageChangedCalled whenever the page in the center of the viewport changes. See Document callbacks+ / +
onDocumentLoadedCalled when a document is loaded. See Document callbacks+ / +
onDocumentErrorCalled when a document loading error. Exception is passed in the attributes+ / +
buildersSet of pdf view builders. See Custom builders+ / +
scrollDirectionPage turning direction+ / +
rendererCustom PdfRenderer options. See custom renderer options- / +
pageSnappingSet to false for mouse wheel scroll on web- / +
physicsHow the widgets should respond to user input- / +
paddingPadding for the every page.+ / -

PdfViewBuilders & PdfViewPinchBuilders

ParameterDescriptionPdfViewPinchBuilders / PdfViewBuilders
optionsAdditional options for builder+ / +
documentLoaderBuilderWidget showing when pdf document loading+ / +
pageLoaderBuilderWidget showing when pdf page loading+ / +
errorBuilderShow document loading error message+ / +
builderRoot view builder for animate pdf loading state+ / +
pageBuilderCallback called to render a widget for each page. See custom page builder- / +

Additional examples

Open another document

pdfController.openDocument(PdfDocument.openAsset('assets/sample.pdf'));

Page control:

// Jump to specified page
pdfController.jumpTo(3);

// Animate to specified page
_pdfController.animateToPage(3, duration: Duration(milliseconds: 250), curve: Curves.ease);

// Animate to next page 
_pdfController.nextPage(duration: Duration(milliseconds: 250), curve: Curves.easeIn);

// Animate to previous page
_pdfController.previousPage(duration: Duration(milliseconds: 250), curve: Curves.easeOut);

Additional pdf info:

// Actual showed page
pdfController.page;

// Count of all pages in document
pdfController.pagesCount;

Document callbacks

PdfView(
  controller: pdfController,
  onDocumentLoaded: (document) {},
  onPageChanged: (page) {},
);

Show actual page number & all pages count

PdfPageNumber(
  controller: _pdfController,
  // When `loadingState != PdfLoadingState.success`  `pagesCount` equals null_
  builder: (_, state, loadingState, pagesCount) => Container(
    alignment: Alignment.center,
    child: Text(
      '$page/${pagesCount ?? 0}',
      style: const TextStyle(fontSize: 22),
    ),
  ),
)

Custom renderer options

PdfView(
  controller: pdfController,
  renderer: (PdfPage page) => page.render(
    width: page.width * 2,
    height: page.height * 2,
    format: PdfPageFormat.JPEG,
    backgroundColor: '#FFFFFF',
  ),
);

Custom builders

// Need static methods for builders arguments
class SomeWidget {
  static Widget builder(
    BuildContext context,
    PdfViewPinchBuilders builders,
    PdfLoadingState state,
    WidgetBuilder loadedBuilder,
    PdfDocument? document,
    Exception? loadingError,
  ) {
    final Widget content = () {
      switch (state) {
        case PdfLoadingState.loading:
          return KeyedSubtree(
            key: const Key('pdfx.root.loading'),
            child: builders.documentLoaderBuilder?.call(context) ??
                const SizedBox(),
          );
        case PdfLoadingState.error:
          return KeyedSubtree(
            key: const Key('pdfx.root.error'),
            child: builders.errorBuilder?.call(context, loadingError!) ??
                Center(child: Text(loadingError.toString())),
          );
        case PdfLoadingState.success:
          return KeyedSubtree(
            key: Key('pdfx.root.success.${document!.id}'),
            child: loadedBuilder(context),
          );
      }
    }();

    final defaultBuilder =
        builders as PdfViewPinchBuilders<DefaultBuilderOptions>;
    final options = defaultBuilder.options;

    return AnimatedSwitcher(
      duration: options.loaderSwitchDuration,
      transitionBuilder: options.transitionBuilder,
      child: content,
    );
  }

  static Widget transitionBuilder(Widget child, Animation<double> animation) =>
      FadeTransition(opacity: animation, child: child);

  static PhotoViewGalleryPageOptions pageBuilder(
    BuildContext context,
    Future<PdfPageImage> pageImage,
    int index,
    PdfDocument document,
  ) =>
      PhotoViewGalleryPageOptions(
        imageProvider: PdfPageImageProvider(
          pageImage,
          index,
          document.id,
        ),
        minScale: PhotoViewComputedScale.contained * 1,
        maxScale: PhotoViewComputedScale.contained * 3.0,
        initialScale: PhotoViewComputedScale.contained * 1.0,
        heroAttributes: PhotoViewHeroAttributes(tag: '${document.id}-$index'),
      );
}

PdfViewPinch(
  controller: pdfPinchController,
  builders: PdfViewPinchBuilders<DefaultBuilderOptions>(
    options: const DefaultBuilderOptions(
      loaderSwitchDuration: const Duration(seconds: 1),
      transitionBuilder: SomeWidget.transitionBuilder,
    ),
    documentLoaderBuilder: (_) =>
        const Center(child: CircularProgressIndicator()),
    pageLoaderBuilder: (_) =>
        const Center(child: CircularProgressIndicator()),
    errorBuilder: (_, error) => Center(child: Text(error.toString())),
    builder: SomeWidget.builder,
  ),
)

PdfView(
  controller: pdfController,
  builders: PdfViewBuilders<DefaultBuilderOptions>(
    // All from `PdfViewPinch` and:
    pageBuilder: SomeWidget.pageBuilder,
  ),
);

Renderer Api

PdfDocument

ParameterDescriptionDefault
sourceNameNeeded for toString method. Contains a method for opening a document (file, data or asset)-
idDocument unique id. Generated when opening document.-
pagesCountAll pages count in document. Starts from 1.-
isClosedIs the document closed-

Local document open:

// From assets (Android, Ios, MacOs, Web)
final document = await PdfDocument.openAsset('assets/sample.pdf')

// From file (Android, Ios, MacOs)
final document = await PdfDocument.openFile('path/to/file/on/device')

// From data (Android, Ios, MacOs, Web)
final document = await PdfDocument.openData((FutureOr<Uint8List>) data)

Network document open:

Install [network_file] package (supports all platforms):

flutter pub add internet_file

And use it

import 'package:internet_file/internet_file.dart';

PdfDocument.openData(InternetFile.get('https://github.com/ScerIO/packages.flutter/raw/fd0c92ac83ee355255acb306251b1adfeb2f2fd6/packages/native_pdf_renderer/example/assets/sample.pdf'))

Open page:

final page = document.getPage(pageNumber); // Starts from 1

Close document:

document.close();

PdfPage

ParameterDescriptionDefault
documentParent documentParent
idPage unique id. Needed for rendering and closing page. Generated when opening page.-
widthPage source width in pixels, int-
heightPage source height in pixels, int-
isClosedIs the page closedfalse

Render image:

final pageImage = page.render(
  // rendered image width resolution, required
  width: page.width * 2,
  // rendered image height resolution, required
  height: page.height * 2,

  // Rendered image compression format, also can be PNG, WEBP*
  // Optional, default: PdfPageImageFormat.PNG
  // Web not supported
  format: PdfPageImageFormat.JPEG,

  // Image background fill color for JPEG
  // Optional, default '#ffffff'
  // Web not supported
  backgroundColor: '#ffffff',

  // Crop rect in image for render
  // Optional, default null
  // Web not supported
  cropRect: Rect.fromLTRB(left, top, right, bottom),
);

PdfPageImage

ParameterDescriptionDefault
idPage unique id. Needed for rendering and closing page. Generated when render page.-
pageNumberPage number. The first page is 1.-
widthWidth of the rendered area in pixels, int-
heightHeight of the rendered area in pixels, int-
bytesRendered image result, Uint8List-
formatRendered image compression format, for web always PNGPdfPageImageFormat.PNG

Close page:
Before open new page android asks to close the past.
If this is not done, the application may crash with an error

page.close();

* PdfPageImageFormat.WEBP support only on android

Rendering additional info

On Web

This plugin uses the PDF.js

On Android

This plugin uses the Android native PdfRenderer

On Ios & MacOs

This plugin uses the iOS & MacOs native CGPDFPage

On Windows

This plugin uses PDFium

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add pdfx

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  pdfx: ^2.2.0

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:pdfx/pdfx.dart'; 

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:pdfx_example/pinch.dart';
import 'package:pdfx_example/simple.dart';

import 'package:universal_platform/universal_platform.dart';

void main() => runApp(const MyApp());

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) => MaterialApp(
        debugShowCheckedModeBanner: false,
        theme: ThemeData(primaryColor: Colors.white),
        darkTheme: ThemeData.dark(),
        home: UniversalPlatform.isWindows
            ? const SimplePage()
            : const PinchPage(),
      );
} 

Download Details:

Author: ScerIO

Source Code: https://github.com/ScerIO/packages.flutter/tree/main/packages/pdfx

#flutter #pdf #images 

Flutter Plugin to Render & Show PDF Pages As Images

A Flutter Plugin That Retrieves Images and Videos From Mobile

Photo Gallery

A Flutter plugin that retrieves images and videos from mobile native gallery.

Installation

First, add photo_gallery as a dependency in your pubspec.yaml file.

iOS

Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:

NSPhotoLibraryUsageDescription - describe why your app needs permission for the photo library. This is called Privacy - Photo Library Usage Description in the visual editor.

<key>NSPhotoLibraryUsageDescription</key>
<string>Example usage description</string>

Android

Add the following permissions to your AndroidManifest.xml, located in <project root>/android/app/src/main/AndroidManifest.xml:

<manifest ...>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
    ...
<manifest/>

API 29+

Add the following property to your AndroidManifest.xml, located in <project root>/android/app/src/main/AndroidManifest.xml to opt-out of scoped storage:

<manifest ...>
    ...
    <application
        android:requestLegacyExternalStorage="true"
        ...>
    <application/>
<manifest/>

Usage

  • Listing albums in the gallery
final List<Album> imageAlbums = await PhotoGallery.listAlbums(
    mediumType: mediumType.image,
);
final List<Album> videoAlbums = await PhotoGallery.listAlbums(
    mediumType: mediumType.video,
    hideIfEmpty: false
);
  • Listing media in an album
final MediaPage imagePage = await imageAlbum.listMedia(
    skip: 5,
    take: 10,
);
final MediaPage videoPage = await videoAlbum.listMedia(
    newest: false,
    skip: 5,
    take: 10,
);
final List<Medium> allMedia = [
    ...imagePage.items,
    ...videoPage.items,
];
  • Loading more media in a album
if (!imagePage.isLast) {
    final nextImagePage = await imagePage.nextPage();
    // ...
}
  • Getting a Medium
final Medium medium = await PhotoGallery.getMedium(
  mediumId: "10", 
  MediumType: MediumType.image
);
  • Getting a file
final File file = await medium.getFile();
final File file = await PhotoGallery.getFile(mediumId: mediumId);
  • Getting thumbnail data
final List<int> data = await medium.getThumbnail();
final List<int> data = await PhotoGallery.getThumbnail(mediumId: mediumId);

You can also specify thumbnail width and height on Android API 29 or higher; You can also specify thumbnail width, height and whether provider high quality or not on iOS:

final List<int> data = await medium.getThumbnail(
    width: 128,
    height: 128,
    highQuality: true,
);
final List<int> data = await PhotoGallery.getThumbnail(
    mediumId: mediumId,
    mediumType: MediumType.image,
    width: 128,
    height: 128,
    highQuality: true,
);
  • Getting album thumbnail data
final List<int> data = await album.getThumbnail();
final List<int> data = await PhotoGallery.getAlbumThumbnail(albumId: albumId);

You can also specify thumbnail width and height on Android API 29 or higher; You can also specify thumbnail width, height and whether provider high quality or not on iOS:

final List<int> data = await album.getThumbnail(
    width: 128,
    height: 128,
    highQuality: true,
);
final List<int> data = await PhotoGallery.getAlbumThumbnail(
    albumId: albumId,
    width: 128,
    height: 128,
    highQuality: true,
);
  • Displaying medium thumbnail

ThumbnailProvider are available to display thumbnail images (here with the help of dependency transparent_image):

FadeInImage(
    fit: BoxFit.cover,
    placeholder: MemoryImage(kTransparentImage),
    image: ThumbnailProvider(
        mediumId: mediumId,
        mediumType: MediumType.image,
        width: 128,
        height: 128,
        hightQuality: true,
    ),
)

Width and height is only available on Android API 29+ or iOS platform

  • Displaying album thumbnail

AlbumThumbnailProvider are available to display album thumbnail images (here with the help of dependency transparent_image):

FadeInImage(
    fit: BoxFit.cover,
    placeholder: MemoryImage(kTransparentImage),
    image: AlbumThumbnailProvider(
        albumId: albumId,
        width: 128,
        height: 128,
        hightQuality: true,
    ),
)

Width and height is only available on Android API 29+ or iOS platform. High quality is only available on iOS platform.

  • Displaying a full size image

You can use PhotoProvider to display the full size image (here with the help of dependency transparent_image):

FadeInImage(
    fit: BoxFit.cover,
    placeholder: MemoryImage(kTransparentImage),
    image: PhotoProvider(
        mediumId: mediumId,
    ),
)

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add photo_gallery

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  photo_gallery: ^1.1.1

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:photo_gallery/photo_gallery.dart'; 

example/lib/main.dart

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:photo_gallery/photo_gallery.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:video_player/video_player.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  List<Album>? _albums;
  bool _loading = false;

  @override
  void initState() {
    super.initState();
    _loading = true;
    initAsync();
  }

  Future<void> initAsync() async {
    if (await _promptPermissionSetting()) {
      List<Album> albums =
          await PhotoGallery.listAlbums(mediumType: MediumType.image);
      setState(() {
        _albums = albums;
        _loading = false;
      });
    }
    setState(() {
      _loading = false;
    });
  }

  Future<bool> _promptPermissionSetting() async {
    if (Platform.isIOS &&
            await Permission.storage.request().isGranted &&
            await Permission.photos.request().isGranted ||
        Platform.isAndroid && await Permission.storage.request().isGranted) {
      return true;
    }
    return false;
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Photo gallery example'),
        ),
        body: _loading
            ? Center(
                child: CircularProgressIndicator(),
              )
            : LayoutBuilder(
                builder: (context, constraints) {
                  double gridWidth = (constraints.maxWidth - 20) / 3;
                  double gridHeight = gridWidth + 33;
                  double ratio = gridWidth / gridHeight;
                  return Container(
                    padding: EdgeInsets.all(5),
                    child: GridView.count(
                      childAspectRatio: ratio,
                      crossAxisCount: 3,
                      mainAxisSpacing: 5.0,
                      crossAxisSpacing: 5.0,
                      children: <Widget>[
                        ...?_albums?.map(
                          (album) => GestureDetector(
                            onTap: () => Navigator.of(context).push(
                                MaterialPageRoute(
                                    builder: (context) => AlbumPage(album))),
                            child: Column(
                              children: <Widget>[
                                ClipRRect(
                                  borderRadius: BorderRadius.circular(5.0),
                                  child: Container(
                                    color: Colors.grey[300],
                                    height: gridWidth,
                                    width: gridWidth,
                                    child: FadeInImage(
                                      fit: BoxFit.cover,
                                      placeholder:
                                          MemoryImage(kTransparentImage),
                                      image: AlbumThumbnailProvider(
                                        albumId: album.id,
                                        mediumType: album.mediumType,
                                        highQuality: true,
                                      ),
                                    ),
                                  ),
                                ),
                                Container(
                                  alignment: Alignment.topLeft,
                                  padding: EdgeInsets.only(left: 2.0),
                                  child: Text(
                                    album.name ?? "Unnamed Album",
                                    maxLines: 1,
                                    textAlign: TextAlign.start,
                                    style: TextStyle(
                                      height: 1.2,
                                      fontSize: 16,
                                    ),
                                  ),
                                ),
                                Container(
                                  alignment: Alignment.topLeft,
                                  padding: EdgeInsets.only(left: 2.0),
                                  child: Text(
                                    album.count.toString(),
                                    textAlign: TextAlign.start,
                                    style: TextStyle(
                                      height: 1.2,
                                      fontSize: 12,
                                    ),
                                  ),
                                ),
                              ],
                            ),
                          ),
                        ),
                      ],
                    ),
                  );
                },
              ),
      ),
    );
  }
}

class AlbumPage extends StatefulWidget {
  final Album album;

  AlbumPage(Album album) : album = album;

  @override
  State<StatefulWidget> createState() => AlbumPageState();
}

class AlbumPageState extends State<AlbumPage> {
  List<Medium>? _media;

  @override
  void initState() {
    super.initState();
    initAsync();
  }

  void initAsync() async {
    MediaPage mediaPage = await widget.album.listMedia();
    setState(() {
      _media = mediaPage.items;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            icon: Icon(Icons.arrow_back_ios),
            onPressed: () => Navigator.of(context).pop(),
          ),
          title: Text(widget.album.name ?? "Unnamed Album"),
        ),
        body: GridView.count(
          crossAxisCount: 3,
          mainAxisSpacing: 1.0,
          crossAxisSpacing: 1.0,
          children: <Widget>[
            ...?_media?.map(
              (medium) => GestureDetector(
                onTap: () => Navigator.of(context).push(MaterialPageRoute(
                    builder: (context) => ViewerPage(medium))),
                child: Container(
                  color: Colors.grey[300],
                  child: FadeInImage(
                    fit: BoxFit.cover,
                    placeholder: MemoryImage(kTransparentImage),
                    image: ThumbnailProvider(
                      mediumId: medium.id,
                      mediumType: medium.mediumType,
                      highQuality: true,
                    ),
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }
}

class ViewerPage extends StatelessWidget {
  final Medium medium;

  ViewerPage(Medium medium) : medium = medium;

  @override
  Widget build(BuildContext context) {
    DateTime? date = medium.creationDate ?? medium.modifiedDate;
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          leading: IconButton(
            onPressed: () => Navigator.of(context).pop(),
            icon: Icon(Icons.arrow_back_ios),
          ),
          title: date != null ? Text(date.toLocal().toString()) : null,
        ),
        body: Container(
          alignment: Alignment.center,
          child: medium.mediumType == MediumType.image
              ? FadeInImage(
                  fit: BoxFit.cover,
                  placeholder: MemoryImage(kTransparentImage),
                  image: PhotoProvider(mediumId: medium.id),
                )
              : VideoProvider(
                  mediumId: medium.id,
                ),
        ),
      ),
    );
  }
}

class VideoProvider extends StatefulWidget {
  final String mediumId;

  const VideoProvider({
    required this.mediumId,
  });

  @override
  _VideoProviderState createState() => _VideoProviderState();
}

class _VideoProviderState extends State<VideoProvider> {
  VideoPlayerController? _controller;
  File? _file;

  @override
  void initState() {
    WidgetsBinding.instance?.addPostFrameCallback((_) {
      initAsync();
    });
    super.initState();
  }

  Future<void> initAsync() async {
    try {
      _file = await PhotoGallery.getFile(mediumId: widget.mediumId);
      _controller = VideoPlayerController.file(_file!);
      _controller?.initialize().then((_) {
        // Ensure the first frame is shown after the video is initialized, even before the play button has been pressed.
        setState(() {});
      });
    } catch (e) {
      print("Failed : $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return _controller == null || !_controller!.value.isInitialized
        ? Container()
        : Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              AspectRatio(
                aspectRatio: _controller!.value.aspectRatio,
                child: VideoPlayer(_controller!),
              ),
              FlatButton(
                onPressed: () {
                  setState(() {
                    _controller!.value.isPlaying
                        ? _controller!.pause()
                        : _controller!.play();
                  });
                },
                child: Icon(
                  _controller!.value.isPlaying ? Icons.pause : Icons.play_arrow,
                ),
              ),
            ],
          );
  }
} 

Download Details:

Author: Firelands128

Source Code: https://github.com/Firelands128/photo_gallery

#flutter #images #android 

A Flutter Plugin That Retrieves Images and Videos From Mobile
Monty  Boehm

Monty Boehm

1659734820

Treat Colors As If They Are N-vectors for The Purposes Of Arithmetic

ColorVectorSpace 

This package is an add-on to ColorTypes, and provides fast mathematical operations for objects with types such as RGB and Gray. Specifically, with this package both grayscale and RGB colors are treated as if they are points in a normed vector space.

Introduction

Colorspaces such as RGB, unlike XYZ, are technically non-linear; perhaps the most "colorimetrically correct" approach when averaging two RGBs is to first convert each to XYZ, average them, and then convert back to RGB. Nor is there a clear definition of computing the sum of two colors. As a consequence, Julia's base color package, ColorTypes, does not support mathematical operations on colors.

However, particularly in image processing it is common to ignore this concern, and for the sake of performance treat an RGB as if it were a 3-vector. The role of this package is to extend ColorTypes to support such mathematical operations. Specifically, it defines + and multiplication by a scalar (and by extension, - and division by a scalar) for grayscale and AbstractRGB colors. These are the requirements of a vector space.

If you're curious about how much the "colorimetrically correct" and "vector space" views differ, the following diagram might help. The first 10 distinguishable_colors were generated, and all pairs were averaged. Each box represents the average of the pair of diagonal elements intersected by tracing vertically and horizontally; within each box, the upper diagonal is the "colorimetrically-correct" version, while the lower diagonal represents the "RGB vector space" version.

ColorVectorSpace

This package also defines norm(c) for RGB and grayscale colors. This makes these color spaces normed vector spaces. Note that norm has been designed to satisfy equivalence of grayscale and RGB representations: if x is a scalar, then norm(x) == norm(Gray(x)) == norm(RGB(x, x, x)). Effectively, there's a division-by-3 in the norm(::RGB) case compared to the Euclidean interpretation of the RGB vector space. Equivalence is an important principle for the Colors ecosystem, and violations should be reported as likely bugs. One violation is abs2; see the section below for more detail.

Usage

using ColorTypes, ColorVectorSpace

For the most part, that's it; just by loading ColorVectorSpace, most basic mathematical operations will "just work" on AbstractRGB, AbstractGray (Color{T,1}), TransparentRGB, and TransparentGray objects. (See definitions for the latter inside of ColorTypes).

However, there are some additional operations that you may need to distinguish carefully.

Multiplication

Grayscale values are conceptually similar to scalars, and consequently it seems straightforward to define multiplication of two grayscale values. RGB values present more options. This package supports three different notions of multiplication: the inner product, the hadamard (elementwise) product, and the tensor product.

julia> c1, c2 = RGB(0.2, 0.3, 0.4), RGB(0.5, 0.3, 0.2)
(RGB{Float64}(0.2,0.3,0.4), RGB{Float64}(0.5,0.3,0.2))

julia> c1⋅c2     # \cdot<TAB> # or dot(c1, c2)
0.09000000000000001

# This is equivelant to `mapc(*, c1, c2)`
julia> c1⊙c2     # \odot<TAB> # or hadamard(c1, c2)
RGB{Float64}(0.1,0.09,0.08000000000000002)

julia> c1⊗c2    # \otimes<TAB> # or tensor(c1, c2)
RGBRGB{Float64}:
 0.1   0.06  0.04
 0.15  0.09  0.06
 0.2   0.12  0.08

Note that c1⋅c2 = (c1.r*c2.r + c1.g*c2.g + c1.b*c2.b)/3, where the division by 3 ensures the equivalence norm(x) == norm(Gray(x)) == norm(RGB(x, x, x)).

Ordinary multiplication * is not supported because it is not obvious which one of these should be the default option.

However, * is defined for grayscale since all these three multiplication operations (i.e., , and ) are equivalent in the 1D vector space.

Variance

The variance v = E((c - μ)^2) (or its bias-corrected version) involves a multiplication, and to be consistent with the above you must specify which sense of multiplication you wish to use:

julia> cs = [c1, c2]
2-element Array{RGB{Float64},1} with eltype RGB{Float64}:
 RGB{Float64}(0.2,0.3,0.4)
 RGB{Float64}(0.5,0.3,0.2)

julia> varmult(⋅, cs)
0.021666666666666667

julia> varmult(⊙, cs)
RGB{Float64}(0.045,0.0,0.020000000000000004)

julia> varmult(⊗, cs)
RGBRGB{Float64}:
  0.045  0.0  -0.03
  0.0    0.0   0.0
 -0.03   0.0   0.02

The corresponding stdmult computes standard deviation.

abs and abs2

To begin with, there is no general and straightforward definition of the absolute value of a vector. There are two reasonably intuitive definitions of abs/abs2: as a channel-wise operator or as a function which returns a real number based on the norm. For the latter, there are also variations in the definition of norm.

In ColorVectorSpace v0.9 and later, abs is defined as a channel-wise operator. abs2 returns a real-valued scalar. In previous versions of ColorVectorSpace, for g = Gray(0.3), ColorVectorSpace returned different values for abs2(g) and abs2(RGB(g)). This breaks the equivalence of g and RGB(g). This behavior is retained, with a deprecation warning, starting with ColorVectorSpace 0.9.6.

In the future, abs2 will be defined as abs2(c) == c⋅c ≈ norm(c)^2. This effectively divides the old result by 3; code that imposes thresholds on abs2(c) may need to be updated. You can obtain that behavior now--and circumvent the deprecation warning-- by using ColorVectorSpace.Future.abs2(c).

We anticipate the following transition schedule:

  • Sept 21, 2021: release ColorVectorSpace 0.9.7 with both abs2 and Future.abs2. abs2 will have a "quiet" deprecation warning (visible with --depwarn=yes or when running Pkg.test)
  • May 19, 2022: make the deprecation warning "noisy" (cannot be turned off). This is designed to catch user-level scripts that may need to update thresholds or other constants.
  • *July 1, 2022: transition abs2 to the new definition and make Future.abs2 give a "noisy" depwarn to revert to regular abs2.
  • *Dec 1, 2022: remove Future.abs2.

The two marked with * denote breaking releases.

Author: JuliaGraphics
Source Code: https://github.com/JuliaGraphics/ColorVectorSpace.jl 
License: View license

#julia #color #images #rgb

Treat Colors As If They Are N-vectors for The Purposes Of Arithmetic
Royce  Reinger

Royce Reinger

1658805240

Gemoji: Emoji Images and Names

gemoji

This library contains character information about native emojis.

Installation

Add gemoji to your Gemfile.

gem 'gemoji'

Example Rails Helper

This would allow emojifying content such as: it's raining :cat:s and :dog:s!

See the Emoji cheat sheet for more examples.

module EmojiHelper
  def emojify(content)
    h(content).to_str.gsub(/:([\w+-]+):/) do |match|
      if emoji = Emoji.find_by_alias($1)
        %(<img alt="#$1" src="#{image_path("emoji/#{emoji.image_filename}")}" style="vertical-align:middle" width="20" height="20" />)
      else
        match
      end
    end.html_safe if content.present?
  end
end

Unicode mapping

Translate emoji names to unicode and vice versa.

>> Emoji.find_by_alias("cat").raw
=> "🐱"  # Don't see a cat? That's U+1F431.

>> Emoji.find_by_unicode("\u{1f431}").name
=> "cat"

Adding new emoji

You can add new emoji characters to the Emoji.all list:

emoji = Emoji.create("music") do |char|
  char.add_alias "song"
  char.add_unicode_alias "\u{266b}"
  char.add_tag "notes"
end

emoji.name #=> "music"
emoji.raw  #=> "♫"
emoji.image_filename #=> "unicode/266b.png"

# Creating custom emoji (no Unicode aliases):
emoji = Emoji.create("music") do |char|
  char.add_tag "notes"
end

emoji.custom? #=> true
emoji.image_filename #=> "music.png"

As you create new emoji, you must ensure that you also create and put the images they reference by their image_filename to your assets directory.

You can customize image_filename with:

emoji = Emoji.create("music") do |char|
  char.image_filename = "subdirectory/my_emoji.gif"
end

For existing emojis, you can edit the list of aliases or add new tags in an edit block:

emoji = Emoji.find_by_alias "musical_note"

Emoji.edit_emoji(emoji) do |char|
  char.add_alias "music"
  char.add_unicode_alias "\u{266b}"
  char.add_tag "notes"
end

Emoji.find_by_alias "music"       #=> emoji
Emoji.find_by_unicode "\u{266b}"  #=> emoji

Author: Github
Source Code: https://github.com/github/gemoji 
License: MIT license

#ruby #emoji #images #names 

Gemoji: Emoji Images and Names
Royce  Reinger

Royce Reinger

1658316300

Ruby-readability: Port Of Arc90's Readability Project to Ruby

Ruby Readability

Ruby Readability is a tool for extracting the primary readable content of a webpage. It is a Ruby port of arc90's readability project.

Install

Command line:

(sudo) gem install ruby-readability

Bundler:

gem "ruby-readability", :require => 'readability'

Example

require 'rubygems'
require 'readability'
require 'open-uri'

source = open('http://lab.arc90.com/experiments/readability/').read
puts Readability::Document.new(source).content

Options

You may provide options to Readability::Document.new, including:

  • :tags: the base whitelist of tags to sanitize, defaults to %w[div p];
  • :remove_empty_nodes: remove <p> tags that have no text content; also removes <p> tags that contain only images;
  • :attributes: whitelist of allowed attributes;
  • :debug: provide debugging output, defaults false;
  • :encoding: if the page is of a known encoding, you can specify it; if left unspecified, the encoding will be guessed (only in Ruby 1.9.x). If you wish to disable guessing, supply :do_not_guess_encoding => true;
  • :html_headers: in Ruby 1.9.x these will be passed to the guess_html_encoding gem to aid with guessing the HTML encoding;
  • :ignore_image_format: for use with .images. For example: :ignore_image_format => ["gif", "png"];
  • :min_image_height: set a minimum image height for #images;
  • :min_image_width: set a minimum image width for #images.
  • :blacklist and :whitelist allow you to explicitly scope to, or remove, CSS selectors.

Command Line Tool

Readability comes with a command-line tool for experimentation in bin/readability.

Usage: readability [options] URL
    -d, --debug                      Show debug output
    -i, --images                     Keep images and links
    -h, --help                       Show this message

Images

You can get a list of images in the content area with Document#images. This feature requires that the fastimage gem be installed.

rbody = Readability::Document.new(body, :tags => %w[div p img a], :attributes => %w[src href], :remove_empty_nodes => false)
rbody.images

Related Projects

  • readability.cr - Port of ruby-readability's port of arc90's readability project to Crystal
  • newspaper is an advanced news extraction, article extraction, and content curation library for Python.

Potential Issues

If you're on a Mac and are getting segmentation faults, see the discussion at https://github.com/sparklemotion/nokogiri/issues/404 and consider updating your version of libxml2. Version 2.7.8 of libxml2, installed with brew, worked for me:

gem install nokogiri -- --with-xml2-include=/usr/local/Cellar/libxml2/2.7.8/include/libxml2 --with-xml2-lib=/usr/local/Cellar/libxml2/2.7.8/lib --with-xslt-dir=/usr/local/Cellar/libxslt/1.1.26

Or if you're using bundler and Rails 3, you can run this command to make bundler always globally build nokogiri this way:

bundle config build.nokogiri -- --with-xml2-include=/usr/local/Cellar/libxml2/2.7.8/include/libxml2 --with-xml2-lib=/usr/local/Cellar/libxml2/2.7.8/lib --with-xslt-dir=/usr/local/Cellar/libxslt/1.1.26

Author: Cantino
Source Code: https://github.com/cantino/ruby-readability 
License: Apache-2.0 license

#ruby #content #webpage 

Ruby-readability: Port Of Arc90's Readability Project to Ruby
Royce  Reinger

Royce Reinger

1658308802

LinkThumbnailer: Ruby gem generating image thumbnails from a given URL

LinkThumbnailer

Ruby gem generating image thumbnails from a given URL. Rank them and give you back an object containing images and website informations. Works like Facebook link previewer.

Features

  • Dead simple.
  • Support OpenGraph protocol.
  • Find and sort images that best represent what the page is about.
  • Find and rate description that best represent what the page is about.
  • Allow for custom class to sort the website descriptions yourself.
  • Support image urls blacklisting (advertisements).
  • Works with and without Rails.
  • Fully customizable.
  • Fully tested.

Installation

Add this line to your application's Gemfile:

gem 'link_thumbnailer'

And then execute:

$ bundle

Or install it yourself as:

$ gem install link_thumbnailer

If you are using Rails, you can generate the configuration file with:

$ rails g link_thumbnailer:install

This will add link_thumbnailer.rb to config/initializers/.

Usage

Run irb and require the gem:

require 'link_thumbnailer'

The gem handle regular website but also website that use the Opengraph protocol.

object = LinkThumbnailer.generate('http://stackoverflow.com')
 => #<LinkThumbnailer::Models::Website:...>

object.title
 => "Stack Overflow"

object.favicon
 => "//cdn.sstatic.net/stackoverflow/img/favicon.ico?v=038622610830"

object.description
 => "Q&A for professional and enthusiast programmers"

object.images.first.src.to_s
 => "http://cdn.sstatic.net/stackoverflow/img/apple-touch-icon@2.png?v=fde65a5a78c6"

LinkThumbnailer generate method return an instance of LinkThumbnailer::Models::Website that respond to to_json and as_json as you would expect:

object.to_json
 => "{\"url\":\"http://stackoverflow.com\",\"title\":\"Stack Overflow\",\"description\":\"Q&A for professional and enthusiast programmers\",\"images\":[{\"src\":\"http://cdn.sstatic.net/stackoverflow/img/apple-touch-icon@2.png?v=fde65a5a78c6\",\"size\":[316,316],\"type\":\"png\"}]}"

Configuration

LinkThumbnailer comes with default configuration values. You can change default value by overriding them in a rails initializer:

In config/initializers/link_thumbnailer.rb

LinkThumbnailer.configure do |config|
  # Numbers of redirects before raising an exception when trying to parse given url.
  #
  # config.redirect_limit = 3

  # Set user agent
  #
  # config.user_agent = 'link_thumbnailer'

  # Enable or disable SSL verification
  #
  # config.verify_ssl = true

  # The amount of time in seconds to wait for a connection to be opened.
  # If the HTTP object cannot open a connection in this many seconds,
  # it raises a Net::OpenTimeout exception.
  #
  # See http://www.ruby-doc.org/stdlib-2.1.1/libdoc/net/http/rdoc/Net/HTTP.html#open_timeout
  #
  # config.http_open_timeout = 5

  # List of blacklisted urls you want to skip when searching for images.
  #
  # config.blacklist_urls = [
  #   %r{^http://ad\.doubleclick\.net/},
  #   %r{^http://b\.scorecardresearch\.com/},
  #   %r{^http://pixel\.quantserve\.com/},
  #   %r{^http://s7\.addthis\.com/}
  # ]

  # List of attributes you want LinkThumbnailer to fetch on a website.
  #
  # config.attributes = [:title, :images, :description, :videos, :favicon]

  # List of procedures used to rate the website description. Add you custom class
  # here. See wiki for more details on how to build your own graders.
  #
  # config.graders = [
  #   ->(description) { ::LinkThumbnailer::Graders::Length.new(description) },
  #   ->(description) { ::LinkThumbnailer::Graders::HtmlAttribute.new(description, :class) },
  #   ->(description) { ::LinkThumbnailer::Graders::HtmlAttribute.new(description, :id) },
  #   ->(description) { ::LinkThumbnailer::Graders::Position.new(description, weight: 3) },
  #   ->(description) { ::LinkThumbnailer::Graders::LinkDensity.new(description) }
  # ]

  # Minimum description length for a website.
  #
  # config.description_min_length = 25

  # Regex of words considered positive to rate website description.
  #
  # config.positive_regex = /article|body|content|entry|hentry|main|page|pagination|post|text|blog|story/i

  # Regex of words considered negative to rate website description.
  #
  # config.negative_regex = /combx|comment|com-|contact|foot|footer|footnote|masthead|media|meta|outbrain|promo|related|scroll|shoutbox|sidebar|sponsor|shopping|tags|tool|widget|modal/i

  # Numbers of images to fetch. Fetching too many images will be slow.
  # Note that LinkThumbnailer will only sort fetched images between each other.
  # Meaning that they could be a "better" image on the page.
  #
  # config.image_limit = 5

  # Whether you want LinkThumbnailer to return image size and type or not.
  # Setting this value to false will increase performance since for each images, LinkThumbnailer
  # does not have to fetch its size and type.
  #
  # config.image_stats = true
  #
  # Whether you want LinkThumbnailer to raise an exception if the Content-Type of the HTTP request
  # is not an html or xml.
  #
  # config.raise_on_invalid_format = false
  #
  # Sets number of concurrent http connections that can be opened to fetch images informations such as size and type.
  #
  # config.max_concurrency = 20

  # Sets the default encoding.
  #
  # config.encoding = 'utf-8'
end

Or at runtime:

object = LinkThumbnailer.generate('http://stackoverflow.com', redirect_limit: 5, user_agent: 'foo')

Note that runtime options will override default global configuration.

See Configuration Options Explained for more details on each configuration options.

Exceptions

LinkThumbnailer defines a list of custom exceptions you may want to rescue in your code. All the following exceptions inherit from LinkThumbnailer::Exceptions:

  • RedirectLimit -- raised when redirection threshold defined in config is reached
  • BadUriFormat -- raised when url given is not a valid HTTP url
  • FormatNotSupported -- raised when the Content-Type of the HTTP request is not supported (not html)

You can rescue from any LinkThumbnailer exceptions using the following code:

begin
  LinkThumbnailer.generate('http://foo.com')
rescue LinkThumbnailer::Exceptions => e
  # do something
end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Run the specs (bundle exec rspec spec)
  4. Commit your changes (git commit -am 'Added some feature')
  5. Push to the branch (git push origin my-new-feature)
  6. Create new Pull Request

Demo Application is here ! The source code of the Demo Application is hosted here!

Author: Gottfrois
Source Code: https://github.com/gottfrois/link_thumbnailer 
License: MIT license

#ruby #link #url #images 

LinkThumbnailer: Ruby gem generating image thumbnails from a given URL
Royce  Reinger

Royce Reinger

1658301080

Metainspector: Ruby Gem for Web Scraping Purposes

MetaInspector

MetaInspector is a gem for web scraping purposes.

You give it an URL, and it lets you easily get its title, links, images, charset, description, keywords, meta tags...

Installation

Install the gem from RubyGems:

gem install metainspector

If you're using it on a Rails application, just add it to your Gemfile and run bundle install

gem 'metainspector'

Supported Ruby versions are defined in .travis.yml.

Usage

Initialize a MetaInspector instance for an URL, like this:

page = MetaInspector.new('http://sitevalidator.com')

If you don't include the scheme on the URL, http:// will be used by default:

page = MetaInspector.new('sitevalidator.com')

You can also include the html which will be used as the document to scrape:

page = MetaInspector.new("http://sitevalidator.com",
                         :document => "<html>...</html>")

Accessing response

You can check the status and headers from the response like this:

page.response.status  # 200
page.response.headers # { "server"=>"nginx", "content-type"=>"text/html; charset=utf-8",
                      #   "cache-control"=>"must-revalidate, private, max-age=0", ... }

Accessing scraped data

URL

page.url                 # URL of the page
page.tracked?            # returns true if the url contains known tracking parameters
page.untracked_url       # returns the url with the known tracking parameters removed
page.untrack!            # removes the known tracking parameters from the url
page.scheme              # Scheme of the page (http, https)
page.host                # Hostname of the page (like, sitevalidator.com, without the scheme)
page.root_url            # Root url (scheme + host, like http://sitevalidator.com/)

Head links

page.head_links          # an array of hashes of all head/links
page.stylesheets         # an array of hashes of all head/links where rel='stylesheet'
page.canonicals          # an array of hashes of all head/links where rel='canonical'
page.feeds               # Get rss or atom links in meta data fields as array of hash in the form { href: "...", title: "...", type: "..." }

Texts

page.title               # title of the page from the head section, as string
page.best_title          # best title of the page, from a selection of candidates
page.author              # author of the page from the meta author tag
page.best_author         # best author of the page, from a selection of candidates
page.description         # returns the meta description
page.best_description    # returns the first non-empty description between the following candidates: standard meta description, og:description, twitter:description, the first long paragraph
page.h1                  # returns h1 text array
page.h2                  # returns h2 text array
page.h3                  # returns h3 text array
page.h4                  # returns h4 text array
page.h5                  # returns h5 text array
page.h6                  # returns h6 text array

Links

page.links.raw           # every link found, unprocessed
page.links.all           # every link found on the page as an absolute URL
page.links.http          # every HTTP link found
page.links.non_http      # every non-HTTP link found
page.links.internal      # every internal link found on the page as an absolute URL
page.links.external      # every external link found on the page as an absolute URL

Images

page.images              # enumerable collection, with every img found on the page as an absolute URL
page.images.with_size    # a sorted array (by descending area) of [image_url, width, height]
page.images.best         # Most relevant image, if defined with the og:image or twitter:image metatags. Fallback to the first page.images array element
page.images.favicon      # absolute URL to the favicon

Meta tags

When it comes to meta tags, you have several options:

page.meta_tags  # Gives you all the meta tags by type:
                # (meta name, meta http-equiv, meta property and meta charset)
                # As meta tags can be repeated (in the case of 'og:image', for example),
                # the values returned will be arrays
                #
                # For example:
                #
                # {
                    'name' => {
                                'keywords'       => ['one, two, three'],
                                'description'    => ['the description'],
                                'author'         => ['Joe Sample'],
                                'robots'         => ['index,follow'],
                                'revisit'        => ['15 days'],
                                'dc.date.issued' => ['2011-09-15']
                              },

                    'http-equiv' => {
                                        'content-type'        => ['text/html; charset=UTF-8'],
                                        'content-style-type'  => ['text/css']
                                    },

                    'property' => {
                                    'og:title'        => ['An OG title'],
                                    'og:type'         => ['website'],
                                    'og:url'          => ['http://example.com/meta-tags'],
                                    'og:image'        => ['http://example.com/rock.jpg',
                                                          'http://example.com/rock2.jpg',
                                                          'http://example.com/rock3.jpg'],
                                    'og:image:width'  => ['300'],
                                    'og:image:height' => ['300', '1000']
                                   },

                    'charset' => ['UTF-8']
                  }

As this method returns a hash, you can also take only the key that you need, like in:

page.meta_tags['property']  # Returns:
                            # {
                            #   'og:title'        => ['An OG title'],
                            #   'og:type'         => ['website'],
                            #   'og:url'          => ['http://example.com/meta-tags'],
                            #   'og:image'        => ['http://example.com/rock.jpg',
                            #                         'http://example.com/rock2.jpg',
                            #                         'http://example.com/rock3.jpg'],
                            #   'og:image:width'  => ['300'],
                            #   'og:image:height' => ['300', '1000']
                            # }

In most cases you will only be interested in the first occurrence of a meta tag, so you can use the singular form of that method:

page.meta_tag['name']   # Returns:
                        # {
                        #   'keywords'       => 'one, two, three',
                        #   'description'    => 'the description',
                        #   'author'         => 'Joe Sample',
                        #   'robots'         => 'index,follow',
                        #   'revisit'        => '15 days',
                        #   'dc.date.issued' => '2011-09-15'
                        # }

Or, as this is also a hash:

page.meta_tag['name']['keywords']    # Returns 'one, two, three'

And finally, you can use the shorter meta method that will merge the different keys so you have a simpler hash:

page.meta   # Returns:
            #
            # {
            #   'keywords'            => 'one, two, three',
            #   'description'         => 'the description',
            #   'author'              => 'Joe Sample',
            #   'robots'              => 'index,follow',
            #   'revisit'             => '15 days',
            #   'dc.date.issued'      => '2011-09-15',
            #   'content-type'        => 'text/html; charset=UTF-8',
            #   'content-style-type'  => 'text/css',
            #   'og:title'            => 'An OG title',
            #   'og:type'             => 'website',
            #   'og:url'              => 'http://example.com/meta-tags',
            #   'og:image'            => 'http://example.com/rock.jpg',
            #   'og:image:width'      => '300',
            #   'og:image:height'     => '300',
            #   'charset'             => 'UTF-8'
            # }

This way, you can get most meta tags just like that:

page.meta['author']     # Returns "Joe Sample"

Please be aware that all keys are converted to downcase, so it's 'dc.date.issued' and not 'DC.date.issued'.

Misc

page.charset             # UTF-8
page.content_type        # content-type returned by the server when the url was requested

Other representations

You can also access most of the scraped data as a hash:

page.to_hash    # { "url"   => "http://sitevalidator.com",
                    "title" => "MarkupValidator :: site-wide markup validation tool", ... }

The original document is accessible from:

page.to_s         # A String with the contents of the HTML document

And the full scraped document is accessible from:

page.parsed  # Nokogiri doc that you can use it to get any element from the page

Options

Forced encoding

If you get a MetaInspector::RequestError, "invalid byte sequence in UTF-8" or similar error, you can try forcing the encoding like this:

page = MetaInspector.new(url, :encoding => 'UTF-8')

Timeout & Retries

You can specify 2 different timeouts when requesting a page:

  • connection_timeout sets the maximum number of seconds to wait to get a connection to the page.
  • read_timeout sets the maximum number of seconds to wait to read the page, once connected.

Both timeouts default to 20 seconds each.

You can also specify the number of retries, which defaults to 3.

For example, this will time out after 10 seconds waiting for a connection, or after 5 seconds waiting to read its contents, and will retry 4 times:

page = MetaInspector.new('www.google', :connection_timeout => 10, :read_timeout => 5, :retries => 4)

If MetaInspector fails to fetch the page after it has exhausted its retries, it will raise MetaInspector::TimeoutError, which you can rescue in your application code.

begin
  page = MetaInspector.new(url)
rescue MetaInspector::TimeoutError
  enqueue_for_future_fetch_attempt(url)
  render_simple(url)
else
  render_rich(page)
end

Redirections

By default, MetaInspector will follow redirects (up to a limit of 10).

If you want to disallow redirects, you can do it like this:

page = MetaInspector.new('facebook.com', :allow_redirections => false)

Headers

By default, the following headers are set:

{
  'User-Agent'      => "MetaInspector/#{MetaInspector::VERSION} (+https://github.com/jaimeiniesta/metainspector)",
  'Accept-Encoding' => 'identity'
}

The Accept-Encoding is set to identity to avoid exceptions being raised on servers that return malformed compressed responses, as explained here.

If you want to override the default headers then use the headers option:

# Set the User-Agent header
page = MetaInspector.new('example.com', :headers => {'User-Agent' => 'My custom User-Agent'})

Disabling SSL verification (or any other Faraday options)

Faraday can be passed options via :faraday_options.

This is useful in cases where we need to customize the way we request the page, like for example disabling SSL verification, like this:

MetaInspector.new('https://example.com')
# Faraday::SSLError: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed

MetaInspector.new('https://example.com', faraday_options: { ssl: { verify: false } })
# Now we can access the page

Allow non-HTML content type

MetaInspector will by default raise an exception when trying to parse a non-HTML URL (one that has a content-type different than text/html). You can disable this behaviour with:

page = MetaInspector.new('sitevalidator.com', :allow_non_html_content => true)
page = MetaInspector.new('http://example.com/image.png')
page.content_type  # "image/png"
page.description   # will raise an exception

page = MetaInspector.new('http://example.com/image.png', :allow_non_html_content => true)
page.content_type  # "image/png"
page.description   # will return a garbled string

URL Normalization

By default, URLs are normalized using the Addressable gem. For example:

# Normalization will add a default scheme and a trailing slash...
page = MetaInspector.new('sitevalidator.com')
page.url # http://sitevalidator.com/

# ...and it will also convert international characters
page = MetaInspector.new('http://www.詹姆斯.com')
page.url # http://www.xn--8ws00zhy3a.com/

While this is generally useful, it can be tricky sometimes.

You can disable URL normalization by passing the normalize_url: false option.

Image downloading

When you ask for the largest image on the page with page.images.largest, it will be determined by its height and width attributes on the HTML markup, and also by downloading a small portion of each image using the fastimage gem. This is really fast as it doesn't download the entire images, normally just the headers of the image files.

If you want to disable this, you can specify it like this:

page = MetaInspector.new('http://example.com', download_images: false)

Caching responses

MetaInspector can be configured to use Faraday::HttpCache to cache page responses. For that you should pass the faraday_http_cache option with at least the :store key, for example:

cache = ActiveSupport::Cache.lookup_store(:file_store, '/tmp/cache')
page = MetaInspector.new('http://example.com', faraday_http_cache: { store: cache })

Exception Handling

Web page scraping is tricky, you can expect to find different exceptions during the request of the page or the parsing of its contents. MetaInspector will encapsulate these exceptions on these main errors:

  • MetaInspector::TimeoutError. When fetching a web page has taken too long.
  • MetaInspector::RequestError. When there has been an error on the request phase. Examples: page not found, SSL failure, invalid URI.
  • MetaInspector::ParserError. When there has been an error parsing the contents of the page.
  • MetaInspector::NonHtmlError. When the contents of the page was not HTML. See also the allow_non_html_content option

Examples

You can find some sample scripts on the examples folder, including a basic scraping and a spider that will follow external links using a queue. What follows is an example of use from irb:

$ irb
>> require 'metainspector'
=> true

>> page = MetaInspector.new('http://sitevalidator.com')
=> #<MetaInspector:0x11330c0 @url="http://sitevalidator.com">

>> page.title
=> "MarkupValidator :: site-wide markup validation tool"

>> page.meta['description']
=> "Site-wide markup validation tool. Validate the markup of your whole site with just one click."

>> page.meta['keywords']
=> "html, markup, validation, validator, tool, w3c, development, standards, free"

>> page.links.size
=> 15

>> page.links[4]
=> "/plans-and-pricing"

Contributing guidelines

You're more than welcome to fork this project and send pull requests. Just remember to:

  • Create a topic branch for your changes.
  • Add specs.
  • Keep your fake responses as small as possible. For each change in spec/fixtures, a comment should be included explaining why it's needed.
  • Update README.md if needed (for example, when you're adding or changing a feature).

Thanks to all the contributors:

https://github.com/jaimeiniesta/metainspector/graphs/contributors

You can also come to chat with us on our Gitter room and Google group.

Related projects

See it in action!

You can try MetaInspector live at this little demo: https://metainspectordemo.herokuapp.com

Author: Metainspector
Source Code: https://github.com/metainspector/metainspector 
License: MIT license

#ruby #web #url #images 

Metainspector: Ruby Gem for Web Scraping Purposes
Mike  Kozey

Mike Kozey

1657696260

Jpegtran_ffi: Mostly Lossless Transformations Of JPEG Images

jpegtran_ffi

Mostly lossless transformations of JPEG images, similar to those than can be made using jpegtran tool, e.g. cropping and rotations. Since JPEG data doesn't need to be decoded or encoded it should hopefully be fast as well.

A lossy recompress method to reduce quality & size is also included. EXIF data can be copied from the original image.

This package uses libjpeg-turbo via Dart's FFI. Unlike platform plugins it should be usable from within isolates.

Example

void cropToSquareAndRotate() {
    var jpegtran = JpegTransformer(_imageBytes);
    try {
        var info = jpegtran.getInfo();

        var cropSize = min(info.width, info.height);
        var crop = JpegCrop(
            w: cropSize,
            h: cropSize,
            x: (info.width - cropSize) ~/ 2,
            y: (info.height - cropSize) ~/ 2,
            alignIfRequired: true,
        );

        var rotate = JpegRotation(
            angle: 90,
            crop: crop,
            options: JpegOptions(grayscale: false),
        );

        var newImage = jpegtran.transform(rotate);
        setState(() {
            _imageBytes = newImage;
        });
    } catch (err) {
        _showError(err, context);
    } finally {
        jpegtran.dispose();
    }
}
Uint8List recompress(Uint8List jpegBytes) {
  var jpegtran = JpegTransformer(jpegBytes);
  try {
    return jpegtran.recompress(
      quality: 70,
      preserveEXIF: true,
    );
  } finally {
    jpegtran.dispose();
  }
}

Installing

Use this package as a library

Depend on it

Run this command:

With Flutter:

 $ flutter pub add jpegtran_ffi

This will add a line like this to your package's pubspec.yaml (and run an implicit flutter pub get):

dependencies:
  jpegtran_ffi: ^0.0.8

Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.

Import it

Now in your Dart code, you can use:

import 'package:jpegtran_ffi/jpegtran_ffi.dart';

example/lib/main.dart

import 'package:flutter/material.dart';

import 'CropSquarePage.dart';
import 'OperationsPage.dart';
import 'CreditsPage.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "jpeg_ffi demo",
      initialRoute: '/',
      routes: {
        '/': (context) => HomePage(),
        '/cropSquare': (context) => Scaffold(body: CropSquarePage()),
        '/operations': (context) => Scaffold(body: OperationsPage()),
        '/credits': (context) => CreditsPage(),
      },
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('jpeg_ffi examples'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            SizedBox(height: 40),
            ElevatedButton(
              child: Text("Crop to square"),
              onPressed: () {
                Navigator.pushNamed(context, '/cropSquare');
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              child: Text("Operations"),
              onPressed: () {
                Navigator.pushNamed(context, '/operations');
              },
            ),
            SizedBox(height: 20),
            ElevatedButton(
              child: Text("Credits"),
              onPressed: () {
                Navigator.pushNamed(context, '/credits');
              },
            ),
          ],
        ),
      ),
    );
  }
}

TODO

  • Remove unneeded parts of libjpeg-turbo
  • Lossy resizing

Original article source at: https://pub.dev/packages/jpegtran_ffi 

#flutter #dart #jpeg #images 

Jpegtran_ffi: Mostly Lossless Transformations Of JPEG Images