Skip to content
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

Enabling zoom1 zooms image, scaling artifacts #85

Open
abaeyens opened this issue Nov 30, 2024 · 0 comments
Open

Enabling zoom1 zooms image, scaling artifacts #85

abaeyens opened this issue Nov 30, 2024 · 0 comments

Comments

@abaeyens
Copy link

Expected behavior

When zoom1 is enabled, the shown image has the exact same shape as the received image, no scaling whatsoever, just a 1-to-1 mapping. Example (produced with a patched version of rqt_image_view):
rqt_zoom1_expected

Observed behavior

After enabling zoom1, the received image is scaled to be 1 px larger on both width and height. This introduces aliasing, example:
rqt_zoom1_observed

To reproduce

  1. Start from a ROS 2 Jazzy environment
  2. publish a test image, script:
#! /usr/bin/env python3
import cv_bridge
import numpy as np
import rclpy.node
from sensor_msgs.msg import Image

def create_test_image():
    # all black
    img = np.zeros((320, 640), dtype=np.uint8)
    # vertical white lines on the left half
    img[:,:img.shape[1]//2:2] = 255
    # horizontal white lines on the right half
    img[::2, img.shape[1]//2:] = 255
    return img

class ImagePublisher(rclpy.node.Node):
    def __init__(self):
        super().__init__('image_publisher')
        self.publisher_ = self.create_publisher(Image, 'test_image', 10)
        self.timer_ = self.create_timer(0.5, self.publish_image)
        self.image_msg_ = cv_bridge.CvBridge().cv2_to_imgmsg(
            create_test_image(), 'mono8')
    
    def publish_image(self):
        self.publisher_.publish(self.image_msg_)

if __name__ == '__main__':
    rclpy.init()
    image_publisher = ImagePublisher()
    rclpy.spin(image_publisher)
    image_publisher.destroy_node()
    rclpy.shutdown()
  1. Launch RQt (ros2 run rqt_image_view rqt_image_view test_image) and in image view click the zoom1 button.

Version

Recent ROS 2 Jazzy Ubuntu Noble install, though this behavior also arises in ROS 1 Noetic and probably older as well.

Presumed cause

Enabling zoom1 results in the line ui_.image_frame->setInnerFrameFixedSize(ui_.image_frame->getImage().size()); being called, which in turn calls setInnerFrameMinimumSize and setInnerFrameMaximumSize. They set the frame size to a larger size than the actual image:

  int border = lineWidth();
  QSize new_size = size;
  new_size += QSize(2 * border, 2 * border);

with the linewidth being set in image_view.ui to 1 px. When subsequently displaying the image with painter.drawImage, Qt notices that the resolutions are different and scales it, resulting in image artifacts.

I'd appreciate if someone could explain briefly (or point to an explanation) of why a (nonzero) border is used, as I don't immediately see any visual effect of it apart from the image aliasing artifacts.

Solution proposals

I currently see three approaches to resolve this:

  1. Set lineWidth in image_view.ui to zero and, as it's zero anyway, remove the border accounting, simplifying the code. From my initial test, this works perfectly, though perhaps this breaks something else?
  2. When setting the frame resolution when zoom1 is enabled, compensate for the border that will be taken into account, or set it directly without taking the border into account.
  3. When drawing the image, account for the border, resulting in the border being visible when zoom1 is enabled. Though I haven't yet seen a border when zoom1 isn't enabled.

Thanks for going through this; I'd be glad to work out any of these proposals and make a PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant