我有多张等离子丝的照片,如下所示:
...其中灯丝在图像顶部被屏蔽中断。我需要进行一些图像分析,包括细丝的长度,如果可能的话,还包括它们的最大宽度。对于图像,在灯丝被中断的地方,我需要连接白点并对其进行插值,就像它不会被中断一样。
我尝试使用我找到的代码,但它只接受黑白图像。
# -*- 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()
您可以找到包含非零元素的第一行和最后一行。
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 tresholding
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 之间的线,当遇到第一个非零像素时,对后续像素进行计数,直到遇到第一个零,依此类推。那么,准确性将取决于阈值的精度。