我需要测量这些等离子体细丝,寻找长度和最大厚度

1 投票
1 回答
103 浏览
提问于 2025-04-14 15:30

我有很多关于等离子体细丝的照片,像这样:

1m 2m 3m 4m

... 在图片的顶部,细丝被遮挡了。我需要进行一些图像分析,包括细丝的长度,以及如果可能的话,最大宽度。对于那些被遮挡的细丝,我需要把白色的点连接起来,并进行插值处理,好像它们没有被遮挡一样。

我试着使用我找到的一个代码,但它只接受黑白图像。

# -*- coding: utf-8 -*-
"""
Created on Sat Mar 16 20:44:05 2024

@author: janko
"""

import cv2
import numpy as np
from skimage import morphology, graph
from skan import Skeleton

MAX_JUNCTION = 4    # maximal size of junctions
MAX_ANGLE = 80      # maximal angle in junction
DELTA = 3           # distance from endpoint to inner point to estimate direction at endpoint


def angle(v1, v2):
    rad = np.arctan2(v2[0], v2[1]) - np.arctan2(v1[0], v1[1])
    return np.abs((np.rad2deg(rad) % 360) - 180)


img = cv2.imread('M2ohX.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

# dilate and threshold
kernel = np.ones((2, 2), np.uint8)
dilated = cv2.dilate(gray, kernel, iterations=1)
ret, thresh = cv2.threshold(dilated, 245, 255, cv2.THRESH_BINARY)

# skeletonize
skeleton = morphology.skeletonize(thresh, method='lee')
skeleton = morphology.remove_small_objects(skeleton.astype(bool), 100, connectivity=2)

# split skeleton into paths, for each path longer than MAX_JUNCTION get list of point coordinates
g = Skeleton(skeleton)
lengths = np.array(g.path_lengths())
paths = [list(np.array(g.path_coordinates(i)).astype(int)) for i in range(g.n_paths) if lengths[i] > MAX_JUNCTION]

# get endpoints of path and vector to inner point to estimate direction at endpoint
endpoints = [[p[0], np.subtract(p[0], p[DELTA]), i] for i, p in enumerate(paths)] +\
            [[p[-1], np.subtract(p[-1], p[-1 - DELTA]), i] for i, p in enumerate(paths)]

# get each pair of distinct endpoints with the same junction and calculate deviation of angle
angles = []
costs = np.where(skeleton, 1, 255)  # cost array for route_through_array

for i1 in range(len(endpoints)):
    for i2 in range(i1 + 1, len(endpoints)):
        e1, d1, p1 = endpoints[i1]
        e2, d2, p2 = endpoints[i2]
        if p1 != p2:
            p, c = graph.route_through_array(costs, e1, e2)       # check connectivity of endpoints at junction
            if c <= MAX_JUNCTION:
                deg = angle(d1, d2)                               # get deviation of directions at junction
                if deg <= MAX_ANGLE:
                    angles.append((deg, i1, i2, p))

# merge paths, with least deviation of angle first
angles.sort(key=lambda a: a[0])

for deg, i1, i2, p in angles:
    e1, e2 = endpoints[i1], endpoints[i2]
    if e1 and e2:
        p1, p2 = e1[2], e2[2]
        paths[p1] = paths[p1] + paths[p2] + p   # merge path 2 into path 1, add junction from route_through_array
        for i, e in enumerate(endpoints):       # switch path 2 at other endpoint to new merged path 1
            if e and e[2] == p2:
                endpoints[i][2] = p1
        paths[p2], endpoints[i1], endpoints[i2] = [], [], []    # disable merged path and endpoints

# display results
for p in paths:
    if p:
        img1 = img.copy()
        for v in p:
            img1[v[0], v[1]] = [0, 0, 255]
        cv2.imshow(f'fiber', img1)
        cv2.waitKey(0)

cv2.destroyAllWindows()

所以我把代码修改成了这样:

import cv2
import numpy as np
from skimage import morphology, graph
from skan import Skeleton

MAX_JUNCTION = 4    # maximal size of junctions
MAX_ANGLE = 80      # maximal angle in junction
DELTA = 3           # distance from endpoint to inner point to estimate direction at endpoint


def angle(v1, v2):
    rad = np.arctan2(v2[0], v2[1]) - np.arctan2(v1[0], v1[1])
    return np.abs((np.rad2deg(rad) % 360) - 180)

img = cv2.imread('DSC_7987.jpg', cv2.IMREAD_GRAYSCALE)


# Otsu's thresholding after Gaussian filtering
blur = cv2.GaussianBlur(img,(5,5),0)
ret, thresh = cv2.threshold(blur,0,255,cv2.THRESH_BINARY+cv2.THRESH_OTSU)

# skeletonize
skeleton = morphology.skeletonize(thresh, method='lee')
skeleton = morphology.remove_small_objects(skeleton.astype(bool), 100, connectivity=2)

# split skeleton into paths, for each path longer than MAX_JUNCTION get list of point coordinates
g = Skeleton(skeleton)
lengths = np.array(g.path_lengths())
paths = [list(np.array(g.path_coordinates(i)).astype(int)) for i in range(g.n_paths) if lengths[i] > MAX_JUNCTION]

# get endpoints of path and vector to inner point to estimate direction at endpoint
endpoints = [[p[0], np.subtract(p[0], p[DELTA]), i] for i, p in enumerate(paths)] +\
        [[p[-1], np.subtract(p[-1], p[-1 - DELTA]), i] for i, p in enumerate(paths)]

# get each pair of distinct endpoints with the same junction and calculate deviation of angle
angles = []
costs = np.where(skeleton, 1, 255)  # cost array for route_through_array

for i1 in range(len(endpoints)):
    for i2 in range(i1 + 1, len(endpoints)):
        e1, d1, p1 = endpoints[i1]
        e2, d2, p2 = endpoints[i2]
        if p1 != p2:
            p, c = graph.route_through_array(costs, e1, e2)       # check connectivity of endpoints at junction
            if c <= MAX_JUNCTION:
                deg = angle(d1, d2)                               # get deviation of directions at junction
                if deg <= MAX_ANGLE:
                    angles.append((deg, i1, i2, p))

# merge paths, with least deviation of angle first
angles.sort(key=lambda a: a[0])

for deg, i1, i2, p in angles:
    e1, e2 = endpoints[i1], endpoints[i2]
    if e1 and e2:
        p1, p2 = e1[2], e2[2]
        paths[p1] = paths[p1] + paths[p2] + p   # merge path 2 into path 1, add junction from route_through_array
        for i, e in enumerate(endpoints):       # switch path 2 at other endpoint to new merged path 1
            if e and e[2] == p2:
                endpoints[i][2] = p1
        paths[p2], endpoints[i1], endpoints[i2] = [], [], []    # disable merged path and endpoints

# display results
for p in paths:
    if p:
        img1 = img.copy()
        for v in p:
            img1[v[0], v[1]] = [0, 0, 255]
        cv2.imshow(f'fiber', img1)
        cv2.waitKey(0)

cv2.destroyAllWindows()

但是我遇到了这个错误:

C:\Users\janko\anaconda3\Lib\site-packages\paramiko\transport.py:219: CryptographyDeprecationWarning: Blowfish has been deprecated
  "class": algorithms.Blowfish,
TypeError: int() argument must be a string, a bytes-like object or a real number, not 'list'


The above exception was the direct cause of the following exception:

Traceback (most recent call last):

  File ~\anaconda3\Lib\site-packages\spyder_kernels\py3compat.py:356 in compat_exec
    exec(code, globals, locals)

  File c:\users\janko\onedrive\muni\bakalářka\fotky_k_analyze\analyza_fotek.py:84
    img1[v[0], v[1]] = [0, 0, 255]

ValueError: setting an array element with a sequence.

1 个回答

1

你可以找到包含非零元素的第一行和最后一行。

raw = cv2.imread("image.jpg")
image = cv2.cvtColor(raw, cv2.COLOR_BGR2GRAY)
image = cv2.normalize(image, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8U)
_, binary_image = cv2.threshold(image, 10, 255, cv2.THRESH_BINARY)

你可以调整阈值参数,或者使用其他方法,比如自适应阈值。这里有更多信息:OpenCV 阈值处理

non_zero_pixels = cv2.findNonZero(binary_image)
y_coordinates, x_coordinates = non_zero_pixels[:, 0, 1], non_zero_pixels[:, 0, 0]
y1, y2 = min(y_coordinates), max(y_coordinates)
x1, x2 = min(x_coordinates), max(x_coordinates)

height, width = y2 - y1, x2 - x1
print("Height: ", height, "px.   Width: ", width, "px.")

cv2.rectangle(raw, (x1, y1), (x2, y2), (0, 255, 0), 2)
cv2.imshow("Image", raw)
cv2.waitKey(0)
cv2.destroyAllWindows()

不太清楚当火焰分裂成几团火焰时,你需要的是这些火焰外轮廓的宽度,还是各个小火焰的宽度。

如果是后者,你可以简单地写一个函数,遍历y2和y1之间的行,当遇到第一个非零像素时,开始计数后面的像素,直到遇到第一个零像素,然后继续这样做。这样,准确度就取决于阈值处理的精确度。

撰写回答