Skip to content

Commit

Permalink
Debug context zooming in oriented images.
Browse files Browse the repository at this point in the history
  • Loading branch information
cmbruns committed May 28, 2024
1 parent 7f084ac commit 7eabf45
Show file tree
Hide file tree
Showing 3 changed files with 46 additions and 40 deletions.
7 changes: 4 additions & 3 deletions vmg/image.vert
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,19 @@ mat2 scale2(float sx, float sy) {

uniform sampler2D image;
uniform float window_zoom = 1.0;
uniform vec2 image_center_tex = vec2(0.5, 0.5);
uniform vec2 image_center_img = vec2(0.5, 0.5);
uniform ivec2 window_size;
uniform mat2 raw_rot_ont = mat2(1);
out vec2 p_tex;

// coordinate systems:
// ndc - normalized device coordinates ; range -1,1 ; origin at center ; positive y up
// win - window ; origin at center ; units window pixels ; positive y up ; origin at center
// cwn - window ; origin at center ; units window pixels ; positive y up ; origin at center
// ont - oriented image coordinates ; units image pixels ; positive y down ; origin at center
// raw - raw image coordinates (before EXIF orientation correction) ; origin at center
// ulc - raw image with origin at upper left
// tex - texture coordinates ; range (0, 1)
// img - image texture coordinates, but with orientation correction

void main() {
// set position for each corner vertex
Expand All @@ -67,6 +68,6 @@ void main() {
vec2 p_raw = raw_rot_ont * p_ont; // raw image pixels
vec2 p_ulc = p_raw + 0.5 * vec2(image_size_raw); // move origin from center to upper left corner
// image_center is in oriented texture coordinates w/ ulc origin
vec2 d_center_tex = raw_rot_ont * (image_center_tex - vec2(0.5));
vec2 d_center_tex = raw_rot_ont * (image_center_img - vec2(0.5));
p_tex = (1.0 / image_size_raw) * p_ulc + d_center_tex;
}
8 changes: 4 additions & 4 deletions vmg/image_widget_gl.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,16 +124,16 @@ def _hover_pixel(self, win_xy: WindowPos) -> bool:
pxl_x = (img_x + 0.5) * w
pxl_y = (img_y + 0.5) * h
self.request_message.emit(
f"p_texc = {p_texc}"
# f"p_texc = {p_texc}"
# f"{win_xy}; "
# f"rc_scale = {rc_scale}; "
# f"{ndc}; "
# f"cwn = {p_cwn}; "
# f"p_ont = {p_ont}"
# f"p_ulimg = {p_ulimg}"
# f"cimg dims = [{ont_width},{ont_height}]; "
f"image = [{img_x:.4f}, {img_y:.4f}]; "
# f"image pixel = [{pxl_x:.1f}, {pxl_y:.1f}]"
# f"image = [{img_x:.4f}, {img_y:.4f}]; "
f"image pixel = [{pxl_x:.1f}, {pxl_y:.1f}]"
, 2000)
return False # Nothing changed, so no update needed

Expand Down Expand Up @@ -166,7 +166,7 @@ def mouseMoveEvent(self, event):
dx = event.pos().x() - self.previous_mouse_position.x()
dy = event.pos().y() - self.previous_mouse_position.y()
self.view_state.drag_relative(dx, dy, self)
self.sphere_view_state.drag_relative(dx, dy, self)
self.sphere_view_state.drag_relative(dx, dy, self) # TODO: redundant?
self.previous_mouse_position = event.pos()
self.update()

Expand Down
71 changes: 38 additions & 33 deletions vmg/view_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,25 @@ def paint_gl(self, state, gl_widget) -> None:

class RectangularViewState(IViewState):
def __init__(self):
self.image_center_tex = [0.5, 0.5] # In GL-like oriented texture coordinates
self.image_center_img = [0.5, 0.5] # In GL-like oriented texture coordinates
self.window_zoom = 1.0 # in windows per image
self.pixel_filter = PixelFilter.CATMULL_ROM

def clamp_center(self):
# Keep the center point on the actual image itself
self.image_center_tex[0] = max(0.0, self.image_center_tex[0])
self.image_center_tex[1] = max(0.0, self.image_center_tex[1])
self.image_center_tex[0] = min(1.0, self.image_center_tex[0])
self.image_center_tex[1] = min(1.0, self.image_center_tex[1])
self.image_center_img[0] = max(0.0, self.image_center_img[0])
self.image_center_img[1] = max(0.0, self.image_center_img[1])
self.image_center_img[0] = min(1.0, self.image_center_img[0])
self.image_center_img[1] = min(1.0, self.image_center_img[1])
z = self.window_zoom
if z <= 1:
self.image_center_tex[0] = 0.5
self.image_center_tex[1] = 0.5
self.image_center_img[0] = 0.5
self.image_center_img[1] = 0.5
else:
self.image_center_tex[0] = min(self.image_center_tex[0], 1 - 0.5 / z)
self.image_center_tex[0] = max(self.image_center_tex[0], 0.5 / z)
self.image_center_tex[1] = min(self.image_center_tex[1], 1 - 0.5 / z)
self.image_center_tex[1] = max(self.image_center_tex[1], 0.5 / z)
self.image_center_img[0] = min(self.image_center_img[0], 1 - 0.5 / z)
self.image_center_img[0] = max(self.image_center_img[0], 0.5 / z)
self.image_center_img[1] = min(self.image_center_img[1], 1 - 0.5 / z)
self.image_center_img[1] = max(self.image_center_img[1], 0.5 / z)

def drag_relative(self, dx, dy, gl_widget):
# Compute scales for converting window pixels to ndc coordinates
Expand All @@ -76,25 +76,31 @@ def drag_relative(self, dx, dy, gl_widget):
x_scale /= ratio_ratio
else:
y_scale *= ratio_ratio
self.image_center_tex[0] += dx / x_scale
self.image_center_tex[1] += dy / y_scale
self.image_center_img[0] += dx / x_scale
self.image_center_img[1] += dy / y_scale
self.clamp_center()

def image_for_window(self, wpos: WindowPos, gl_widget):
# TODO - not correct for 90 degree rotated images
x_scale = y_scale = self.window_zoom
ratio_ratio = gl_widget.width() * gl_widget.image.shape[0] / (gl_widget.height() * gl_widget.image.shape[1])
if ratio_ratio > 1:
x_scale /= ratio_ratio
def image_for_window(self, p_qwn: WindowPos, gl_widget):
p_cwn = numpy.array(p_qwn) - (gl_widget.width() / 2, gl_widget.height() / 2) # origin at center
p_cwn[1] *= -1 # flip y
window_aspect = gl_widget.width() / gl_widget.height()
image_size_raw = numpy.flip(gl_widget.image.shape[0:2])
image_size_ont = [abs(x) for x in gl_widget.raw_rot_ont.T @ image_size_raw]
image_aspect_ont = image_size_ont[0] / image_size_ont[1]
if window_aspect > image_aspect_ont:
rc_scale = image_size_ont[1] / gl_widget.height() / self.window_zoom
else:
y_scale *= ratio_ratio
wx = (wpos[0] - gl_widget.width() / 2) / gl_widget.width() / x_scale
wy = (wpos[1] - gl_widget.height() / 2) / gl_widget.height() / y_scale
return wx, wy
rc_scale = image_size_ont[0] / gl_widget.width() / self.window_zoom
p_ont = numpy.array([
[rc_scale, 0],
[0, -rc_scale],
], dtype=numpy.float32) @ p_cwn
p_img = p_ont / image_size_ont # origin at center, oriented, texture coordinates
return p_img

def reset(self):
self.window_zoom = 1.0
self.image_center_tex = [0.5, 0.5]
self.image_center_img = [0.5, 0.5]

def zoom_relative(self, zoom_factor: float, zoom_center: WindowPos, gl_widget):
new_zoom = self.window_zoom * zoom_factor
Expand All @@ -107,9 +113,8 @@ def zoom_relative(self, zoom_factor: float, zoom_center: WindowPos, gl_widget):
z1 = [x * zoom_factor for x in z2] # Before position
dx = z2[0] - z1[0]
dy = z2[1] - z1[1]
dx, dy = (gl_widget.raw_rot_ont @ [dx, dy])
self.image_center_tex[0] -= dx
self.image_center_tex[1] -= dy
self.image_center_img[0] -= dx
self.image_center_img[1] -= dy
# Limit zoom-out because you never need more than twice the image dimension to move around
self.window_zoom = max(1.0, self.window_zoom)
self.clamp_center()
Expand All @@ -125,12 +130,12 @@ def __init__(self):
self.projection = Projection360.STEREOGRAPHIC

def clamp(self):
self.pitch = min(self.pitch, math.pi / 2)
self.pitch = max(self.pitch, -math.pi / 2)
self.pitch = min(self.pitch, math.pi / 2.0)
self.pitch = max(self.pitch, -math.pi / 2.0)
self.window_zoom = max(self.window_zoom, 0.05)

def drag_relative(self, dx, dy, gl_widget):
win_size = (gl_widget.width() + gl_widget.height()) / 2;
win_size = (gl_widget.width() + gl_widget.height()) / 2
self.yaw += dx / win_size / self.window_zoom
c = math.cos(self.yaw)
s = math.sin(self.yaw)
Expand Down Expand Up @@ -183,7 +188,7 @@ def __init__(self):
self.shader = None
self.zoom_location = None
self.window_size_location = None
self.image_center_tex_location = None
self.image_center_img_location = None
self.pixelFilter_location = None
self.raw_rot_ont_location = None

Expand All @@ -198,7 +203,7 @@ def initialize_gl(self) -> None:
GL.glLinkProgram(self.shader)
self.zoom_location = GL.glGetUniformLocation(self.shader, "window_zoom")
self.window_size_location = GL.glGetUniformLocation(self.shader, "window_size")
self.image_center_tex_location = GL.glGetUniformLocation(self.shader, "image_center_tex")
self.image_center_img_location = GL.glGetUniformLocation(self.shader, "image_center_img")
self.pixelFilter_location = GL.glGetUniformLocation(self.shader, "pixelFilter")
self.raw_rot_ont_location = GL.glGetUniformLocation(self.shader, "raw_rot_ont")

Expand All @@ -211,7 +216,7 @@ def paint_gl(self, state, gl_widget) -> None:
GL.glUseProgram(self.shader)
GL.glUniform1f(self.zoom_location, state.window_zoom)
GL.glUniform2i(self.window_size_location, gl_widget.width(), gl_widget.height())
GL.glUniform2f(self.image_center_tex_location, *state.image_center_tex)
GL.glUniform2f(self.image_center_img_location, *state.image_center_img)
GL.glUniform1i(self.pixelFilter_location, state.pixel_filter.value)
GL.glUniformMatrix2fv(self.raw_rot_ont_location, 1, True, gl_widget.raw_rot_ont)
GL.glDrawArrays(GL.GL_TRIANGLE_STRIP, 0, 4)
Expand Down

0 comments on commit 7eabf45

Please sign in to comment.