传统图像处理(二)

文章目录

  • 目标检测
  • 图像分割
    • GrabCut算法
  • 图像搜索

目标检测

目标检测可以使用HOG+SVM的方式实现。在单类别物体检测任务中,可以使用OpenCV的"特征点检测+特征描述"匹配方式快速检测目标,但是这种方式一般适用于单个物体检测。如果图片中存在多个同类别物体,就需要借助聚类方法来实现。
OpenCV中的ORB检测器算法采用FAST算法来检测特征点,使用BRIEF进行特征点描述(用于匹配特征点)。
其中FAST算法寻找角点的依据是:若某像素与其邻域内足够多的像素相差较大,则该像素可能是角点。

cv2.ORB_create(nfeatures = 500,
             scaleFactor = 1.2,
             nlevels = 8,
             edgeThreshold = 31,
             firstLevel = 0,
             WTA_K = 2,
             scoreType = HARRIS_SCORE,
             patchSize = 31,
             fastThreshold = 20)
nfeatures - int:确定要查找的最大要素(关键点)数。

scaleFactor - float:金字塔抽取率,必须大于1。ORB使用图像金字塔来查找要素,因此必须提供金字塔中每个图层与金字塔所具有的级别数之间的比例因子。scaleFactor = 2表示经典金字塔,其中每个下一级别的像素比前一级低4倍。大比例因子将减少发现的功能数量。

nlevels - int:金字塔等级的数量。最小级别的线性大小等于

input_image_linear_size / pow(scaleFactor,nlevels)。

edgeThreshold - - int:未检测到要素的边框大小。由于关键点具有特定的像素大小,因此必须从搜索中排除图像的边缘。 edgeThreshold的大小应该等于或大于patchSize参数。

firstLevel - int:此参数允许您确定应将哪个级别视为金字塔中的第一级别。它在当前实现中应为0。通常,具有统一标度的金字塔等级被认为是第一级。

WTA_K - int:用于生成定向的BRIEF描述符的每个元素的随机像素的数量。可能的值为2,34,其中2为默认值。例如,值3意味着一次选择三个随机像素来比较它们的亮度。返回最亮像素的索引。由于有3个像素,因此返回的索引将为0,12。

scoreType - int:此参数可以设置为HARRIS_SCORE或FAST_SCORE。默认的HARRIS_SCORE表示Harris角算法用于对要素进行排名。该分数仅用于保留最佳功能。 FAST_SCORE生成的关键点稍差,但计算起来要快一些。

patchSize - int:面向简要描述符使用的补丁的大小。当然,在较小的金字塔层上,由特征覆盖的感知图像区域将更大。
前两个参数(nfeatures和scaleFactor)可能是最有可能改变的参数。

利用orb.detectAndCompute()检测关键点并计算

kp,des = orb.detectAndCompute(gray,None)
参数:

·gray: 进行关键点检测的图像,注意是灰度图像

返回:

·kp: 关键点信息,包括位置,尺度,方向信息

·des: 关键点描述符,每个关键点BRIEF特征向量,二进制字符串,


函数np.append(arr, values, axis=None)
作用:
为原始array添加一些values

参数:
arr:需要被添加values的数组
values:添加到数组arr中的值(array_like,类数组)
axis:可选参数,如果axis没有给出,那么arr,values都将先展平成一维数组。注:如果axis被指定了,那么arr和values需要同为一维数组或者有相同的shape,否则报错:ValueError: arrays must have same number of dimensions
原文链接:https://blog.csdn.net/weixin_42216109/article/details/93889047
#coding=utf-8
import cv2
from matplotlib import pyplot as plt
import numpy as np
from sklearn.cluster import KMeans
#最小匹配次数
MIN_MATCH_CONT=10
#读写图片
img1 = cv2.imread("cv.jpg", cv2.COLOR_BGR2RGB)
# img1=cv2.resize(img1,(120,120))
# print(img1.shape)
# plt.imshow(img1)
# plt.show()
img2=cv2.imread("cvs.jpg",cv2.COLOR_BGR2RGB)


#创建ORB检测器
orb=cv2.ORB_create(1000,1.2,nlevels=12,edgeThreshold=3)

#寻找关键点和关键点的特征描述
kp1,des1=orb.detectAndCompute(img1,None)
kp2,des2=orb.detectAndCompute(img2,None)

#对于关键点聚类
x = np.array(kp2[0].pt).reshape(-1, 2)
print(x)
for i in range(len(kp2)):
    x = np.append(x, [kp2[i].pt]).reshape(-1, 2)
x=x[1:len(x)]
print(len(x))
clf=KMeans(n_clusters=3)

#1. k-means需要调的参数很少,主要是n_clusters,簇的个数

#  2.返回对象的属性

# (1)cluster_centers_:每个簇的中心坐标

# (2)labels_:每个数据点的标签

# (3)inertia_:同一类别下的所有点到簇的平方距离

# # #训练模型
clf.fit(x)
labels=clf.labels_
cluster_centers=clf.cluster_centers_
# #计算标签的数量
labels_unique=np.unique(labels)
n_clusters_=len(labels_unique)

# # #按聚类将关键点加入到列表
s=[None]*n_clusters_
for i in range(n_clusters_):
    l=clf.labels_
    d,=np.where(l==i)
    print(d.__len__())
    s[i]=list(kp2[xx] for xx in d)

#对每个聚类中的关键点进行演算
des2_=des2
for i in range(n_clusters_):
    kp2=s[i]
    l=clf.labels_
    d,=np.where(l==i)
    des2=des2_[d,]
    #定义匹配算法
    FLANN_INDEX_KDTREE=0
    index_params=dict(algorithm=FLANN_INDEX_KDTREE,trees=5)
    search_params=dict(checks=50)
    flann=cv2.FlannBasedMatcher(index_params,search_params)
    des1=np.float32(des1)
    des2=np.float32(des2)
    #开始匹配
    matches=flann.knnMatch(des1,des2,2)
    #保存所有匹配合格的点
    good=[]
    for m,n in matches:
        if m.distance <0.7*n.distance:
            good.append(m)
    #如果合格点超过3个,则认为匹配有效
    if len(good) >3:
        src_pts=np.float32([kp1[m.queryIdx].pt for m in good]).reshape(-1,1,2)
        dst_pts=np.float32([kp2[m.trainIdx].pt for m in good]).reshape(-1,1,2)
        #计算基准点到目标点的转换矩阵
        M,mask=cv2.findHomography(src_pts,dst_pts,cv2.RANSAC,2)
        #如果没有找到转换矩阵
        if M is None:
            print("NO Homography")
        else:
            matchesMask=mask.ravel().tolist()
            h,w,n=img1.shape
            pts=np.float32([[0,0],[0,h-1],[w-1,h-1],[w-1,0]]).reshape(-1,1,2)
            dst=cv2.perspectiveTransform(pts,M)
            #绘制检测框
            img2=cv2.polylines(img2,[np.int32(dst)],True,255,3,cv2.LINE_AA)
            draw_params=dict(
                matchColor=(0,255,0),
                singlePointColor=None,
                matchesMask=matchesMask,
                flags=2,

            )
            img3=cv2.drawMatches(img1,kp1,img2,kp2,good,None,**draw_params)
            plt.imshow(img3,"gray"),plt.show()

    else:
        print("Not enough matches are found -%d/%d" %(len(good),MIN_MATCH_CONT))
        matchesMask=None

传统图像处理(二)_第1张图片

图像分割

在opencv中,有两种常用的图像分割算法:一种是分水岭算法,一种是GrabCut算法。

GrabCut算法

关于算法理论
链接: https://blog.csdn.net/kyjl888/article/details/78253829
GrabCut算法的实现步骤:

  • 在图片中定义(一个或者多个)包含物体的矩形。矩形外的区域被自动认为是背景。
  • 对于用户定义的矩形区域,可用背景中的数据来区分它里面的前景和背景区域。
  • 用高斯混合模型(GMM)来对背景和前景建模,并将未定义的像素标记为可能的前景或者背景。
  • 图像中的每一个像素都被看做通过虚拟边与周围像素相连接,而每条边都有一个属于前景或者背景的概率,这是基于它与周边像素颜色上的相似性。
  • 每一个像素(即算法中的节点)会与一个前景或背景节点连接。在节点完成连接后(可能与背景或前景连接),若节点之间的边属于不同终端(即一个节点属于前景,另一个节点属于背景),则会切断他们之间的边,这就能将图像各部分分割出来。
    原文链接:
    https://blog.csdn.net/wsp_1138886114/article/details/104076134
void grabCut(
InputArray img,
InputOutputArray mask,
Rect rect,
InputOutputArray bgdModel,
InputOutputArray fgdModel,
int iterCount,
int mode = GC_EVAL
);

参数说明:
img—待分割的源图像,是83通道
mask—掩码图像,如果使用掩码进行初始化,那么mask保存初始化掩码信息;在执行分割的时候,也可以将用户交互所设定的前景与背景保存到mask中,然后再传入grabCut函数;在处理结束之后,mask中会保存结果。mask只能取以下四种值:(若无标记GCD_BGD或GCD_FGD,则结果只有GCD_PR_BGD或GCD_PR_FGD;)

GCD_BGD(=0),背景;
GCD_FGD(=1),前景;
GCD_PR_BGD(=2),可能的背景;
GCD_PR_FGD(=3),可能的前景。
rect—限定要进行分割的图像范围
bgdModel—背景模型,如果为None,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5;
fgdModel—前景模型,如果为None,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型图像,且行数只能为1,列数只能为13x5;
iterCount—迭代次数,必须大于0;
mode—grabCut函数操作方法,可选:

GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;
GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;
GC_EVAL(=2),执行分割。
输入:图像、被标记好的前景、背景。
输出:分割图像
其中输入的前景、背景指的是一种概率,如果你已经明确某一块区域是背景,那么它属于背景的概率为1;当然如果你觉得它有可能背景,但是没有百分百的肯定,这个时候你就要用到高斯模型,对其进行建模,然后估算概率。现在我以下图为例,用户通过交互输入框选区域,前景位于框选区域内,也就是说矩形区域外的全部属于背景,且概率为百分百。然后方框内可能属于前景,概率需要用高斯混合建模求解。
原文链接:https://blog.csdn.net/wsp_1138886114/article/details/104076134

cv2.grabCut()函数参数

img 输入图像,图像的类型必须为:CV_8UC3
mask 掩模图像,确定前景区域,背景区域,不确定区域,可以设置为cv2.GC_BGD,cv2.GC_FGD,cv2.GC_PR_BGD,cv2.GC_PR_FGD,也可以输入0,1,2,3它的大小和image一样,但是它的格式为CV_8UC1,只能是单通道的,grabcut算法的结果就保存在该图像中。
mask图像的值只能为下面下面4个值(PR,probably表示可能的):计算完成后mask里面值为03,其中0表示背景,1表示前景,2表示可能是背景,3表示可能是前景 ,代码中将02合并为背景 13合并为前景

GC_BGD    = 0,  //背景

GC_FGD    = 1,  //前景 
GC_PR_BGD = 2,  //可能背景

GC_PR_FGD = 3   //可能前景 
代码中将02合并为背景 13合并为前景

根据rectangle生成的mask图像规则为:四边形外面的部分一定是背景,所以在mask图中对应的像素值为GC_BGD,而四边形内部的的值可能为前景,所以对应的像素值为GC_PR_FGD。

rect 前景的矩形,格式为(x,y,w,h),分别为左上角坐标和宽度,高度
bdgModel, fgdModel)临时矩阵变量,只需要创建两个大小为(1,65)np.float64的数组。 
bgdModel(background背景)——背景模型,如果为null,函数内部会自动创建一个bgdModel;bgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;
fgdModel(foreground前景——前景模型,如果为null,函数内部会自动创建一个fgdModel;fgdModel必须是单通道浮点型(CV_32FC1)图像,且行数只能为1,列数只能为13x5;
iterCount 迭代次数,迭代越多,效果越好,但划时间也越长。
mode cv2.GC_INIT_WITH_RECT 或 cv2.GC_INIT_WITH_MASK,使用矩阵模式还是掩模模式。
mode——用于指示grabCut函数进行什么操作,可选的值有:
  GC_INIT_WITH_RECT(=0),用矩形窗初始化GrabCut;
  GC_INIT_WITH_MASK(=1),用掩码图像初始化GrabCut;
  GC_EVAL(=2),执行分割。
参数为GC_INIT_WITH_MASK,这时第三个参数rectangle没有使用,我们必须在调用grabcut函数之前,手工设置mask图像
原文链接:https://blog.csdn.net/m0_37264397/article/details/78922872
cv2.rectangle(picture, (x_min,y_min), (x_max,y_max), (255, 0, 255), -1)
#coding=utf-8
import numpy as np
import cv2
from matplotlib import pyplot as plt

#显示图片
def show(img):
    img=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
    plt.imshow(img)

img=cv2.imread("sun.jpg")
show(img)
#编写grabu函数
def grabcut(img,mask,rect,iter=20):
    img_=img.copy()
    bg_model=np.zeros((1,65),np.float64)
    fg_model=np.zeros((1,65),np.float64)
    cv2.grabCut(img.copy(),mask,rect,bg_model,fg_model,iter,cv2.GC_INIT_WITH_RECT)
    # 根据是否条件满足,返回元素为0/1的数组
    mask2=np.where((mask==2)|(mask==0),0,1).astype('uint8')
    img_=img*mask2[:,:,np.newaxis]
    return img_

# 返回一个和图片等尺寸、且全为0的矩阵
mask=np.zeros(img.shape[:2],np.uint8)
# ret = (240, 213, 1057, 1031)

ret = (2, 2, img.shape[0] - 1, img.shape[1] - 1)
img_cpoy=img.copy()
#画图
cv2.rectangle(img_cpoy,ret[:2],ret[2:],(0,255,0),3)
show(img_cpoy)
img1=grabcut(img,mask,ret)
im=np.hstack((img,img1))
show(im)
plt.show()

传统图像处理(二)_第2张图片

图像搜索

图片所包含的特征能够生成一组“指纹”(不是唯一的),这些“指纹”可以进行比较。改变图片的大小,亮度甚至颜色,都不会改变它的散列值。

感知散列算法是散列算法的一类,主要用来做相似图片的搜索工作,它工作步骤如下:

  • 对图片进行灰度处理
  • 缩小图片的尺寸
  • 简化图片的色彩,原本色彩的值域是[0,255],共256个值,简化色彩就是将色彩可选值的数量进行压缩
  • 计算像素均值
  • 根据像素均值将图片像素值置为0或1
  • 使用汉明距离对比图像
#coding=utf-8
import random
import matplotlib.pyplot as plt
import cv2
import numpy as np

def hash(img):
    #灰度处理
    #cv2.COLOR_BGR2RGB
    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    #缩小尺寸
    img=cv2.resize(img,(8,8))
    print(img)
    #简化色彩
    img=(img/4).astype(np.uint8)*4
    #计算均值
    m=np.mean(img)
    img[img<=m]=0
    img[img>m]=1
    # plt.imshow(img*255,cmap="gray")
    # plt.show()
    return img.reshape(-1)


img1 = cv2.imread("panda1.jpg", cv2.IMREAD_GRAYSCALE)
img2 = cv2.imread("panda2.jpg", cv2.IMREAD_GRAYSCALE)
img3 = cv2.imread("dog.jpg", cv2.IMREAD_GRAYSCALE)

# img1 = cv2.imread("panda1.jpg", 0)
# img2 = cv2.imread("panda2.jpg", 0)
# img3 = cv2.imread("dog.jpg", 0)
#算图像编码
hash_img1=hash(img1)
hash_img2=hash(img2)
hash_img3=hash(img3)
#计算图像之间的汉明距离
distance1=np.sum(hash_img1==hash_img2) /hash_img1.shape[0]
distance2= np.sum(hash_img1 == hash_img3) / hash_img1.shape[0]
#展示结果
plt.subplot(131)
plt.xticks([])
plt.yticks([])
plt.imshow(img1)
plt.subplot(132)
plt.xticks([])
plt.yticks([])
plt.imshow(img2)
plt.title("distance:{}".format(round(distance1,5)))
plt.subplot(133)
plt.xticks([])
plt.yticks([])
plt.imshow(img3)
plt.title("distance:{}".format(round(distance2, 5)))
plt.show()

传统图像处理(二)_第3张图片

你可能感兴趣的:(Python计算机视觉,python,人工智能,计算机视觉)