-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit adb3c42
Showing
66 changed files
with
19,561 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
root = true | ||
|
||
[*.cr] | ||
charset = utf-8 | ||
end_of_line = lf | ||
insert_final_newline = true | ||
indent_style = space | ||
indent_size = 2 | ||
trim_trailing_whitespace = true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: crystal-vips CI | ||
on: | ||
push: | ||
pull_request: | ||
branches: [main] | ||
schedule: | ||
- cron: '0 6 * * 6' # Every Saturday 6 AM | ||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Download source | ||
uses: actions/checkout@v2 | ||
- name: Install Crystal | ||
uses: crystal-lang/install-crystal@v1 | ||
- name: Install libvips | ||
env: | ||
DEBIAN_FRONTEND: noninteractive | ||
run: | ||
# we only need the library | ||
sudo apt-get install --fix-missing -qq -o Acquire::Retries=3 libvips-dev | ||
- name: Run tests | ||
run: crystal spec | ||
- name: Check formatting | ||
run: crystal tool format --check |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
name: Deploy Docs | ||
|
||
on: | ||
push: | ||
branches: | ||
- main | ||
|
||
jobs: | ||
test: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- name: Download source | ||
uses: actions/checkout@v2 | ||
- name: Install Crystal | ||
uses: crystal-lang/install-crystal@v1 | ||
- name: Build | ||
run: crystal docs | ||
- name: Deploy | ||
if: github.ref == 'refs/heads/main' | ||
uses: JamesIves/[email protected] | ||
with: | ||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
BRANCH: gh-pages | ||
FOLDER: docs | ||
CLEAN: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
/docs/ | ||
/lib/ | ||
/bin/ | ||
/images/ | ||
/.shards/ | ||
*.dwarf | ||
.vscode/** | ||
# Libraries don't need dependency lock | ||
# Dependencies will be locked in applications that use them | ||
/shard.lock |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2022 Ali Naqvi <[email protected]> | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,127 @@ | ||
# CrystalVips | ||
|
||
[![crystal-vips CI](https://github.com/naqvis/crystal-vips/actions/workflows/ci.yml/badge.svg)](https://github.com/naqvis/crystal-vips/actions/workflows/ci.yml) | ||
[![Latest release](https://img.shields.io/github/release/naqvis/crystal-vips.svg)](https://github.com/naqvis/crystal-vips/releases) | ||
[![Docs](https://img.shields.io/badge/docs-available-brightgreen.svg)](https://naqvis.github.io/crystal-vips/) | ||
|
||
Provides Crystal language interface to the [libvips](https://github.com/libvips/libvips) image processing library. | ||
Programs that use `CrystalVips` don't manipulate images directly, instead they create pipelines of image processing operations starting from a source image. When the pipe is connected to a destination, the whole pipeline executes at once and in parallel, streaming the image from source to destination in a set of small fragments. | ||
|
||
Because `CrystalVips` is parallel, its' quick, and because it doesn't need to keep entire images in memory, its light. For example, the libvips speed and memory use benchmark: | ||
|
||
[https://github.com/libvips/libvips/wiki/Speed-and-memory-use](https://github.com/libvips/libvips/wiki/Speed-and-memory-use) | ||
|
||
## Pre-requisites | ||
|
||
You need to [install the libvips | ||
library](https://www.libvips.org/install.html). It's in the linux package managers, homebrew and MacPorts, and there are Windows binaries on the vips website. For example, on Debian: | ||
|
||
``` | ||
sudo apt-get install --no-install-recommends libvips42 | ||
``` | ||
|
||
(`--no-install-recommends` stops Debian installing a *lot* of extra packages) | ||
|
||
Or macOS: | ||
|
||
``` | ||
brew install vips | ||
``` | ||
|
||
## Installation | ||
|
||
1. Add the dependency to your `shard.yml`: | ||
|
||
```yaml | ||
dependencies: | ||
vips: | ||
github: naqvis/crystal-vips | ||
``` | ||
2. Run `shards install` | ||
|
||
## Usage | ||
|
||
```crystal | ||
require "vips" | ||
im = Vips::Image.new_from_file("image.jpg") | ||
# put im at position (100, 100) in a 3000 x 3000 pixel image, | ||
# make the other pixels in the image by mirroring im up / down / | ||
# left / right, see | ||
# https://libvips.github.io/libvips/API/current/libvips-conversion.html#vips-embed | ||
im = im.embed(100, 100, 3000, 3000, extend: Vips::Enums::Extend::Mirror) | ||
# multiply the green (middle) band by 2, leave the other two alone | ||
im *= [1, 2, 1] | ||
# make an image from an array constant, convolve with it | ||
mask = Vips::Image.new_from_array([ | ||
[-1, -1, -1], | ||
[-1, 16, -1], | ||
[-1, -1, -1]], 8) | ||
im = im.conv(mask, precision: Vips::Enums::Precision::Integer) | ||
# finally, write the result back to a file on disk | ||
im.write_to_file("output.jpg") | ||
``` | ||
|
||
Refer to [samples](samples) folder for more samples | ||
|
||
## Development | ||
|
||
To run all tests: | ||
|
||
``` | ||
crystal spec | ||
``` | ||
|
||
# Getting more help | ||
|
||
The libvips website has a handy table of [all the libvips | ||
operators](http://libvips.github.io/libvips/API/current/func-list.html). Each | ||
one links to the main API docs so you can see what you need to pass to it. | ||
|
||
A simple way to see the arguments for an operation is to try running it | ||
from the command-line. For example: | ||
|
||
```bash | ||
$ vips embed | ||
embed an image in a larger image | ||
usage: | ||
embed in out x y width height | ||
where: | ||
in - Input image, input VipsImage | ||
out - Output image, output VipsImage | ||
x - Left edge of input in output, input gint | ||
default: 0 | ||
min: -1000000000, max: 1000000000 | ||
y - Top edge of input in output, input gint | ||
default: 0 | ||
min: -1000000000, max: 1000000000 | ||
width - Image width in pixels, input gint | ||
default: 1 | ||
min: 1, max: 1000000000 | ||
height - Image height in pixels, input gint | ||
default: 1 | ||
min: 1, max: 1000000000 | ||
optional arguments: | ||
extend - How to generate the extra pixels, input VipsExtend | ||
default: black | ||
allowed: black, copy, repeat, mirror, white, background | ||
background - Color for background pixels, input VipsArrayDouble | ||
operation flags: sequential | ||
``` | ||
|
||
## Contributing | ||
|
||
1. Fork it (<https://github.com/naqvis/crystal-vips/fork>) | ||
2. Create your feature branch (`git checkout -b my-new-feature`) | ||
3. Commit your changes (`git commit -am 'Add some feature'`) | ||
4. Push to the branch (`git push origin my-new-feature`) | ||
5. Create a new Pull Request | ||
|
||
## Contributors | ||
|
||
- [Ali Naqvi](https://github.com/naqvis) - creator and maintainer |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
require "../src/vips" | ||
|
||
if ARGV.size < 2 | ||
puts "Usage: #{__FILE__} input_image output" | ||
exit(1) | ||
end | ||
|
||
im = Vips::Image.new_from_file(ARGV[0], access: Vips::Enums::Access::Sequential) | ||
|
||
left_text, _ = Vips::Image.text("left corner", dpi: 300) | ||
left = left_text.embed(50, 50, im.width, 150) | ||
|
||
right_text, _ = Vips::Image.text("right corner", dpi: 300) | ||
right = right_text.embed(im.width - right_text.width - 50, 50, im.width, 150) | ||
|
||
footer = (left | right).ifthenelse(0, [255, 0, 0], blend: true) | ||
|
||
im = im.insert(footer, 0, im.height, expand: true) | ||
|
||
im.write_to_file ARGV[1] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
require "../src/vips" | ||
|
||
# Edge detection | ||
|
||
if ARGV.size < 2 | ||
puts "Usage: #{__FILE__} input_image output" | ||
exit(1) | ||
end | ||
|
||
im = Vips::Image.new_from_file(ARGV[0], access: Vips::Enums::Access::Sequential) | ||
|
||
# optionall convert to greyscale | ||
# mono = im.colourspace(Vips::Enums::Interpretation::Bw) | ||
# canny = mono.canny(sigma: 1.4, precision: Vips::Enums::Precision::Integer) | ||
|
||
# Canny edge detector | ||
canny = im.canny(sigma: 1.4, precision: Vips::Enums::Precision::Integer) | ||
|
||
# Canny makes a float image, scale the output up to make it visible | ||
scale = canny * 64 | ||
|
||
scale.write_to_file(ARGV[1]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
require "../src/vips" | ||
|
||
# Captcha generator | ||
# Reference: https://github.com/libvips/libvips/issues/898 | ||
|
||
def wobble(image) | ||
# a warp image is a 2D grid containing the new coordinates of each pixel with | ||
# the new x in band 0 and the new y in band 1 | ||
# | ||
# you can also use a complex image | ||
# | ||
# start from a low-res XY image and distort it | ||
|
||
xy = Vips::Image.xyz(image.width // 20, image.height // 20) | ||
x_distort = Vips::Image.gaussnoise(xy.width, xy.height) | ||
y_distort = Vips::Image.gaussnoise(xy.width, xy.height) | ||
xy += (x_distort.bandjoin(y_distort) / 150) | ||
xy = xy.resize(20) | ||
xy *= 20 | ||
|
||
# apply the warp | ||
image.mapim(xy) | ||
end | ||
|
||
if ARGV.size < 2 | ||
puts "Usage: #{__FILE__} outputfile text" | ||
exit(1) | ||
end | ||
|
||
text_layer = Vips::Image.black 1, 1 | ||
x_position = 0 | ||
|
||
ARGV[1].each_char do |c| | ||
if c.ascii_whitespace? | ||
x_position += 50 | ||
next | ||
end | ||
|
||
letter, _ = Vips::Image.text(c.to_s, dpi: 600) | ||
|
||
image = letter.gravity(Vips::Enums::CompassDirection::Centre, letter.width + 50, letter.height + 50) | ||
|
||
# random scale and rotate | ||
image = image.similarity(scale: Random.rand(0.2) + 0.8, | ||
angle: Random.rand(40) - 20) | ||
|
||
# random wobble | ||
image = wobble(image) | ||
|
||
# random color | ||
color = (1..3).map { Random.rand(255) } | ||
image = image.ifthenelse(color, 0, true) | ||
|
||
# tag as 9-bit srgb | ||
image = image.copy(interpretation: Vips::Enums::Interpretation::Srgb).cast(Vips::Enums::BandFormat::Uchar) | ||
|
||
# position at our write position in the image | ||
image = image.embed(x_position, 0, image.width + x_position, image.height) | ||
|
||
text_layer += image | ||
text_layer = text_layer.cast(Vips::Enums::BandFormat::Uchar) | ||
|
||
x_position += letter.width | ||
end | ||
|
||
# remove any unused edges | ||
text_layer = text_layer.crop(*text_layer.find_trim(background: 0)) | ||
|
||
# make an alpha for the text layer: just a mono version of the image, but scaled | ||
# up so letters themeselves are not transparent | ||
alpha = (text_layer.colourspace(Vips::Enums::Interpretation::Bw) * 3).cast(Vips::Enums::BandFormat::Uchar) | ||
text_layer = text_layer.bandjoin(alpha) | ||
|
||
# make a white background with random speckles | ||
speckles = Vips::Image.gaussnoise(text_layer.width, text_layer.height, mean: 400, sigma: 200) | ||
background = (1..3).reduce(speckles) do |a, _| | ||
a.bandjoin(Vips::Image.gaussnoise(text_layer.width, text_layer.height, mean: 400, sigma: 200)) | ||
.copy(interpretation: Vips::Enums::Interpretation::Srgb).cast(Vips::Enums::BandFormat::Uchar) | ||
end | ||
|
||
# composite the text over the background | ||
final = background.composite(text_layer, Vips::Enums::BlendMode::Over) | ||
final.write_to_file ARGV[0] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
require "../src/vips" | ||
|
||
# Conversion | ||
# Reference: https://github.com/libvips/lua-vips/blob/master/example/combine.lua | ||
|
||
if ARGV.size < 3 | ||
puts "Usage: #{__FILE__} input_image watermark_image output" | ||
exit(1) | ||
end | ||
|
||
LEFT = 100 | ||
TOP = 100 | ||
|
||
im = Vips::Image.new_from_file(ARGV[0], access: Vips::Enums::Access::Sequential) | ||
wm = Vips::Image.new_from_file(ARGV[1], access: Vips::Enums::Access::Sequential) | ||
|
||
width = wm.width | ||
height = wm.height | ||
|
||
# extract related area from main image | ||
base = im.crop(LEFT, TOP, width, height) | ||
|
||
# composite the two areas using the PDF "over" mode | ||
composite = base.composite(wm, Vips::Enums::BlendMode::Over) | ||
|
||
# the result will have an alpha, and our base image does not .. we must flatten | ||
# out the alpha before we can insert it back into a plain RGB JPG image | ||
rgb = composite.flatten | ||
|
||
# insert composite back in to main image on related area | ||
combined = im.insert(rgb, LEFT, TOP) | ||
combined.write_to_file(ARGV[2]) |
Oops, something went wrong.