我需要测量这些等离子体细丝,寻找长度和最大厚度
我有很多关于等离子体细丝的照片,像这样:
... 在图片的顶部,细丝被遮挡了。我需要进行一些图像分析,包括细丝的长度,以及如果可能的话,最大宽度。对于那些被遮挡的细丝,我需要把白色的点连接起来,并进行插值处理,好像它们没有被遮挡一样。
我试着使用我找到的一个代码,但它只接受黑白图像。
# -*- 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之间的行,当遇到第一个非零像素时,开始计数后面的像素,直到遇到第一个零像素,然后继续这样做。这样,准确度就取决于阈值处理的精确度。