- 图像识别是识别图像中物体的类别(它属于哪个类)的任务。图像识别通常被称为
Classification
、Categorization
、Clustering
等。 - 一种常见的方法是通过
HOG
、SIFT
、SURF
等方法从图像中提取一些特征,并通过特征确定物体类别。这种方法在CNN
普及之前广泛采用,但CNN
可以完成从特征提取到分类等一系列任务。 - 这里,利用图像的颜色直方图来执行简单的图像识别。算法如下:
- 将训练图像进行减色处理(RGB取4种值)。
- 创建减色图像的直方图。直方图中,RGB分别取四个值,但为了区分它们,
B=[1,4]、G=[5,8]、R=[9,12]
,这样bin=12
。请注意,我们还需要为每个图像保存相应的柱状图。也就是说,需要将数据储存在database = np.zeros((10(训练数据集数), 13(RGB + class), dtype=np.int)
中。 - 将上一个步骤中计算得到的柱状图记为
database
。 - 计算想要识别的图像与直方图之间的差,将差称作特征量。
- 直方图的差异的总和是最小图像是预测的类别。换句话说,它被认为与近色图像属于同一类。
- 计算将想要识别的图像的柱状图(与训练图像的柱状图)的差,将这个差作为特征量。
- 统计柱状图的差,差最小的图像为预测的类别。换句话说,可以认为待识别图像与具有相似颜色的图像属于同一类。
import cv2
import numpy as np
import matplotlib.pyplot as plt
from glob import glob
#除了日常的图像处理三个包opencv、numpy和matplotlib外,还专门引入了glob这个包,这个包只有一个方法,就是glob作用类似于linux的ls,列出列表下的所有文件
## Dicrease color减色,与前面的方法基本一致,将像素范围由0-255压缩到32、96、160、224这四个离散值中
def dic_color(img):
img //= 63
img = img * 64 + 32
return img
## Database
def get_DB():
# get image paths
train = glob("123//train/*")#给出训练所用的文件夹路径
train.sort()#对其排序
# prepare database
db = np.zeros((len(train), 13), dtype=np.int32)#假设train有6张照片,db即为一个6行13列每个元素均为0的二维数组
# each image#对于每一张图片
for i, path in enumerate(train):#均对其进行减色处理,i取0,1,2,3,4,5代表六张图片
img = dic_color(cv2.imread(path))
# get histogram获取直方图
for j in range(4):#j取0,1,2,3代表着灰度值为32、96、160、224这几个离散值的数目
db[i, j] = len(np.where(img[..., 0] == (64 * j + 32))[0])#img[...,0]代表蓝色通道
db[i, j+4] = len(np.where(img[..., 1] == (64 * j + 32))[0])#img[...,1]代表绿色通道
db[i, j+8] = len(np.where(img[..., 2] == (64 * j + 32))[0])#img[...,1]代表红色通道,每个通道占据db每行的4个位置,这样一共占据了12个位置,db每行有13个位置,刚好留最后一个位置放类型
# get class
if 'akahara' in path:
cls = 0
elif 'madara' in path:
cls = 1
# store class label
db[i, -1] = cls#db的最后一列放类型
img_h = img.copy() // 64#得到0,1,2,3四个值
img_h[..., 1] += 4#绿色通道统一加上4
img_h[..., 2] += 8#红色通道统一加上8,将蓝绿红三个通道分开
plt.subplot(2, 5, i+1)#2行5列十个图,依次排列
plt.hist(img_h.ravel(), bins=12, rwidth=0.8)#plt.hist绘制直方图的三个参数12个条数,每个宽度0.8。img_h的取值变为0-11,对简化后的img_h做直方图。
plt.title(path)
print(db)
plt.show()
# get database
get_DB()
上述代码主要对图片进行了减色及统计各通道各个像素点的计数并对种类进行了标记,最终绘出了每张图片减色后的柱状图
下面代码将会输出与各个图像直方图差别最小的(训练数据集的)文件名和预测类别。这种评价方法被称为最近邻法
import cv2
import numpy as np
import matplotlib.pyplot as plt
from glob import glob#导入四个库,与上面代码一致,不解释
#Dicrease color#日常减色处理很简单
def dic_color(img):
img //= 63
img = img * 64 + 32
return img
# Database
def get_DB():
# get training image path
train = glob("123//train//*")
train.sort()
# prepare database
db = np.zeros((len(train), 13), dtype=np.int32)
# prepare path database
pdb = []#整一个放图片路径的数组用来保存图片路径
# each image
for i, path in enumerate(train):
# read image
img = dic_color(cv2.imread(path))
# get histogram
for j in range(4):
db[i, j] = len(np.where(img[..., 0] == (64 * j + 32))[0])
db[i, j + 4] = len(np.where(img[..., 1] == (64 * j + 32))[0])
db[i, j + 8] = len(np.where(img[..., 2] == (64 * j + 32))[0])
# get class
if 'akahara' in path:
cls = 0
elif 'madara' in path:
cls = 1
# store class label
db[i, -1] = cls
# store image path
pdb.append(path)
return db, pdb#返回db和图片最终的路径
# test进行测试
def test_DB(db, pdb):
# get test image path
test = glob("123//test//*")#测试文件的路径
test.sort()
success_num = 0.#成功数目初始化为0
# each image对于每一张图片
for path in test:
# read image
img = dic_color(cv2.imread(path))#先将其进行减色
# get histogram
hist = np.zeros(12, dtype=np.int32)#看好,这次变成12了,上面训练的是带标记的13
for j in range(4):
hist[j] = len(np.where(img[..., 0] == (64 * j + 32))[0])
hist[j + 4] = len(np.where(img[..., 1] == (64 * j + 32))[0])
hist[j + 8] = len(np.where(img[..., 2] == (64 * j + 32))[0])#依次得到hist索引0-12的值
# get histogram difference计算直方图差异
difs = np.abs(db[:, :12] - hist)#difs为一个n行12列的数组,n行代表训练图像有n个
difs = np.sum(difs, axis=1)#计算出每一个训练图片和测试图片的差值和
# get argmin of difference只显示最小差异的那个
pred_i = np.argmin(difs)
# get prediction label得到预测标签
pred = db[pred_i, -1]
if pred == 0:
pl = "akahara"
elif pred == 1:
pl = "madara"
print(path, "is similar >>", pdb[pred_i], " Pred >>", pl)#将结果打印出来
db, pdb = get_DB()
test_DB(db, pdb)
上述代码虽然会识别出图像的类型,但是也存在很大程度的误判,为了防止误判,我们有必要把最近邻算法改成KNN算法,最近邻算法其实也是KNN中K=1的特殊情况
import cv2
import numpy as np
import matplotlib.pyplot as plt
from glob import glob#导入三个常用库opencv、numpy、matplotlib以及一个处理文件的glob库
# Dicrease color减色函数
def dic_color(img):
img //= 63
img = img * 64 + 32
return img
# Database
def get_DB():
# get training image path
train = glob("123//train//*")
train.sort()
# prepare database
db = np.zeros((len(train), 13), dtype=np.int32)
pdb = []
# each train
for i, path in enumerate(train):
# read image
img = dic_color(cv2.imread(path))
# histogram
for j in range(4):
db[i, j] = len(np.where(img[..., 0] == (64 * j + 32))[0])
db[i, j+4] = len(np.where(img[..., 1] == (64 * j + 32))[0])
db[i, j+8] = len(np.where(img[..., 2] == (64 * j + 32))[0])
# get class
if 'akahara' in path:
cls = 0
elif 'madara' in path:
cls = 1
# store class label
db[i, -1] = cls
# add image path
pdb.append(path)
return db, pdb#返回db和图片最终的路径
# test
def test_DB(db, pdb, N=3):#将KNN中N=3作为参数传入测试函数
# get test image path#获取测试文件的路径
test = glob("123//test//*")
test.sort()
accuracy_N = 0.
# each image对于每一张图片
for path in test:
# read image
img = dic_color(cv2.imread(path))#进行减色
# get histogram
hist = np.zeros(12, dtype=np.int32)#生成特征向量
for j in range(4):
hist[j] = len(np.where(img[..., 0] == (64 * j + 32))[0])
hist[j+4] = len(np.where(img[..., 1] == (64 * j + 32))[0])
hist[j+8] = len(np.where(img[..., 2] == (64 * j + 32))[0])#依次得到hist索引0-12的值
# get histogram difference计算直方图差异
difs = np.abs(db[:, :12] - hist)#difs为一个n行12列的数组,n行代表训练图像有n个
difs = np.sum(difs, axis=1)#计算出每一个训练图片和测试图片的差值和
# get top N
pred_i = np.argsort(difs)[:N]#得到前三个最小的值
# predict class index
pred = db[pred_i, -1]#获取到前三个向量的最后一个索引即获取到标签
# get class label
if len(pred[pred == 0]) > len(pred[pred == 1]):#如果标记为0的标签个数大于标记为1的标签个数,则最终为akahara
pl = "akahara"
else:
pl = 'madara'#否则为madara
print(path, "is similar >> ", end='')
for i in pred_i:
print(pdb[i], end=', ')
print("|Pred >>", pl)
# count accuracy
gt = "akahara" if "akahara" in path else "madara"#对gt做标记
if gt == pl:#如果测试正确,则accuracy_N加1
accuracy_N += 1.
accuracy = accuracy_N / len(test)#计算出正确率
print("Accuracy >>", accuracy, "({}/{})".format(int(accuracy_N), len(test)))
db, pdb = get_DB()
test_DB(db, pdb)
总结一下
- 简单图像识别以图像灰度为特征,将BGR三通道拆分,将0-255减色到32、96、160、224四个离散值上,灰度依次排开,B占前四位,G占中央四位,R占后四位,这样,每个图像都会变成一个1行12列的向量,然后用最近邻算法或者KNN来进行图像识别,原理相对比较简单
网友评论