简体   繁体   English

计算图像中两个像素之间的距离

[英]Calculate the distance between two pixels in an image

I'm trying to calculate the distance between two pixels, but in a specific way.我正在尝试以特定方式计算两个像素之间的距离。 I need to know the thickness of the red line in the image, so my idea was to go through the image by columns, find the coordinates of the two edge points and calculate the distance between them.我需要知道图像中红线的粗细,所以我的想法是go逐列通过图像,找到两个边缘点的坐标并计算它们之间的距离。 Do this for the two lines, both top and bottom.对顶部和底部的两条线执行此操作。 Do this for each column and then calculate the average.对每一列执行此操作,然后计算平均值。 I should also do a conversion from pixels to real scale.我还应该进行从像素到真实比例的转换。

This is my code for now:这是我现在的代码:

# Make numpy array from image
npimage = np.array(image)

# Describe what a single red pixel looks like
red = np.array([255, 0, 0], dtype=np.uint8)   

firs_point = 0
first_find = False
for i in range(image.width):
    column = npimage[:,i]
    for row in column:
        comparison = row == red
        equal_arrays = comparison.all()
        if equal_arrays == True and first_find == False:
                first_x_coord = i
                first_find = True

I can't get the coordinates.我无法获得坐标。 Can someone help me please?有人能帮助我吗? Of course, if there are more optimal ways to calculate it, I will be happy to accept proposals.当然,如果有更优化的计算方法,我很乐意接受建议。 I am very new!我很新! Thank you very much!非常感谢!

using opencv:使用 opencv:

img = cv2.imread(image_path)
average_line_width = np.average(np.count_nonzero((img[:,:,:]==np.array([0,0,255])).all(2),axis=0))/2
print(average_line_width)

using pil使用 pil

img = np.asarray(Image.open(image_path))
average_line_width = np.average(np.count_nonzero((img[:,:,:]==np.array([255,0,0])).all(2),axis=0))/2
print(average_line_width)

output in both cases: output 在这两种情况下:

18.430701754385964

After properly masking all red pixels, you can calculate the cumulative sum per each column in that mask:正确屏蔽所有红色像素后,您可以计算该掩码中每列的累积总和:

累计金额

Below each red line, you have a large area with a constant value: Below the first red line, it's the thickness of that red line.在每条红线下方,您有一个具有恒定值的大面积:在第一条红线下方,是那条红线的粗细。 Below the second red line, it's the cumulative thickness of both red lines, and so on (if there would be even more red lines).在第二条红线下方,是两条红线的累积粗细,以此类推(如果有更多红线的话)。

So, now, for each column, calculate the histogram from the cumulative sum, and filter out these peaks;所以,现在,对于每一列,从累积和计算直方图,并过滤掉这些峰值; leaving out the 0 in the histogram, that'd be the large black area at the top.忽略直方图中的0 ,那将是顶部的大黑色区域。 Per column, you get the above mentioned (cumulative) thickness values for all red lines.每列,您将获得所有红线的上述(累积)厚度值。 The remainder is to extract the actual, single thickness values, and calculate the mean over all those.剩下的就是提取实际的单个厚度值,并计算所有这些值的平均值。

Here's my code:这是我的代码:

import cv2
import numpy as np

# Read image
img = cv2.imread('Dc4zq.png')

# Mask RGB pure red
mask = (img == [0, 0, 255]).all(axis=2)

# We check for two lines
n = 2

# Cumulative sum for each column
cs = np.cumsum(mask, axis=0)

# Thickness values for each column
tvs = np.zeros((n, img.shape[1]))
for c in range(img.shape[1]):

    # Calculate histogram of cumulative sum for a column
    hist = np.histogram(cs[:, c], bins=np.arange(img.shape[1]+1))

    # Get n highest histogram values
    # These are the single thickness values for a column
    tv = np.sort(np.argsort(hist[0][1:])[::-1][0:n]+1)
    tv[1:] -= tv[:-1]
    tvs[:, c] = tv

# Get mean thickness value
mtv = np.mean(tvs.flatten())
print('Mean thickness value:', mtv)

The final result is:最终结果是:

Mean thickness value: 18.92982456140351
----------------------------------------
System information
----------------------------------------
Platform:      Windows-10-10.0.16299-SP0
Python:        3.9.1
NumPy:         1.20.1
OpenCV:        4.5.1
----------------------------------------

EDIT: I'll provide some more details on the "NumPy magic" involved.编辑:我将提供有关所涉及的“NumPy 魔法”的更多细节。

# Calculate the histogram of the cumulative sum for a single column
hist = np.histogram(cs[:, c], bins=np.arange(img.shape[1] + 1))

Here, bins represent the intervals for the histogram, ie [0, 1] , [1, 2] , and so on.这里, bins表示直方图的区间,即[0, 1][1, 2]等。 To also get the last interval [569, 570] , you need to use img.shape[1] + 1 in the np.arange call, because the right limit is not included in np.arange .要获得最后一个区间[569, 570] ,您需要在np.arange调用中使用img.shape[1] + 1 ,因为np.arange中不包含右限制。

# Get the actual histogram starting from bin 1
hist = hist[0][1:]

In general, np.histogram returns a tuple, where the first element is the actual histogram.通常, np.histogram返回一个元组,其中第一个元素是实际的直方图。 We extract that, and only look at all bins larger 0 (remember, the large black area).我们提取它,并且只查看所有大于0的 bin(请记住,大的黑色区域)。

Now, let's disassemble this code:现在,让我们反汇编这段代码:

tv = np.sort(np.argsort(hist[0][1:])[::-1][0:n]+1)

This line can be rewritten as:这一行可以改写为:

# Get the actual histogram starting from bin 1
hist = hist[0][1:]

# Get indices of sorted histogram; these are the actual bins
hist_idx = np.argsort(hist)

# Reverse the found indices, since we want those bins with the highest counts
hist_idx = hist_idx[::-1]

# From that indices, we only want the first n elements (assuming there are n red lines)
hist_idx = hist_idx[:n]

# Add 1, because we cut the 0 bin
hist_idx = hist_idx + 1

# As a preparation: Sort the (cumulative) thickness values
tv = np.sort(hist_idx)

By now, we have the (cumulative) thickness values for each column.到目前为止,我们已经有了每列的(累积)厚度值。 To reconstruct the actual, single thickness values, we need the "inverse" of the cumulative sum.为了重建实际的单个厚度值,我们需要累积和的“倒数”。 There's this nice Q&A on that topic.关于那个主题有这个很好的问答

# The "inverse" of the cumulative sum to reconstruct the actual thickness values
tv[1:] -= tv[:-1]

# Save thickness values in "global" array
tvs[:, c] = tv

I'm not sure I got it but I used the answer of joostblack to calculcate both average thickness in pixel of both lines.我不确定我明白了,但我使用了 joostblack 的答案来计算两条线的平均厚度(以像素为单位)。 Here is my code with comments:这是我的带有注释的代码:

import numpy as np

## Read the image
img = cv2.imread('img.png')

## Create a mask on the red part (I don't use hsv here)
lower_val = np.array([0,0,0])
upper_val = np.array([150,150,255])
mask = cv2.inRange(img, lower_val, upper_val)

## Apply the mask on the image
only_red = cv2.bitwise_and(img,img, mask= mask)


gray = cv2.cvtColor(only_red, cv2.COLOR_BGR2GRAY) 
  
## Find Canny edges 
edged = cv2.Canny(gray, 30, 200) 

## Find contours
img, contours, hier = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) 


## Select contours using a bonding box
coords=[]
for c in contours:
    x,y,w,h = cv2.boundingRect(c)
    if w>10:
        ## Get coordinates of the bounding box is width is sufficient (to avoid noise because you have a huge red line on the left of your image)
        coords.append([x,y,w,h])

## Use the previous coordinates to cut the image and compute the average thickness for one red line using the answer proposed by joostblack
for x,y,w,h in coords:
    average_line_width = np.average(np.count_nonzero(only_red[y:y+h,x:x+w],axis=0))
    print(average_line_width)
    ## Show you the selected result
    cv2.imshow('image',only_red[y:y+h,x:x+w])
    cv2.waitKey(0)

The first one is average 6.34 pixels when the 2nd is 5.94 pixels (in the y axis).第一个是平均 6.34 像素,而第二个是 5.94 像素(在 y 轴上)。 If you want something more precise you'll need to change this formula!如果你想要更精确的东西,你需要改变这个公式!

One way of doing this is to calculate the medial axis (centreline) of the red pixels.一种方法是计算红色像素的中轴(中心线)。 And then, as that line is 1px wide, the number of centreline pixels gives the length of the red lines.然后,由于那条线是 1px 宽,中心线像素的数量给出了红线的长度。 If you also calculate the number of red pixels, you can easily determine the average line thickness using:如果您还计算红色像素的数量,您可以使用以下方法轻松确定平均线粗细:

average thickness = number of red pixels / length of red lines

The code looks like this:代码如下所示:

#!/usr/bin/env python3

import cv2
import numpy as np
from skimage.morphology import medial_axis

# Load image
im=cv2.imread("Dc4zq.png")

# Make mask of all red pixels and count them
mask = np.alltrue(im==[0,0,255], axis=2)  
nRed = np.count_nonzero(mask)

# Get medial axis of red lines and line length
skeleton = (medial_axis(mask*255)).astype(np.uint8)
lenRed = np.count_nonzero(skeleton)
cv2.imwrite('DEBUG-skeleton.png',(skeleton*255).astype(np.uint8))

# We now know the length of the red lines and the total number of red pixels
aveThickness = nRed/lenRed
print(f'Average thickness: {aveThickness}, red line length={lenRed}, num red pixels={nRed}')

That gives the skeleton as follows:这给出了如下骨架:

在此处输入图像描述

Sample Output样品 Output

Average thickness: 16.662172878667725, red line length=1261, num red pixels=21011

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM