Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python OpenCV Drawing Line Width

When specifying a line thickness greater than 1, the resulting line width drawn by cv2.line() is wider than specified. Specifying a thickness of 1,2,3,4,5,6 produces a line width of 1,3,5,5,7,7 respectively. I have tried using different lineType values (4,8,16) and sub-pixel point locations with the shift parameter with no effect on the line width. Am I doing something wrong?

For example:

import numpy as np
import cv2

a = np.zeros((10,10), dtype=np.uint8)
cv2.line(a, (0,4), (9,4), 1, 2)
print(a)

produces:

 [[0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [1 1 1 1 1 1 1 1 1 1]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]
like image 387
Brian Trotter Avatar asked Mar 20 '23 02:03

Brian Trotter


1 Answers

Your question is a simple one, and the answer is a simple one, as well: No, you are not doing anything wrong. The implementation of the line drawing in OpenCV seems to be quite basic.

I tried this with several line widths and also with fractional starting and ending positions. The line width parameter does not really work. The simple Brezenham lines (single-pixel lines from integer pixel positions) are fine, but the thicker lines are not good.

See this test code:

import numpy as np
import cv2

a = np.zeros((200,200),dtype=np.uint8)
for u in np.exp(np.linspace(0,1j*2*np.pi,25)):
    cv2.line(a, (100, 100), (int(100+100*u.real+.5), int(100+100*u.imag+.5)), 255, 2)
cv2.imwrite('lines.png', a)

What you get is this:

enter image description here

The line thickness depends heavily on the angle; diagonal lines are much thicker than horizontal or vertical lines. Also, the thickness of the lines is anyway wrong (3 at thinnest) as you noticed. Using anti-alias does not make a significant change.

However, if this is done with fractional coordinates, the thickness variation is smaller (but the thickness is still wrong):

import numpy as np
import cv2

a = np.zeros((200,200),dtype=np.uint8)
for u in np.exp(np.linspace(0,1j*2*np.pi,25)):
    cv2.line(a, (1600, 1600), (int(1600+1600*u.real+.5), int(1600+1600*u.imag+.5)), 255, 2, shift=4)
cv2.imwrite('lines_shift.png', a)

enter image description here

Also, the problem seems to vanish with thick lines (thickness set to 5, real thickness 7, no fractional coordinates). This suggest that the rotation-dependent error is additive:

enter image description here

Then let's try to draw overlapping files with widths 10, 11, and 12 with different shades of gray:

import numpy as np
import cv2

a = np.zeros((200,200),dtype=np.uint8)
for u in np.exp(np.linspace(0,1j*2*np.pi,25)):
    cv2.line(a, (100, 100), (int(100+100*u.real+.5), int(100+100*u.imag+.5)), 255, 12)
    cv2.line(a, (100, 100), (int(100+100*u.real+.5), int(100+100*u.imag+.5)), 128, 11)
    cv2.line(a, (100, 100), (int(100+100*u.real+.5), int(100+100*u.imag+.5)), 64, 10)
cv2.imwrite('lines_variation.png', a)

enter image description here

In principle there should be three colours seen, but in practice only two are visible. This means that the lines with widths 11 and 12 are drawn with the same width (13). Any tricks with the fractional coordinates produce the same result.

Summary: Only odd line widths are available. Narrow lines have large relative width errors if fractional (shifted) coordinates are not used. If you need to have more control, draw thicker line at a higher resolution and then downsample. (Clumsy, I know.)

The tests above have been carried ouy with OpenCV version 2.4.9.

like image 67
DrV Avatar answered Mar 27 '23 13:03

DrV