使用 Python 通过基于颜色的图像分割进行物体检测

2019-05-08 16:32:08 浏览数 (1)

本文为 AI 研习社编译的技术博客,原标题 : Object detection via color-based image segmentation using python 作者 | Salma Ghoneim 翻译 | Mr丶Lonely 校对 | 酱番梨 审核 | 约翰逊·李加薪 整理 | 立鱼王 原文链接: https://towardsdatascience.com/object-detection-via-color-based-image-segmentation-using-python-e9b7c72f0e11

图片来自Pexels

入门

如果你已经安装了jupyter notebook或者一个IDE,你可以运行python然后下载OpenCV,只需要跳到执行即可。

工具

我们今天的英雄是Anaconda。一个免费开源的发行版,帮助我们下载不同的软件包并且将它们整理到独立的环境之中。

维基百科告诉我们关于Anaconda的内容:

Anaconda是用于科学计算(数据科学,机器学习应用程序,大规模数据处理,预测分析等)的Python和R编程语言的免费开源发行版,旨在简化包管理和部署。包版本由包管理系统conda管理。Anaconda发行版已被超过1200万用户使用,包括1400多种适用于Windows,Linux和MacOS的流行数据科学包。

以下是有关如何下载Anaconda的详细教程: 适用于Windows的Anaconda和适用于Linux的Anaconda https://docs.anaconda.com/anaconda/install/windows/ https://docs.anaconda.com/anaconda/install/linux/

创建环境

打开bash(cmd)并输入

代码语言:javascript复制
$ conda create -n myEnv python=3

提示下载软件包时选择y(表示是)。

代码语言:javascript复制
$ source activate myEnv$ conda install anaconda$ conda activate myEnv$ conda install opencv
$ jupyter notebook

这将在浏览器中为您打开jupyter notebook。

一些重要的术语

轮廓

轮廓可以简单地解释为连接所有连续点(连同边界)的曲线,具有相同的颜色或亮度。轮廓是形状分析和目标检测和识别的有用工具。

阈值

在灰度图像上应用阈值处理使其成为二值图像。你可以设置一个阈值,其中低于此阈值的所有值都将变为黑色,高于此阈值的所有值都将变为白色。

执行

现在你已经有了所有你需要的东西。

我们将从一个简单的例子开始,向你展示基于颜色的分割是如何工作的。

忍受我一下,直到我们得到好的东西。

一个Ombre圈 - 使用photoshop制作的图像

如果你想和我一起尝试,你可以从原文免费获得这个图像。

在下面的代码中,我将把这个图像分成17个灰度级。然后使用轮廓测量每个级别的区域。

代码语言:javascript复制
import cv2
import numpy as np

def viewImage(image):
    cv2.namedWindow('Display', cv2.WINDOW_NORMAL)
    cv2.imshow('Display', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
    
def grayscale_17_levels (image):
    high = 255
    while(1):  
        low = high - 15
        col_to_be_changed_low = np.array([low])
        col_to_be_changed_high = np.array([high])
        curr_mask = cv2.inRange(gray, col_to_be_changed_low,col_to_be_changed_high)
        gray[curr_mask > 0] = (high)
        high -= 15
        if(low == 0 ):
            break
            
image = cv2.imread('./path/to/image')
viewImage(image)
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
grayscale_17_levels(gray)
viewImage(gray)

相同的图像分为17个灰度级

代码语言:javascript复制
def get_area_of_each_gray_level(im):

## convert image to gray scale (must br done before contouring)
    image = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    output = []
    high = 255
    first = True
    while(1):
    
low = high - 15
        if(first == False):
        
# making values that are of a greater gray level black 
            ## so it won't get detected  
            to_be_black_again_low = np.array([high])
            to_be_black_again_high = np.array([255])
            curr_mask = cv2.inRange(image, to_be_black_again_low, 
            to_be_black_again_high)
            image[curr_mask > 0] = (0)
            
        # making values of this gray level white so we can calculate
        # it's area
        ret, threshold = cv2.threshold(image, low, 255, 0)
        contours, hirerchy = cv2.findContours(threshold, 
        cv2.RETR_LIST, cv2.CHAIN_APPROX_NONE)
        
if(len(contours) > 0):

output.append([cv2.contourArea(contours[0])])
            cv2.drawContours(im, contours, -1, (0,0,255), 3)
            
high -= 15
        first = False
        if(low == 0 ):
        
break

return output

在这个函数中,我只是通过将位于此范围内的所有数据统一到一个强度,在此迭代中简单地转换我想要轮廓(高亮)的灰色范围(强度)。我将所有其他强度转换为黑色(包括更大和更小的强度)。

第二步我对图像进行阈值处理,以便只有我想要轮廓的颜色现在显示为白色而其他所有颜色都转换为黑色。此步骤在这里没有太大变化,但必须完成,因为轮廓最适合黑白(阈值)图像。在应用此步骤(阈值处理)之前,下面的图像将是相同的,除了白色环将变成灰色的(第10个灰度级的灰度强度(255-15 * 10))

第10个部分单独出现以便能够计算其面积

代码语言:javascript复制
image = cv2.imread('./path/to/image')print(get_area_of_each_gray_level(image))viewImage(image)

17个灰度级的轮廓到原始图像上

包含区域值的数组

这样我们就可以得到每个灰度级的面积。

这真的很重要吗?

在我们继续之前,我想强调这个主题的重要性。我是一名计算机工程专业的学生,我正在开展一个名为机器学习的项目,用于智能肿瘤检测和识别。

在该项目中使用基于颜色的图像分割来帮助计算机学习如何检测肿瘤。当处理MRI扫描时,程序必须检测所述MRI扫描的癌症水平。它通过将扫描分割成不同的灰度级别来实现这一点,其中最暗的是充满癌细胞,而最接近白色的是更健康的部分。然后它计算肿瘤对每个灰度级的隶属程度。有了这些信息,该程序能够识别肿瘤及其阶段。

该项目基于软计算,模糊逻辑和机器学习,您可以在模糊逻辑及其如何治愈癌症方面了解更多信息。

物体检测

照片来自Pexels的Lukas

你可以从Pexels免费获得这个图像。你只需要裁剪它。

在此图像中,我们只想轮廓化叶子。由于该图像的纹理非常不规则且不均匀,这意味着虽然没有很多颜色。该图像中的绿色强度也能改变其亮度。因此,这里最好的做法是将所有这些不同的绿色阴影统一为一个阴影。这样当我们应用轮廓时,它将把叶子作为一个整体对象来处理。

注意:如果你在图像上应用轮廓线而不进行任何预处理,则会出现以下情况。我只是想让你看看叶子的不均匀性如何让OpenCV识别不出这只是一个对象。

在没有预处理的情况下进行轮廓加工,检测到531个轮廓

代码语言:javascript复制
import cv2
import numpy as np

def viewImage(image):
    cv2.namedWindow('Display', cv2.WINDOW_NORMAL)
    cv2.imshow('Display', image)
    cv2.waitKey(0)
    cv2.destroyAllWindows()

首先,你必须知道颜色的HSV表示,你可以通过将其RGB转换为HSV来了解它,如下所示。

代码语言:javascript复制
## getting green HSV color representation
green = np.uint8([[[0, 255, 0 ]]])
green_hsv = cv2.cvtColor(green,cv2.COLOR_BGR2HSV)
print( green_hsv)

HSV颜色的绿色表示

将图像转换为HSV:使用HSV可以更轻松地获得一种颜色的完整范围。HSV,H代表Hue,S代表饱和度,V代表值。我们已经知道绿色是[60,255,255]。世界上所有的绿色都在[45,100,50]到[75,255,255]之间,即[60-15,100,50]到[60 15,255,255]。图15仅是近似值。

我们取这个范围并将其转换为[75,255,200]或任何其他浅色(第三个值必须相对较大),因为这是颜色的亮度,这是当到达阈值时使该部分为白色的值图片。

代码语言:javascript复制
image = cv2.imread('./path/to/image.jpg')
hsv_img = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
viewImage(hsv_img) ## 1

green_low = np.array([45 , 100, 50] )
green_high = np.array([75, 255, 255])
curr_mask = cv2.inRange(hsv_img, green_low, green_high)
hsv_img[curr_mask > 0] = ([75,255,200])
viewImage(hsv_img) ## 2

## converting the HSV image to Gray inorder to be able to apply 
## contouring
RGB_again = cv2.cvtColor(hsv_img, cv2.COLOR_HSV2RGB)
gray = cv2.cvtColor(RGB_again, cv2.COLOR_RGB2GRAY)
viewImage(gray) ## 3

ret, threshold = cv2.threshold(gray, 90, 255, 0)
viewImage(threshold) ## 4

contours, hierarchy =  cv2.findContours(threshold,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
cv2.drawContours(image, contours, -1, (0, 0, 255), 3)
viewImage(image) ## 5

左图:转换为HSV后的图像(1)

右图:应用模板后的图像(颜色统一)(2)

左图:从HSV转换为灰色后的图像(3)

右图:达到阈值的图像,最后一步(4)

最终轮廓(5)

由于背景中似乎也存在不规则性,我们可以使用这种方法获得最大的轮廓,最大的轮廓当然是叶子。

我们可以得到轮廓数组中叶子轮廓的索引,从中得到叶子的面积和中心。

轮廓具有许多其他可以使用的特征,例如轮廓周长,凸包,边界矩形等等。你可以从这里了解更多相关信息。

代码语言:javascript复制
def findGreatesContour(contours):
    largest_area = 0
    largest_contour_index = -1
    i = 0
    total_contours = len(contours)
    while (i < total_contours ):
        area = cv2.contourArea(contours[i])
        if(area > largest_area):
            largest_area = area
            largest_contour_index = i
        i =1
            
    return largest_area, largest_contour_index
    
# to get the center of the contour
cnt = contours[13]
M = cv2.moments(cnt)
cX = int(M["m10"] / M["m00"])
cY = int(M["m01"] / M["m00"])

largest_area, largest_contour_index = findGreatesContour(contours)

print(largest_area)
print(largest_contour_index)

print(len(contours))

print(cX)

打印语句的结果

0 人点赞