Andreas Unterweger
Andreas Unterweger

Reputation: 595

Pixels at arrow tip missing when using antialiasing

I am trying to draw an arrow with OpenCV 3.2:

#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>

using namespace cv;

int main()
{
  Mat image(480, 640, CV_8UC3, Scalar(255, 255, 255)); //White background
  Point from(320, 240); //Middle
  Point to(639, 240); //Right border
  arrowedLine(image, from, to, Vec3b(0, 0, 0), 1, LINE_AA, 0, 0.1);
  imshow("Arrow", image);
  waitKey(0);
  return 0;
}

An arrow is drawn, but at the tip some pixels are missing:

Arrow with missing tip pixels

To be more precise, two columns of pixels are not colored correctly (zoomed):

enter image description here

If I disable antialiasing, i.e., if I use

arrowedLine(image, from, to, Vec3b(0, 0, 0), 1, LINE_8, 0, 0.1);

instead (note the LINE_8 instead of LINE_AA), the pixels are there, albeit without antialiasing:

enter image description here

I am aware that antialiasing might rely on neighboring pixels, but it seems strange that pixels are not drawn at all at the borders instead of being drawn without antialiasing. Is there a workaround for this issue?

Increasing the X coordinate, e.g. to 640 or 641) makes the problem worse, i.e., more of the arrow head pixels disappear, while the tip still lacks nearly two complete pixel columns.

Extending and cropping the image would solve the neighboring pixels issue, but in my original use case, where the problem appeared, I cannot enlarge my image, i.e., its size must remain constant.

Upvotes: 3

Views: 413

Answers (1)

cbuchart
cbuchart

Reputation: 11555

After a quick review, I've found that OpenCV draws AA lines using a Gaussian filter, which contracts the final image.

As I've suggested in comments, you can implement your own function for the AA mode (you can call the original one if AA is disabled) extending the points manually (see code below to have an idea).

Other option may be to increase the line width when using AA.

You may also simulate the AA effect of OpenCV but on the final image (may be slower but helpful if you have many arrows). I'm not an OpenCV expert so I'll write a general scheme:

// Filter radius, the higher the stronger
const int kRadius = 3;

// Image is extended to fit pixels that are not going to be blurred
Mat blurred(480 + kRadius * 2, 640 + kRadius * 2, CV_8UC3, Scalar(255, 255, 255));

// Points moved a according to filter radius (need testing, but the idea is that)
Point from(320, 240 + kRadius);
Point to(639 + kRadius * 2, 240 + kRadius);

// Extended non-AA arrow
arrowedLine(blurred, ..., LINE_8, ...);

// Simulate AA
GaussianBlur(blurred, blurred, Size(kRadius, kRadius), ...);

// Crop image (be careful, it doesn't copy data)
Mat image = blurred(Rect(kRadius, kRadius, 640, 480));

Another option may be to draw the arrow in an image twice as large and the scale it down with a good smoothing filter.

Obviously, last two options will work only if you don't have any previous data on the image. If so, then use a transparent image for temporal drawing and overlay it at the end.

Upvotes: 3

Related Questions