New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Blur examples #60
Comments
Hi @dariocravero, I had a look and you're right, the thing to make a Gaussian mask is not wrapped by ruby-vips. Strange, I wonder how it got overlooked. I'm sorry you wasted so much time looking. The next version is almost done and it's a complete rewrite of the binding. The new version is dynamic, so all vips operations are automatically wrapped. It should fix all this. In the meantime, you need to calculate the mask yourself. I wrote a small program for you: #!/usr/bin/ruby
include Math
require 'rubygems'
require 'vips'
# make a 1D int gaussian mask suitable for a separable convolution
#
# sigma is (roughly) radius, min_ampl is the minimum amplitude we consider, it
# sets how far out the mask goes
#
# we normalise to 20 so that the mask sum stays under 255 for most blurs ...
# this will let vips use its fast SSE path for 8-bit images
def gaussmask(sigma, min_ampl)
sigma2 = 2.0 * sigma ** 2.0
# find the size of the mask
max_size = -1
(1..10000).each do |x|
if exp(-x ** 2.0 / sigma2) < min_ampl
max_size = x
break
end
end
if max_size == -1
puts "mask too large"
return nil
end
width = max_size * 2 + 1
mask = []
(0...width).each do |x|
xo = x - width / 2
mask << (20.0 * exp(-xo ** 2 / sigma2)).round
end
puts "mask = #{mask}"
VIPS::Mask.new [mask], mask.reduce(:+), 0
end
im = VIPS::Image.new(ARGV[0])
im = im.convsep(gaussmask(4, 0.2))
im.write(ARGV[1]) Hopefully it's clear. The |
That function is a quick copy-paste of this C operation: https://github.com/jcupitt/libvips/blob/master/libvips/create/gaussmat.c You might get some more ideas from that. There are some other mask makers in that directory that might be useful. |
Wow!! :) Thanks so much!! That will totally do for now... :) The difference with
Here are the time outputs while blurring an image with a radius of
That's on a MacBook Pro with a 2.3 GHz i5 and 16GB of memory. Dynamic bindings are probably the best, @DAddYE never released ffi-gen but it's supposed to do just that :). |
If you want to work on the new ruby binding, that would be great! I've almost done the Python one, it would make a good starting point: https://github.com/jcupitt/libvips/blob/master/python/vips8/vips.py It's based on gobject-introspection: https://developer.gnome.org/gi/stable/ The idea is that the C library is marked up with some special comments. These are parsed by gobject-introspection to generate a typelib. The API docs are generated from these comments too, eg.: http://www.vips.ecs.soton.ac.uk/supported/7.40/doc/html/libvips/libvips-conversion.html#vips-join The typelib is loaded by a Python program using pygobject: https://wiki.gnome.org/action/show/Projects/PyGObject Or a Ruby program using the equivalent gem: https://rubygems.org/gems/gobject-introspection Now you can call directly into the C library from Ruby. The API ends up being rather un-Pythonic (or rather un-Ruby-ish), so you write a small layer over that to make a nice, high-level binding. vips8 has some extra introspection stuff it provides to expose features like optional arguments and default values. You end up with this nice API, all generated at runtime in only a few hundred lines of Python, with no C required. This should make the binding more portable, hopefully. Platforms like Windows will finally get support. a = Vips.Image.new_from_file(sys.argv[1])
b = Vips.Image.new_from_file(sys.argv[2])
c = a.join(b, Vips.Direction.HORIZONTAL,
expand = True,
shim = 100,
align = Vips.Align.CENTRE,
background = [128, 255, 128])
c.write_to_file(sys.argv[3]) There are some test Python files which show the process. https://github.com/jcupitt/libvips/blob/master/python/try.py Then My current work plan is:
I'd be very happy to hand you the Ruby part, if you have time. |
Also, try your benchmark on a large image, perhaps 10,000 x 10,000 pixel RGB jpeg. You'll see a huge difference in memory use as well. If you add http://libvips.blogspot.co.uk/2012/06/how-libvips-opens-file.html |
Last post, I'm not sure I was very clear about the gobject-introspection stuff.
It uses the vips8 introspection stuff, invoked via goi, to look for and call vips8 operations, like vips_join() (join two images together). The set of operations will change: they will gain new optional arguments, new operations will be added (they can even be added at runtime via plugins), so this part of the binding is extremely dynamic. Summary: the vips8 Python and Ruby bindings should automatically update as needed in the future. They are written in pure Python (or Ruby) so should be trivially portable. They should only be a few hundred lines of code. It should be possible to generate the docs automatically too, but I've not really looked into that yet. |
Brilliant! Thanks for the detailed explanation on how to get that going. Introspecting the library to automatically build the bindings is very clever. I will try to give it a go any time soon but can't promise anything as we're currently in the middle of releasing a good few things over the next few weeks. What's your expected timeline on it? I could probably schedule it in :) |
Hey John, Here's a more Ruby-esque version of the mask:
Any thoughts on it? Hope you like it :). The constants would generally be extracted into some sort of class but that will do for the example. Taking carrierwave-vips as a base, I'm making it agnostic of the uploader (or file manager) and building an operation-oriented processing layer. Should be releasing it today :). EDIT: replaced |
Oh much neater, nice. Why do you need the I'll be starting the ruby-vips8 binding in a couple of months, so make a start before then if you'd like to take it over. |
As a matter of fact, you don't. I guess auld habits die hard :P :). reduce doesn't need it. Good, will take that into account then! |
Gem released! vips-process. GitHub repo. Would love to hear your thoughts on it @jcupitt :) |
Wow nice! That's much more Ruby-esque than anything I've tried. I noticed one tiny thing on a quick read, you have:
Of course sigma (the standard deviation of the gaussian) is a float. Don't suppose it makes much difference. |
Cool :) Updated!. |
I read a bit more. The README is getting easier to understand, heh. It's a nice way to specify a set of operations, it feels very declarative.
|
I did a blog post about the new Python binding: http://libvips.blogspot.co.uk/2014/10/image-annotation-with-pyvips8.html It has some timings and examples. The Ruby vips8 binding should get nicer in a similar way, hopefully. |
Sorry for the late reply. Thanks for the feedback. I reckon that Regarding the suggestions at the end of your previous post:
I haven't thought of those as I haven't had a use case but probably the answer will be yes.
True, need to have a look at that.
How would you go about implementing that? |
vipsthumbnail uses jpeg-shrink-on-load. It opens once to get the true image dimensions, calculates the shrink factor, then opens again, setting "shrink". Resampling methods: vipsthumbnail has a better one now, check the sources. The idea is to .shrink() less and .affine() more. This tends to preserve edges better. To prevent aliasing, you put a slight blur inbetween them. The lower sampling density gives a peak, the blur makes some lobes, and you end up with something close to lanczos2, the default ImageMagick shrinker. It's noticably better quality than the previous technique I was using. |
Perfect will make sure to check that out. Thanks! |
I came across this old issue by accident. ruby-vips8 is finally out as a gem: http://libvips.blogspot.co.uk/2016/01/ruby-vips-is-dead-long-live-ruby-vips8.html |
Hi @jcupitt,
Thanks for making this happen! :)
I wonder if you would have any snippets of code to share to apply gaussian blur to an image. I've been looking for hours and couldn't find anything online :(.
Thanks again,
Darío
The text was updated successfully, but these errors were encountered: