如何使用opencv对图像进行矢量化?
How to vectorize an image using opencv?
我使用术语“向量化”是因为这是用来描述我正在写的过程的。我不知道它实际上叫什么,但我想做的是获取图像的元素并将它们分成不同的图像。
Here is an example picture I'm trying to "vectorize"
我想做的是(使用 opencv)将玉米芯与它所附着的绿色原料分开,并将每个玉米芯块分成它们自己的图像。
我试过的是:
def kmeansSegmentation(path_to_images, image_name, path_to_save_segments):
img = cv2.imread(path_to_images+image_name)
img_blur = cv2.GaussianBlur(img, (3,3), 0)
img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY)
img_reshaped = img_gray.reshape((-1, 3))
img_reshaped = np.float32(img_reshaped)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 5
attempts = 10
ret,label,center=cv2.kmeans(img_reshaped,K,None,criteria,attempts,cv2.KMEANS_PP_CENTERS)
center = np.uint8(center)
res = center[label.flatten()]
v = np.median(res)
sigma=0.33
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
edges = cv2.Canny(img_gray, lower, upper)
contours, hierarchy = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
sorted_contours= sorted(contours, key=cv2.contourArea, reverse= True)
mask = np.zeros(img.shape[:2], dtype=img.dtype)
array_of_contour_areas = [cv2.contourArea(contour) for contour in contours]
contour_avg = sum(array_of_contour_areas)/len(array_of_contour_areas)
contour_var = sum(pow(x-contour_avg,2) for x in array_of_contour_areas) / len(array_of_contour_areas)
contour_std = math.sqrt(contour_var)
print("Saving segments", len(sorted_contours))
for (i,c) in tqdm(enumerate(sorted_contours)):
if (cv2.contourArea(c) > contour_avg-contour_std*2):
x,y,w,h= cv2.boundingRect(c)
cropped_contour= img[y:y+h, x:x+w]
cv2.drawContours(mask, [c], 0, (255), -1)
#tmp_image_name= image_name + "-kmeans-" + str(K) + str(random.random()) + ".jpg"
#cv2.imwrite(path_to_save_segments+tmp_image_name, cropped_contour)
result = cv2.bitwise_and(img, img, mask=mask)
"""
scale_percent = 30 # percent of original size
width = int(edges.shape[1] * scale_percent / 100)
height = int(edges.shape[0] * scale_percent / 100)
dim = (width, height)
resized = cv2.resize(result, dim, interpolation = cv2.INTER_AREA)
cv2.imshow("edges", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
"""
#tmp_image_name= image_name + "-kmeans-" + str(K) + str(random.random()) + ".png"
#cv2.imwrite(path_to_save_segments+tmp_image_name, result)
return result
请原谅注释掉的代码,那是我在修改算法时观察我对图像所做的更改。
我相信您正在尝试做的是所谓的“实例分割”。这个过程最好用深度学习技术来完成,除非你能找到一个预训练的模型,否则它可能不适合你。这是一篇关于如何做到这一点的文章:https://www.analyticsvidhya.com/blog/2019/04/introduction-image-segmentation-techniques-python/
一个更简单(但不太准确)的解决方案可能是使用 RGB 阈值来创建图片的“轮廓”,然后使用填充算法来辨别结果图像中的特定像素。要绘制图像的粗略轮廓,您必须尝试使用不同的阈值。首先,将整个图像转换为灰度w。 OpenCV(cv2.imread('image-name', 0)
)。要测试不同的阈值,只需检查每个像素值是否高于或低于某个值(您选择):
import numpy as np
import cv2
import matplotlib.pyplot as plt
def apply_threshold(img_array, threshold):
threshold_applied = []
for row in img_array:
threshold_applied.append([])
for pixel in row:
if pixel>threshold:
threshold_applied[len(threshold_applied)-1].append([255, 255, 255])
else:
threshold_applied[len(threshold_applied)-1].append([0, 0, 0])
new_img = np.array(threshold_applied, np.uint8)
cv2.imwrite(str(threshold)+".jpg", new_img)
img = cv2.imread("Ek1Dx.jpg", 0)
apply_threshold(img, 210)
如果您 运行 代码,您会发现输出非常不准确,几乎看不到玉米棒子和玉米粒。在过去十年左右的时间里,实例分割和分割图像实际上一直是一个非常大的计算机科学问题。您可能可以使用其他过滤器来获得更准确的结果,但我认为识别阈值是一种基线图像分割技术。如果你愿意,你可以尝试调整程序中的阈值,看看它是否会给你一个更分裂的图像。
我使用术语“向量化”是因为这是用来描述我正在写的过程的。我不知道它实际上叫什么,但我想做的是获取图像的元素并将它们分成不同的图像。
Here is an example picture I'm trying to "vectorize"
我想做的是(使用 opencv)将玉米芯与它所附着的绿色原料分开,并将每个玉米芯块分成它们自己的图像。
我试过的是:
def kmeansSegmentation(path_to_images, image_name, path_to_save_segments):
img = cv2.imread(path_to_images+image_name)
img_blur = cv2.GaussianBlur(img, (3,3), 0)
img_gray = cv2.cvtColor(img_blur, cv2.COLOR_BGR2GRAY)
img_reshaped = img_gray.reshape((-1, 3))
img_reshaped = np.float32(img_reshaped)
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 10, 1.0)
K = 5
attempts = 10
ret,label,center=cv2.kmeans(img_reshaped,K,None,criteria,attempts,cv2.KMEANS_PP_CENTERS)
center = np.uint8(center)
res = center[label.flatten()]
v = np.median(res)
sigma=0.33
lower = int(max(0, (1.0 - sigma) * v))
upper = int(min(255, (1.0 + sigma) * v))
edges = cv2.Canny(img_gray, lower, upper)
contours, hierarchy = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
sorted_contours= sorted(contours, key=cv2.contourArea, reverse= True)
mask = np.zeros(img.shape[:2], dtype=img.dtype)
array_of_contour_areas = [cv2.contourArea(contour) for contour in contours]
contour_avg = sum(array_of_contour_areas)/len(array_of_contour_areas)
contour_var = sum(pow(x-contour_avg,2) for x in array_of_contour_areas) / len(array_of_contour_areas)
contour_std = math.sqrt(contour_var)
print("Saving segments", len(sorted_contours))
for (i,c) in tqdm(enumerate(sorted_contours)):
if (cv2.contourArea(c) > contour_avg-contour_std*2):
x,y,w,h= cv2.boundingRect(c)
cropped_contour= img[y:y+h, x:x+w]
cv2.drawContours(mask, [c], 0, (255), -1)
#tmp_image_name= image_name + "-kmeans-" + str(K) + str(random.random()) + ".jpg"
#cv2.imwrite(path_to_save_segments+tmp_image_name, cropped_contour)
result = cv2.bitwise_and(img, img, mask=mask)
"""
scale_percent = 30 # percent of original size
width = int(edges.shape[1] * scale_percent / 100)
height = int(edges.shape[0] * scale_percent / 100)
dim = (width, height)
resized = cv2.resize(result, dim, interpolation = cv2.INTER_AREA)
cv2.imshow("edges", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
"""
#tmp_image_name= image_name + "-kmeans-" + str(K) + str(random.random()) + ".png"
#cv2.imwrite(path_to_save_segments+tmp_image_name, result)
return result
请原谅注释掉的代码,那是我在修改算法时观察我对图像所做的更改。
我相信您正在尝试做的是所谓的“实例分割”。这个过程最好用深度学习技术来完成,除非你能找到一个预训练的模型,否则它可能不适合你。这是一篇关于如何做到这一点的文章:https://www.analyticsvidhya.com/blog/2019/04/introduction-image-segmentation-techniques-python/
一个更简单(但不太准确)的解决方案可能是使用 RGB 阈值来创建图片的“轮廓”,然后使用填充算法来辨别结果图像中的特定像素。要绘制图像的粗略轮廓,您必须尝试使用不同的阈值。首先,将整个图像转换为灰度w。 OpenCV(cv2.imread('image-name', 0)
)。要测试不同的阈值,只需检查每个像素值是否高于或低于某个值(您选择):
import numpy as np
import cv2
import matplotlib.pyplot as plt
def apply_threshold(img_array, threshold):
threshold_applied = []
for row in img_array:
threshold_applied.append([])
for pixel in row:
if pixel>threshold:
threshold_applied[len(threshold_applied)-1].append([255, 255, 255])
else:
threshold_applied[len(threshold_applied)-1].append([0, 0, 0])
new_img = np.array(threshold_applied, np.uint8)
cv2.imwrite(str(threshold)+".jpg", new_img)
img = cv2.imread("Ek1Dx.jpg", 0)
apply_threshold(img, 210)
如果您 运行 代码,您会发现输出非常不准确,几乎看不到玉米棒子和玉米粒。在过去十年左右的时间里,实例分割和分割图像实际上一直是一个非常大的计算机科学问题。您可能可以使用其他过滤器来获得更准确的结果,但我认为识别阈值是一种基线图像分割技术。如果你愿意,你可以尝试调整程序中的阈值,看看它是否会给你一个更分裂的图像。