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

19 篇文章 2 订阅
1 篇文章 0 订阅

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

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

以下部分是正文:

by: − − − b y : 朱 世 新


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'))
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')
  • 18
    点赞
  • 158
    收藏
    觉得还不错? 一键收藏
  • 15
    评论
首先,在Python中安装OpenCV库。在终端输入以下命令: ``` pip install opencv-python ``` 接下来,我们可以使用以下代码来实现基于OpenCV的人脸检测算法: ```python import cv2 # 加载分类 face_cascade = cv2.CascadeClassifier('haarcascade_frontalface_default.xml') # 加载图像 img = cv2.imread('test.jpg') # 转换为灰度图像 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测人脸 faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5) # 在图像中标记人脸 for (x, y, w, h) in faces: cv2.rectangle(img, (x, y), (x+w, y+h), (0, 255, 0), 2) # 显示图像 cv2.imshow('img', img) cv2.waitKey() ``` 解释一下代码: 1. 加载分类:我们使用OpenCV内置的CascadeClassifier函数来加载人脸检测分类。这里使用的是`haarcascade_frontalface_default.xml`,你可以在OpenCV官网上找到并下载其他分类。 2. 加载图像:我们使用`cv2.imread()`函数来加载测试图像。 3. 转换为灰度图像:我们将图像转换为灰度图像,因为分类需要输入灰度图像。 4. 检测人脸:我们使用`detectMultiScale()`函数来检测人脸。`scaleFactor`和`minNeighbors`参数可以调整检测的精度和速度。 5. 标记人脸:我们使用`cv2.rectangle()`函数在图像中标记人脸。 6. 显示图像:最后,我们使用`cv2.imshow()`和`cv2.waitKey()`函数来显示图像,并等待用户按下任意键关闭窗口。 这就是基于OpenCV的人脸检测算法实现

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值