ruby-vips icon indicating copy to clipboard operation
ruby-vips copied to clipboard

Can't figure out how to rotate around coordinates, and there is some error in docs

Open Nakilon opened this issue 2 years ago • 7 comments

I need to rotate this image around the center of the circle with known coordinates

temp

[x,y,angle]
[398, 290, -2.4995608216061647]
img.rotate(angle * 180 / Math::PI, idx: x, idy: y, odx: x, ody: y)

but it looks wrong, the position has shifted

temp

how do I do it without shifting the resulting circle?

Also the https://www.libvips.org/API/current/libvips-resample.html#vips-rotate doc has the ody arg twice.

vips-8.11.3-Wed Aug 11 09:29:27 UTC 2021
Vips::VERSION "2.1.4"

Nakilon avatar May 08 '23 17:05 Nakilon

After playing around I suppose rotation shifts the image, i.e. you don't know the odx/ody ahead, because it all shifts to hold the top left corner pixel inside the image. It feels wrong.

Nakilon avatar May 08 '23 18:05 Nakilon

Hi @Nakilon,

Also the https://www.libvips.org/API/current/libvips-resample.html#vips-rotate doc has the ody arg twice.

Oops! Thanks, I patched it.

I'll make you a rotate example, I have some code somewhere.

jcupitt avatar May 09 '23 09:05 jcupitt

Sorry, just to be clear, you'd like an image the same width and height as the input, and with the circle in the same position, but the circle should be rotated so the leaf points roughly upwards, is that right?

jcupitt avatar May 09 '23 16:05 jcupitt

Here's a thing that does ^^^ my guess.

#!/usr/bin/ruby

require 'vips'

x, y, angle = 398, 290, -2.4995608216061647

image = Vips::Image.new_from_file ARGV[0]

# add an alpha so we can see the image edges
image = image.add_alpha

# the [[a, b], [c, d]] affine transform we want (simple rotate)
a = Math.cos(angle)
b = -Math.sin(angle)
c = -b
d = a

# point (x, y) in the input goes to this coordinate in the output
x_after = a * x + b * y
y_after = c * x + d * y

# therefore to keep (x, y) in the same place, we need to move the output area
# by the difference
dx = x_after - x
dy = y_after - y

image = image.affine [a, b, c, d], oarea: [dx, dy, image.width, image.height]

image.write_to_file ARGV[1]

I see:

$ ./naklion.rb ~/pics/naklion.png x2.png

To make:

x2

You don't need the alpha, but it's an easy way to see where the image edges are.

jcupitt avatar May 09 '23 16:05 jcupitt

... perhaps rotate should have an oarea parameter. It would make this a little simpler.

jcupitt avatar May 09 '23 16:05 jcupitt

Yes, you understood right. Thank you. I predicted it would need to calculate these sinuses by yourself. And yes, it's very inconvinient. As a user of this method I don't expect the rotation to change the size of image, because it's kind of unpredictable. After have no luck with it I ended up with just pixel-by-pixel projecting by myself ..(

Vips::Image.bandjoin( img.bandsplit.map do |band|
  array = band.to_a
  Vips::Image.new_from_array( array.map.with_index do |row, i|
    row.map.with_index do |c, j|
      (inner+outer)/2 < Math.hypot(x-j, y-i) ? c : ((
        ii = i - y
        jj = j - x
        array[jj * Math::sin(angle) + ii * Math::cos(angle) + y]\
             [jj * Math::cos(angle) - ii * Math::sin(angle) + x]
      ))
    end
  end ).cast :uchar
end )

otherwise I would need kind of reverse engineer this transformation method.

Nakilon avatar May 09 '23 17:05 Nakilon

The goal was to transform this image to this image

Nakilon avatar May 09 '23 17:05 Nakilon