英文:
OpenCV - how do I get pixel values at junctions of components with different colors from an image?
问题
我有一个由不同颜色表示的不同随机晶粒取向构成的晶粒结构,这些颜色在从相场模拟生成的图像中显示出来。我需要识别并获取每个晶粒的像素值,还需要找到交汇点(三重交汇点和更高阶交汇点)以及边界上的像素坐标。
晶粒结构,不同取向
晶粒结构突出显示边界
我尝试了几种图像处理方法,还尝试通过实现一个检测颜色变化和连通性并分类晶粒的算法来解决这个问题,但无法从代码中获取像素值和交汇点。有人可以帮助吗?以下是我尝试的一种方法。
import cv2
import numpy as np
import matplotlib.pyplot as plt
# 读取图像
image = cv2.imread('image_path', cv2.IMREAD_GRAYSCALE)
# 应用阈值创建二值图像
_, binary_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)
# 应用形态学运算以去除小噪声和文本
kernel = np.ones((3, 3), np.uint8)
clean_image = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel)
# 查找连通组件
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(clean_image, 8, cv2.CV_32S)
# 大小阈值以过滤掉小的连通组件
size_threshold = 30
# 为可视化创建颜色映射
colors = [np.array([np.random.randint(256), np.random.randint(256), np.random.randint(256)], dtype=np.uint8) for _ in range(num_labels)]
colors[0] = [255, 255, 255] # 将背景颜色设置为白色
# 创建用于可视化的彩色图像
output_image = np.zeros((image.shape[0], image.shape[1], 3), dtype=np.uint8)
# 用于跟踪较大连通组件的新标签的变量
new_label = 1
desired_label = list(range(50))
for i in range(1, num_labels):
coordinates = np.column_stack(np.where(labels == i))
x = stats[i, cv2.CC_STAT_LEFT]
y = stats[i, cv2.CC_STAT_TOP]
w = stats[i, cv2.CC_STAT_WIDTH]
h = stats[i, cv2.CC_STAT_HEIGHT]
area = stats[i, cv2.CC_STAT_AREA]
(cX, cY) = centroids[i]
if coordinates.shape[0] > size_threshold:
if new_label in desired_label:
output_image[labels == i] = colors[new_label]
cv2.rectangle(output_image, (x, y), (x + w, y + h), (0, 255, 0), 3)
cv2.circle(output_image, (int(cX), int(cY)), 4, (0, 0, 255), -1)
else:
output_image[labels == i] = [255, 255, 255]
new_label += 1
plt.figure(2)
print(f"Number of connected components (labels): {new_label - 1}")
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
# 定义较小的核心以进行形态学运算
kernel_dilate = np.ones((2, 2), np.uint8)
# 应用膨胀以扩展仅进行一次迭代的白色区域
dilated_image = cv2.dilate(output_image, kernel_dilate, iterations=1)
plt.figure(3)
plt.imshow(cv2.cvtColor(dilated_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
# 将膨胀图像的白色区域转换为黑色,黑色区域转换为白色
intersection_image = cv2.bitwise_not(dilated_image)
# 将图像转换为灰度
intersection_image_gray = cv2.cvtColor(intersection_image, cv2.COLOR_BGR2GRAY)
# 应用二进制阈值以分离线条
_, intersection_image_binary = cv2.threshold(intersection_image_gray, 127, 255, cv2.THRESH_BINARY)
# 查找黑线的骨架
skeleton = cv2.ximgproc.thinning(intersection_image_binary)
# 应用十字形核心以找到交点
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
intersection_points = cv2.morphologyEx(skeleton, cv2.MORPH_HITMISS, cross_kernel)
# 在原始图像上绘制交点
intersection_image_with_points = intersection_image.copy()
intersection_image_with_points[intersection_points > 0] = [0, 0, 255] # 用红色标记交点
plt.figure(4)
plt.imshow(cv2.cvtColor(intersection_image_with_points, cv2.COLOR_BGR2RGB))
plt.axis('off')
y_coords, x_coords = np.where(intersection_points != 0)
# 在output_image上绘制交点
for x, y in zip(x_coords, y_coords):
cv2.circle(output_image, (x, y), 3, (0, 0, 255), -1) # 用红色标记交点
plt.figure(5)
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()
这是你提供的代码的翻译部分。如果需要进一步的帮助或解释,请告诉我。
英文:
I have a grain structure with different random grain orientations represented by different colors in the image generated from phase field simulations. I need to identify and get the pixel values for every grain and also need to find the pixel coordinates at the junctions (triple junctions and junctions of higher order) and along the boundaries.
Grain structure with different orientations
Grain structure highlighting boundaries
I tried several image processing methods and also tried to solve this by implementing an algorithm which detects color change and connectivity and classifies the grains but was not able to get the pixel values and junction points from the code. Can anyone help? Here is one of the approaches I tried.
import cv2
import numpy as np
import matplotlib.pyplot as plt
# Read the image
image = cv2.imread('image_path', cv2.IMREAD_GRAYSCALE)
# Apply threshold to create a binary image
_, binary_image = cv2.threshold(image, 127, 255, cv2.THRESH_BINARY_INV)
# Apply morphological operations to remove small noise and text
kernel = np.ones((3, 3), np.uint8)
clean_image = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel)
# Find connected components
num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(clean_image, 8, cv2.CV_32S)
# Size threshold to filter out small connected components
size_threshold = 30
# Create a color map for visualization
colors = [np.array([np.random.randint(256), np.random.randint(256), np.random.randint(256)], dtype=np.uint8) for _ in range(num_labels)]
colors[0] = [255, 255, 255] # Setting background color to white
# Create a color image for visualization
output_image = np.zeros((image.shape[0], image.shape[1], 3), dtype=np.uint8)
# Variable to track the new labels for the larger connected components
new_label = 1
desired_label = list(range(50))
for i in range(1, num_labels):
coordinates = np.column_stack(np.where(labels == i))
x = stats[i, cv2.CC_STAT_LEFT]
y = stats[i, cv2.CC_STAT_TOP]
w = stats[i, cv2.CC_STAT_WIDTH]
h = stats[i, cv2.CC_STAT_HEIGHT]
area = stats[i, cv2.CC_STAT_AREA]
(cX, cY) = centroids[i]
if coordinates.shape[0] > size_threshold:
if new_label in desired_label:
output_image[labels == i] = colors[new_label]
cv2.rectangle(output_image, (x, y), (x + w, y + h), (0, 255, 0), 3)
cv2.circle(output_image, (int(cX), int(cY)), 4, (0, 0, 255), -1)
else:
output_image[labels == i] = [255, 255, 255]
new_label += 1
plt.figure(2)
print(f"Number of connected components (labels): {new_label - 1}")
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
# Define a smaller kernel for the morphological operation
kernel_dilate = np.ones((2, 2), np.uint8)
# Apply dilation to expand the white areas with only one iteration
dilated_image = cv2.dilate(output_image, kernel_dilate, iterations=1)
plt.figure(3)
plt.imshow(cv2.cvtColor(dilated_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
# Convert the white areas of the dilated image to black and the black areas to white
intersection_image = cv2.bitwise_not(dilated_image)
# Convert the image to grayscale
intersection_image_gray = cv2.cvtColor(intersection_image, cv2.COLOR_BGR2GRAY)
# Apply binary thresholding to isolate the lines
_, intersection_image_binary = cv2.threshold(intersection_image_gray, 127, 255, cv2.THRESH_BINARY)
# Find the skeleton of the black lines
skeleton = cv2.ximgproc.thinning(intersection_image_binary)
# Apply a cross-shaped kernel to find intersection points
cross_kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
intersection_points = cv2.morphologyEx(skeleton, cv2.MORPH_HITMISS, cross_kernel)
# Draw the intersection points on the original image
intersection_image_with_points = intersection_image.copy()
intersection_image_with_points[intersection_points > 0] = [0, 0, 255] # Marking the intersection points in red
plt.figure(4)
plt.imshow(cv2.cvtColor(intersection_image_with_points, cv2.COLOR_BGR2RGB))
plt.axis('off')
y_coords, x_coords = np.where(intersection_points != 0)
# Draw the intersection points on the output_image
for x, y in zip(x_coords, y_coords):
cv2.circle(output_image, (x, y), 3, (0, 0, 255), -1) # Marking the intersection points in red
plt.figure(5)
plt.imshow(cv2.cvtColor(output_image, cv2.COLOR_BGR2RGB))
plt.axis('off')
plt.show()
答案1
得分: 1
计算唯一相邻像素的数量...
我去掉了图像中所有多余的轴、边框和注释,所以我从这个开始:
我认为彩色的三个通道是一个不错的可视化效果,但在处理时可能会有些麻烦,所以我使用 np.dot()
将每个 BGR888 像素转换为简单的24位灰度值:
#!/usr/bin/env python3
import numpy as np
import cv2
from scipy.ndimage import generic_filter
# 打开图像
im = cv2.imread('q78bN.png')
# 将3个8位通道的图像转换为单通道的24位图像
im24 = np.dot(im.astype(np.uint32),[1,256,65536])
然后我运行了 SciPy
的 generic_filter
,在图像上传递了一个3x3的滤波器。我将每个3x3邻域中的所有像素放入一个集合中(集合不包含重复项),然后得到集合的长度,现在这个长度就是每个像素点的唯一相邻像素数量。我保存了这个结果,然后进行了对比拉伸,这样你就可以看到具有不同相邻像素的像素会显示为最亮的像素:
# 运行通用滤波器,计算3x3邻域中唯一颜色的数量
result = generic_filter(im24, lambda P: len(set(P)), (3, 3))
# 保存结果
cv2.imwrite('result.png', result)
如果你想要找到具有4个不同颜色相邻像素的坐标,你可以使用:
print(np.where(result==4))
一旦你获得了感兴趣点的坐标,你可以显然在你的原始图像上使用它们,而不是在我的奇怪的24位灰度图像上使用它们。
处理时间似乎相当合理,但如果你想要加速它,可能会受益于使用 numba
版本。
我没有运行任何计时/性能测试,但我想到 len(np.unique(P))
可能 比使用 len(set(P))
更快。
英文:
Count number of unique neighbouring pixels...
I trimmed all the extraneous axes, borders and annotations off your image, so I started with this:
I think the 3 channels of colour are a nice visualisation, but pesky for processing so I converted each BGR888 pixel into a simple 24-bit greyscale value using np.dot()
:
#!/usr/bin/env python3
import numpy as np
import cv2
from scipy.ndimage import generic_filter
# Open image
im = cv2.imread('q78bN.png')
# Make single channel 24-bit image instead of 3 channels of 8-bit each
im24 = np.dot(im.astype(np.uint32),[1,256,65536])
Then I ran SciPy
's generic_filter
to pass a 3x3 filter over the image. I put all pixels from each 3x3 neighbourhood into a set (which cannot hold duplicates) and then got the length of the set which is now the number of unique neighbours each cell has. I saved that and then contrast-stretched it so you can see the pixels with most different neighbours show up as the brightest pixels:
# Run generic filter counting unique colours in 3x3 neighbourhood
result = generic_filter(im24, lambda P: len(set(P)), (3, 3))
# Save result
cv2.imwrite('result.png', result)
If you want to find coordinates having 4 different coloured neighbours, you can use:
print(np.where(result==4))
Once you have the coordinates of the interesting points, you can obviously use them on your original image, rather than on my weird 24-bit greyscale image.
The processing time seems pretty reasonable, but if you wanted to speed it up, it would probably benefit from a numba
version.
I didn't run any timing/performance tests, but it occurred to me that len(np.unique(P))
may be faster than using len(set(P))
.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论