图像分类器:基于opencv、随机森林、逻辑回归算法实现

转载链接:https://blog.csdn.net/qq_42379006/article/details/80929670

图像分类器:基于opencv、随机森林、逻辑回归算法实现

                本文介绍了什么是图像分类以及图像分类的过程,介绍了图像分类的关键操作、opencv的使用、图像的三种颜色空间:RGB、HSV、L*a*b*、以及颜色直方图,另外基于
RGB模式及L*a*b*模式下使用随机森林、逻辑回归算法实现对4种图片的分类。

以下部分是正文:

by:−−−by:朱世新


1 项目简介

1.1 图像分类器 - 什么是图像分类

01-什么是图像分类.png-137.5kB
01-什么是图像分类-2.png-153kB

1.2 案例目标

02-案例目标.png-225.5kB

1.3 图像分类过程

03-图像分类过程.png-125.7kB

1.4 图像分类关键点

1.4.1 关键点1:数据准备

04-关键点:数据准备.png-98.6kB

1.4.2 关键点2:数据集划分

04-关键点2:数据集划分.png-109.2kB

1.4.3 关键点3:特征表示

04-关键点3:特征表示.png-58.5kB

1.4.4 关键点4:分类算法

04-关键点4:分类算法.png-28.5kB

1.4.5 关键点5:模型评估

04-关键点5:模型评估.png-46.2kB

2 opencv简介及安装使用

2.1 opencv安装

> conda install opencv

 
 

    2.2 什么是opencv

    05-opencv:什么是opencv.png-78.7kB

    2.3 opencv使用

    >>在线文档:https://docs.opencv.org/2.4.11/<<

    2.4 代码演示:

    import cv2
    # 读取图片
    img_file = '../data/images/image_crocus_0001.png'
    '''
    cv2.imread(filename,(flags))
    参数说明:
    filename:图片文件名
    flags:指定图片的颜色类型
        - IMREAD_ANYCOLOR    返回无损图片
        - IMREAD_ANYDEPTH    如果载入图像的深度为16位或者32位,就返回对应深度图,否则就转换为8位图像
        - IMREAD_COLOR       总是把图片转为3通道的BGR图片
        - IMREAD_GRAYSCALE   返回灰度图片
        - IMREAD_UNCHANGED   返回原始状态图片
    
    支持的图片格式
        - Windows位图:*.bmp, *.dib
        -JPEG文件:*.jpeg, *.jpg, *.jpe
        - PNG文件:*.png
        - Portable image format:*.pbm, *.pgm, *.ppm
        - Sun rasters:*.sr, *.ras
        - TIFF文件:*.tiff, *.tif
    '''
    img = cv2.imread(img_file)
    
    '''
    读入的图片得到的是ndarray对象,0~255的整数
    ''' 
    type(img)       >> np.ndarray
    '''
    ndarray的三个维度分别是图片的:高、宽、通道
    '''
    img.shape
    # 图片的显示方法
    '''
    方法一:命令行输入:
    python 2.0_cv2_imshow.py ../data/images/image_crocus_0001.png IMREAD_COLOR
    
    方法二:使用matplotlib显示图片
    plt.imshow()在显示图片的时候是按RGB通道的顺序显示,与cv2的方式正好相反
    需要通过np.flip(img,axis=2)调整3个通道的顺序
    '''
    import matplotlib.pyplot as plt
    import numpy as np
    
    plt.imshow(np.flip(img,axis=2))
    ple.asix('off')
    plt.show()
    
    # 保存图片
    output_image = os.path.join(output_dir,'image.png')
    cv2.imwrite(output_image,img)

    3 图像特征 - 颜色直方图(代码演示)

    import numpy as np
    import cv2
    import matplotlib.pyplot as plt
    
    pic_file='data/images/image_crocus_0001.png'
    img_bgr = cv2.imread(pic_file,cv2.IMREAD_COLOR)

    3.1 RGB颜色空间

    # 分别获取三个通道的ndarray数据
    img_b=img_bgr[...,0]
    img_g=img_bgr[...,1]
    img_r=img_bgr[...,2]
    
    # 分通道显示图片
    fig = plt.gcf()
    fig.set_size_inches(10,10)
    plt.subplot(221)
    plt.imshow(np.flip(img_bgr,axis=2))
    plt.axis('off')
    plt.title('Image')
    
    plt.subplot(222)
    plt.imshow(img_r,cmap='gray')
    plt.axis('off')
    plt.title('R')
    
    plt.subplot(223)
    plt.imshow(img_g,cmap='gray')
    plt.axis('off')
    plt.title('G')
    
    plt.subplot(224)
    plt.imshow(img_b,cmap='gray')
    plt.axis('off')
    plt.title('B')
    plt.show()

    image_1ch4d17cp4rn1s8j1jc31a8915ml9.png-509.8kB

    3.2 HSV颜色空间

    # 通过cvtColor()方法转换颜色空间
    img_hsv = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2HSV)
    img_h = img_hsv[...,0]
    img_s = img_hsv[...,1]
    img_v = img_hsv[...,2]
    '''分通道显示图片'''
    fig = plt.gcf()
    fig.set_size_inches(10,10)
    
    plt.subplot(221)
    plt.imshow(img_hsv)
    plt.axis('off')
    plt.title('HSV')
    
    plt.subplot(222)
    plt.imshow(img_h,cmap='gray')
    plt.axis('off')
    plt.title('H')
    
    plt.subplot(223)
    plt.imshow(img_s,cmap='gray')
    plt.axis('off')
    plt.title('S')
    
    plt.subplot(224)
    plt.imshow(img_v,cmap='gray')
    plt.axis('off')
    plt.title('V')
    plt.show()

    image_1ch4d4fqn15g81a2ktatk5j1fpj1m.png-526.6kB

    3.3 L*a*b*颜色空间

    img_lab = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2LAB)
    img_ls = img_lab[...,0]
    img_as = img_lab[...,1]
    img_bs = img_lab[...,2]
    '''分通道显示图片'''
    fig = plt.gcf()
    fig.set_size_inches(10,12)
    
    plt.subplot(221)
    plt.imshow(img_lab)
    plt.axis('off')
    plt.title('L*a*b')
    
    plt.subplot(222)
    plt.imshow(img_ls,cmap='gray')
    plt.axis('off')
    plt.title('L*')
    
    
    plt.subplot(223)
    plt.imshow(img_ls,cmap='gray')
    plt.axis('off')
    plt.title('a*')
    
    plt.subplot(224)
    plt.imshow(img_ls,cmap='gray')
    plt.axis('off')
    plt.title('b*')
    
    plt.show()

    image_1ch4d7as1r2jmpf9fv9l815l323.png-444.5kB

    3.4 灰度图

    img_gray = cv2.cvtColor(img_bgr,cv2.COLOR_BGR2GRAY)
    fig = plt.gcf()
    fig.set_size_inches(5,5)
    plt.imshow(img_gray,cmap='gray')
    plt.axis('off')
    plt.title('Gray')
    plt.show()

    image_1ch4d8mnf1hht9h4sg214qp3092g.png-84.2kB

    4 颜色直方图

    4.1 灰度图的颜色直方图

    '''
    cv2.calcHist(images,channels,mask,histSize,ranges[,hist[,accumulate]]) -> hist
    参数说明:
    images:图片列表
    channels:需要计算直方图的通道,[0]表示计算通道0的直方图,[0,1,2]表示计算0,1,2所表示颜色的直方图
    mask:蒙版,只计算>0的位置上像素的颜色直方图,取None表示无蒙版
    histSize:每个维度上直方图的大小,[8]表示把通道0的颜色取值等分为8份后计算直方图
    ranges:每个维度的取值方位,[lower0,upper0,lower1,upper1,…],lower可以取到,upper取不到
    hist:保存结果的ndarray对象
    accumulate:是否积累,如果设置了这个值,hist不会被清零,直方图结果直接积累到hist中
    '''
    img_gray_hist = cv2.calcHist([img_gray],[0],None,[256],[0,256])
    
    plt.imshow(img_gray,cmap='gray')
    plt.axis('off')
    plt.title('Grayscale Image')
    plt.show()
    
    plt.plot(img_gray_hist)
    plt.title('Grauscale Histogram')
    plt.xlabel('Bins')
    plt.ylabel('# of Pixels')
    plt.show()

    image_1ch4dbcg0oqa1qfr13cn19te3h63a.png-90.2kB

    4.2 带蒙版的颜色直方图

    '''读取模板'''
    mask_file = 'data/masks/mask_crocus_0001.png'
    mask = cv2.imread(mask_file,cv2.IMREAD_UNCHANGED)
    img_gray_hist_with_mask = cv2.calcHist([img_gray],[0],mask,[256],[0,256])
    '''
    图片按位与
    cv2.bitwise_and(src1,src2[,dst[,mask])  -> dst
    参数说明:
    src1:图片1
    src2:图片2
    dst:保存结果的ndarray对象
    mask:蒙版
    '''
    img_masked = cv2.bitwise_and(img_gray,img_gray,mask=mask)  # 使得蒙版与原图重合
    plt.imshow(img_masked,cmap='gray')
    plt.axis('off')
    plt.title('Image with mask')
    ple.show()

    image_1ch4dhsvk15ru1rrk2vj1lia16c23n.png-15.1kB

    4.3 多个颜色直方图

    '''按R、G、B三个通道分别计算颜色直方图'''
    b_hist = cv2.calcHist([img_bgr],[0],None,[256],[0,256])
    g_hist = cv2.calcHist([img_bgr],[1],None,[256],[0,256])
    r_hist = cv2.calcHist([img_bgr],[2],None,[256],[0,256])
    
    '''显示三个通道的颜色直方图'''
    plt.plot(b_hist,label='B',color='blue')
    plt.plot(g_hist,label='G',color='green')
    plt.plot(r_hist,label='R',color='red')
    plt.legend(loc='best')
    plt.xlim([0,256])
    plt.show()

    image_1ch4dja1nbpg1il4ff1l3dr2944.png-34.6kB

    4.4 按多个通道计算颜色直方图

    '''把一个像素的多个通道合在一起看作一个值'''
    hist = cv2.calcHist([img_bgr],[0,1,2],None,[8,8,8],[0,256,0,256,0,256])
    hist.shape          >> (8,8,8) 三维8*8*8的空间
    '''含义:B、G、R取值分别在(0,32)的像素的个数 --> [0,0,0]对应21044个像素'''
    hist[0,0,0]         >> 21044.0

    5 分别使用随机森林、逻辑回归构建图像分类模型

    5.1 图片预处理(读取、贴标签、数据拆分、数据标准化)

    # 导入模块
    import numpy as np
    import cv2
    import os
    from sklearn.model_selection import train_test_split
    from sklearn.preprocessing import LabelEncoder
    from sklearn.ensemble import RandomForestClassifier
    from sklearn.linear_model import LogisticRegression
    from sklearn.metrics import confusion_matrix,precision_recall_fscore_support
    import pandas as pd
    import pickle
    # 创建模型输出文件夹
    output_dir='output'
    if not os.path.exists(output_dir):
        os.mkdir(output_dir)
    # 数据准备 - 读取图片并贴标签
    img_dir = './data/images'
    images=[]
    labels=[]
    for fname in os.listdir(img_dir):
        '''跳过不是目标图片的文件'''
        if not fname.startswith('image'):continue
        '''
        合并目录:os.path.join
        import os
        os.path.join('/hello/','good/boy/','doiido')
        >>> '/hello/good/boy/doiido'
        '''
        fpath = os.path.join(img_dir,fname)
        '''根据文件名,提取图片分类'''    
        lab = fpath.split('_')[1]
        '''RGB模式读取图片,读入的图片得到的是ndarray对象,0~255的整数'''
        img = cv2.imread(fpath,cv2.IMREAD_COLOR)
        images.append(img)
        labels.append(lab)
    
    '''将图片标签ID化,输出可由计算机处理的数据,四种花,四种标签,此处相当于多分类问题'''
    label_encoder = LabelEncoder()
    y = label_encoder.fit_transform(labels)
    '''训练集、测试集划分,stratify = y,表示根据图片类别进行分类,确保每个类别图片的均衡性'''
    train_idx,test_idx = train_test_split(range(len(y)),test_size=0.2,stratify = y, random_state = 1234)    # 返回拆分后的索引
    train_y = y[train_idx]
    test_y = y[test_idx]

    5.2 使用RGB颜色直方图做特征训练分类器

    5.2.1 获取每个图片的RGB特征数据
    # 计算RGB颜色直方图
    '''定义特征转化函数'''
    def transform(img):
        '''每个通道等分为8组后计算直方图'''
        hist = cv2.calcHist([img],[0,1,2],None,[8]*3,[0,256]*3)
        '''将8*8*8的多维数组拉平'''
        return hist.ravel()
    '''提取每个图像的直方图特征,按行合并成一个大的矩阵,每一行即一张图片的长度为512特征'''
    x_rgb = np.row_stack([transform(img) for img in images])
    '''
    根据索引取得处理后的图片分别存入训练集和测试集
    x[[],:]:取得对应行数的所有列数据
    '''
    train_x = x_rgb[train_idx,:]
    test_x = x_rgb[test_idx,:]
    5.2.2 使用随机森林分类器
    model_rgb_rf = RandomForestClassifier(n_estimators =15, max_depth =3, random_state=1234) # 1234随机初始化的种子
    model_rgb_rf.fit(train_x,train_y) # 训练数据集
    5.2.3 定义模型保存
    '''
    保存模型:
    通常情况下在一个地方训练的模型都会在另外一个地方被使用,因此每次训练的模型都需要保存成一个可调用的对象
    本例中采用pickle.dump()方法将训练的模型保存,然后可以实现在其他地方调用,以此方便模型的使用
    '''
    def save_model(model,label_encoder,output_file):
        try:
            with open(output_file,'wb') as outfile:
                pickle.dump({
                    'model':model,
                    'label_encoder':label_encoder
                },outfile)
            return True
        except:
            return False
    # 保存模型
    save_model(model_rgb_rf,label_encoder,os.path.join(output_dir,'model_rgb_rf.pkl'))
    5.2.4 随机森林模型评估
    '''计算各项评价指标'''
    def eval_model(y_true,y_pred,labels):
        '''计算每个分类器的Precision,Recall,f1,support'''
        P,r,f1,s =precision_recall_fscore_support(y_true,y_pred)
        '''计算总体平均的Precision,Recall,f1,support'''
        tot_P = np.average(P,weights =s)
        tot_r = np.average(r,weights =s)
        tot_f1 = np.average(f1,weights =s)
        tot_s = np.sum(s)
        res1 = pd.DataFrame({
            'Label':labels,
            'Precision':P,
            'Reacll':r,
            'F1':f1,
            'Support':s
        })
        res2 = pd.DataFrame({
            'Label':['总体'],
            'Precision':[tot_P],
            'Recall':[tot_r],
            'F1':[tot_f1],
            'Support':[tot_s]
        })
        res2.index=[999]
        res = pd.concat([res1,res2])
        '''计算混淆矩阵'''
        conf_mat = pd.DataFrame(confusion_matrix(y_true,y_pred),columns=labels,index=labels)
        return conf_mat,res[['Label','Precision','Recall','F1','Support']]
    '''在测试集上计算每个图片的预测分类'''
    y_pred_rgb_rf = model_rgb_rf.predict(test_x)
    '''评估模型'''
    conf_mat_lab_rf,evalues_rf = eval_model(test_y,y_pred_rgb_rf,label_encoder.classes_)

    09-rgb-01-rf-混淆矩阵.png-8.8kB
    09-rgb-01-rf-各项指标.png-15.5kB

    5.2.5 使用逻辑回归训练分类器
    '''l2正则惩罚项,惩罚力度为1'''
    model_rgb_lr = LogisticRegression(penalty ='l2',C=1,random_state=1234) 
    model_rgb_lr.fit(train_x,train_y)
    # 保存模型
    save_model(model_rgb_lr,label_encoder,os.path.join(output_dir,'model_rgb_lr.pkl'))
    5.2.6 逻辑回归模型评估
    '''在测试集上计算每个图片的预测分类'''
    y_pred_rgb_lr = model_rgb_lr.predict(test_x)
    '''评估模型'''
    conf_mat_rgb_lr,evalues_rgb_lr = eval_model(test_y,y_pred_rgb_lr,label_encoder.classes_)

    09-rgb-02-lr-混淆矩阵.png-9kB
    09-rgb-02-lr-各项指标.png-14.8kB

    5.3 使用L*a*b*颜色直方图做特征训练分类器

    5.3.1 计算L*a*b*颜色直方图
    '''定义特征转化函数'''
    def lab_transform(img):
        '''将图片从bgr模式转为Lab模式'''
        img = cv2.cvtColor(img,cv2.COLOR_BGR2LAB)
        '''每个通道等分8组后计算直方图'''
        hist = cv2.calcHist([img],[0,1,2],None,[8]*3,[0,256]*3)
        '''将8*8*8的多维数组拉平'''
        return hist.ravel()
    '''提取每个图像的直方图特征'''
    x_lab = np.row_stack([lab_transform(img) for img in images])
    train_x = x_lab[train_idx,:]
    test_x = x_lab[test_idx,:]
    5.3.2 使用随机森林训练分类器
    model_lab_rf = RandomForestClassifier(n_estimators=15,max_depth=3,random_state=1234)
    model_lab_rf.fit(train_x,train_y)
    save_model(model_lab_rf,label_encoder,os.path.join(output_dir,'model_lab_rf.pkl'))
    5.3.3 评估Lab模式下随机森林分类器
    '''在测试集上计算每个图片的预测分类'''
    y_pred_lab_rf = model_lab_rf.predict(test_x)
    '''评估模型'''
    conf_mat_lab_rf ,evalues_lab_rf = eval_model(test_y,y_pred_lab_rf,label_encoder.classes_)

    10-lab-01-rf-混淆矩阵.png-8.7kB
    10-lab-01-rf-各项指标.png-15.1kB

    5.3.4 使用逻辑回归分类器
    model_lab_lr = LogisticRegression(penalty='l2',C=1,random_state=1234)
    model_lab_lr.fit(train_x,train_y)
    save_model(model_lab_lr,label_encoder,os.path.join(output_dir,'model_lab_lr.pkl'))
     
     
    • 1
    • 2
    • 3
    5.3.5 评估Lab模式下逻辑回归分类器
    '''在测试集上计算每个图片的预测分类'''
    y_pred_lab_lr = model_lab_lr.predict(test_x)
    '''评估模型'''
    conf_mat_lab_lr ,evalues_lab_lr = eval_model(test_y,y_pred_lab_lr,label_encoder.classes_)

    10-lab-02-lr-混淆矩阵.png-8.8kB
    10-lab-02-lr-各项指标.png-14.7kB

    6 使用分类器对新图像分类

    '''定义一个用于分类的Predictor'''
    class Predictor(object):
        def __init__(self,model_file):
            with open(model_file,'rb') as infile:
                self.loaded = pickle.load(infile)
            self.model = self.loaded['model']
            self.label_encoder = self.loaded['label_encoder']
    
        '''实现分类逻辑'''
        def predict(self,img_file):
            '''读取图像文件'''
            img = cv2.imread(img_file,cv2.IMREAD_COLOR)
            '''颜色空间转为Lab'''
            img = cv2.cvtColor(img,cv2.COLOR_BGR2LAB)
            '''计算颜色直方图,返回8*8*8的nadarray'''
            x = cv2.calcHist([img],[0,1,2],None,[8]*3,[0,256]*3)
            '''变形为(1,512)的ndarray'''
            x = x.reshape((1,-1))
            '''预测分类'''
            y = self.model.predict(x)
            '''转化为原始标签'''
            label = self.label_encoder.inverse_transform(y)
            return label
    predictor = Predictor(os.path.join(output_dir,'model_lab_rf.pkl'))
    '''对新图片进行分类'''
    predictor.predict('data/images/image_crocus_0001.png')
    >> array(['crocus'], dtype='<U9')
    
    • 3
      点赞
    • 7
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值