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

Python 计算图像中两个像素之间的距离,python,numpy,opencv,distance,points,Python,Numpy,Opencv,Distance,Points,我试图计算两个像素之间的距离,但是用一种特定的方法。我需要知道图像中红线的厚度,所以我的想法是按列遍历图像,找到两个边缘点的坐标并计算它们之间的距离。对两行(顶部和底部)执行此操作。对每列执行此操作,然后计算平均值。 我还应该做一个从像素到真实比例的转换 这是我目前的代码: # Make numpy array from image npimage = np.array(image) # Describe what a single red pixel looks like red = np.

我试图计算两个像素之间的距离,但是用一种特定的方法。我需要知道图像中红线的厚度,所以我的想法是按列遍历图像,找到两个边缘点的坐标并计算它们之间的距离。对两行(顶部和底部)执行此操作。对每列执行此操作,然后计算平均值。 我还应该做一个从像素到真实比例的转换

这是我目前的代码:

# 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
我找不到坐标。有人能帮我吗?当然,如果有更好的计算方法,我很乐意接受这些建议。我是新来的! 多谢各位

使用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)
使用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)
两种情况下的输出:

18.430701754385964

我不确定我是否得到了它,但我使用了joostblack的答案来计算两条线的平均厚度(以像素为单位)。以下是我的代码和注释:

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)

第一个是平均6.34像素,第二个是5.94像素(在y轴上)。如果你想要更精确的东西,你需要改变这个公式

一种方法是计算红色像素的中轴(中心线)。然后,因为这条线是1px宽,所以中心线像素的数量给出了红线的长度。如果还计算红色像素数,则可以使用以下方法轻松确定平均线厚度:

average thickness = number of red pixels / length of red lines
代码如下所示:

#!/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}')
这将提供如下骨架:

样本输出

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

正确屏蔽所有红色像素后,可以计算该屏蔽中每列的累积和:

在每一条红线下面,都有一个大面积的常量值:在第一条红线下面,它是该红线的厚度。在第二条红线下方,是两条红线的累积厚度,依此类推(如果有更多的红线)

因此,现在,对于每一列,根据累积和计算直方图,并过滤掉这些峰值;去掉直方图中的
0
,那将是顶部的大黑色区域。对于每列,您可以获得所有红线的上述(累积)厚度值。其余部分是提取实际的单个厚度值,并计算所有这些值的平均值

这是我的密码:

导入cv2
将numpy作为np导入
#读取图像
img=cv2.imread('Dc4zq.png')
#遮罩RGB纯红色
掩码=(img==[0,0,255])。全部(轴=2)
#我们查两行
n=2
#每列的累积总和
cs=np.cumsum(掩码,轴=0)
#每列的厚度值
tvs=np.zero((n,img.shape[1]))
对于范围内的c(img.形状[1]):
#计算列的累积和直方图
hist=np.直方图(cs[:,c],bins=np.arange(img.shape[1]+1))
#获取n个最大直方图值
#这些是柱的单个厚度值
tv=np.sort(np.argsort(hist[0][1:])[::-1][0:n]+1)
电视
tvs[:,c]=tv
#获取平均厚度值
mtv=np.平均值(tvs.展平()
打印(“平均厚度值:”,mtv)
最终结果是:

平均厚度值:18.92982456140351
----------------------------------------
系统信息
----------------------------------------
平台:Windows-10-10.0.16299-SP0
Python:3.9.1
NumPy:1.20.1
OpenCV:4.5.1
----------------------------------------

编辑:我将提供有关“NumPy魔术”的更多细节

#计算单列累积和的直方图
hist=np.直方图(cs[:,c],bins=np.arange(img.shape[1]+1))
这里,
bin
表示直方图的间隔,即
[0,1]
[1,2]
,等等。要获得最后一个间隔
[569570]
,您需要在
np.arange
调用中使用
img.shape[1]+1
,因为在
np.arange
中不包括正确的限制

#从bin 1开始获取实际直方图
hist=hist[0][1:]
通常,
np.histogram
返回一个元组,其中第一个元素是实际的直方图。我们提取它,只查看所有比
0
大的箱子(请记住,大的黑色区域)

现在,让我们分解此代码:

tv=np.sort(np.argsort(hist[0][1:)[::-1][0:n]+1)
这一行可以重写为:

#从bin 1开始获取实际直方图
hist=hist[0][1:]
#得到排序直方图的指标;这些是真正的垃圾箱
hist_idx=np.argsort(hist)
#反转找到的索引,因为我们需要具有最高计数的箱子
hist_idx=hist_idx[:-1]
#根据该索引,我们只需要前n个元素(假设有n条红线)
hist_idx=hist_idx[:n]
#添加1,因为我们剪切了0 bin
hist_idx=hist_idx+1
#作为准备:对(累积)厚度值进行排序
tv=np.sort(历史idx)
现在,我们已经有了每列的(累积)厚度值。要重建实际的单个厚度值,我们需要累积和的“倒数”。关于这个话题有很多问题

#累积总和的“倒数”,以重建实际厚度值
电视
#在“全局”数组中保存厚度值
tvs[:,c]=tv

您希望这是自动的吗?你想知道红线的最大厚度,le min还是平均值?是的,我的想法是让它自动化!我认为平均值是最具代表性的。为什么不尝试在R通道上设置阈值呢?是的,我在考虑R为白色,其余为黑色的二值化,但我不知道如何去掉宽度:/要转换为真实比例,你需要知道图像中某物的真实大小!非常感谢:)你能解释一下这个函数是如何工作的吗?为什么要使用img[:,:,2]并将结果除以2?这是按列通过图像的吗?它计算红色圆周率