使用传统图像处理算法+机器学习进行shadow detection

本文介绍了结合加权中值滤波、MeanShift图像分割和SVM分类器的阴影检测方法。通过图像预处理、特征提取和机器学习模型,实现了对阴影和非阴影区域的有效区分,适用于不同场景,但在数据量和分类器选择上仍有优化空间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

阴影是图像中常见的现象,它们对于场景理解和分析非常重要。由于阴影区域通常比较暗淡,而且与周围物体区别较大,因此在图像处理和计算机视觉领域中,阴影检测是一个重要的研究方向。传统的阴影检测算法通常基于阈值或边缘检测等基本技术,但这些方法难以应对复杂的场景和不同光照条件下的挑战。近年来,随着AI技术的发展,基于卷积神经网络(CNN)的阴影检测方法成为了一个热门的研究方向,并在许多实际场景中取得了较好的性能。本文将介绍如何结合传统的图像处理算法和机器学习技术来实现阴影检测,并探讨这种方法在不同场景下的效果和适用性。

技术方案

step1:对静态图像进行阴影检测前首先通过加权中值滤波进行图像预处理
step2:对于处理后的图像使用mean shift算法将图像分割成若干区域
step3:对分割好的图像区域,提取颜色特征、亮度特征、纹理特征归一化输入svm分类器得到区域对信息,根据区域对信息结合阴影特征完成阴影检测

加权中值滤波是一种基于排序的滤波方法,它可以有效地去除图像中的噪声和小细节。在阴影检测中,加权中值滤波可以用于平滑阴影边缘,从而提高阴影检测的准确性。

Mean Shift算法是一种基于密度估计的聚类方法,可以用于图像分割和目标跟踪等任务。在阴影检测中,Mean Shift算法可以用于聚类图像中的像素,以便进一步分离阴影区域和非阴影区域。

图像特征是指从图像中提取出的代表图像特点的数值量。在阴影检测中,常用的图像特征包括颜色特征、亮度特征、纹理特征等。这些特征可以用来训练分类器,以便将阴影区域和非阴影区域进行区分。

SVM分类器是一种常用的监督学习算法,它可以用于分类和回归等任务。在阴影检测中,SVM分类器可以用于训练一个分类模型,以便将图像中的像素分成阴影区域和非阴影区域。SVM分类器的优点是可以适应不同的图像特征,因此在不同的场景中具有较好的通用性。

代码呈现

图像分割

阴影检测第一步就是对图像进行分割,图像分割用到的mean shift算法将图像分割成若干区域。当然,在做图像分割之前,先做对图像进行了加权中值滤波进行图像预处理。

因为用到了opencv的接口的中值加权滤波,所以需要安装 pip install opencv-contrib-python==3.4.18.65

import cv2
import os
import numpy as np
from sklearn import cluster
from sklearn.cluster import MeanShift, estimate_bandwidth
from itertools import cycle
from PIL import Image

import matplotlib.pyplot as plt
import matplotlib.pylab as pylab


is_Debug = False

def image_preprocess(img):

    kernel = 7
    
    filtered = cv2.ximgproc.weightedMedianFilter(img, img, kernel)   ##将图像进行加权中值滤波处理

    return filtered


def get_segment_parts(img_orig, labels, n_clusters_):

    contours_ret = []
    segments_ret = []
    for i in range(n_clusters_):

        img = np.zeros((img_orig.shape), np.uint8)

        img[labels == i] = [255,255,255]     

        gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
        ret, binary = cv2.threshold(gray,127,255,cv2.THRESH_BINARY)  
        kernel = np.ones((7,7), dtype=np.uint8)
        binary = cv2.morphologyEx(binary, cv2.MORPH_OPEN, kernel)
        contours, hierarchy = cv2.findContours(binary,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)[-2:]

        if len(contours) == 0: continue
        contours_ret.append(contours)
        ## get contour roi image
        imgroi = np.zeros(img_orig.shape, dtype=np.uint8) # Create mask where white is what we want, black otherwise
        cv2.drawContours(imgroi, contours, -1, (255,255,255), -1, cv2.LINE_AA) # Draw filled contour in mask
        roi = cv2.bitwise_and(img_orig,imgroi)
        segments_ret.append(roi)

        if is_Debug:
            
            cv2.drawContours(img,contours,-1,(0,0,255),3)  
            cv2.imwrite('contours_' + str(i) + '.jpg', img)
            cv2.imwrite('roi_' + str(i) + '.jpg', roi)
        


    return segments_ret,contours_ret


def image_segment(img_input, top_k=5):
    #image = Image.open('1-1.png')

    
    img_pre = image_preprocess(img_input)

    image_arr = np.array(img_pre)

    original_shape = image_arr.shape

    w = original_shape[1] #宽度

    h = original_shape[0] #高度

    # Flatten image.
    X = np.reshape(image_arr, [-1, 3])

    bandwidth = estimate_bandwidth(X, quantile=0.1, n_samples=100)

    #print(bandwidth)
    ms = MeanShift(bandwidth=bandwidth, bin_seeding=True)
    ms.fit(X)

    labels = ms.labels_
    #print(labels.shape)
    cluster_centers = ms.cluster_centers_
    #print(cluster_centers.shape)

    labels_unique = np.unique(labels)
    n_clusters_ = len(labels_unique)

    #print("number of estimated clusters : %d" % n_clusters_)
    segmented_image = np.reshape(labels, original_shape[:2])  # Just take size, ignore RGB channels.

    n_clusters_ = min(top_k, n_clusters_)
    
    segments_ret,contours_ret = get_segment_parts(img_input, segmented_image, n_clusters_)

    return segments_ret,contours_ret

输入一张带有阴影的图片
请添加图片描述

分割出来的各个部分的图片如下
在这里插入图片描述
其中第四张图片就是带有阴影的图片
请添加图片描述
可以看出用上述的技术方案分割出来的各个图块的效果还是比较不错的

特征提取

在完成上述的图像分割之后,需要再对每个分割后的图像单独做图像的特征提取,这里选择的特征主要包含颜色,纹理和亮度特征,具体看代码

from PIL import Image
import numpy as np
import os
from skimage.feature import local_binary_pattern
import cv2
from collections import OrderedDict
from scipy.stats import skew
from scipy.stats import kurtosis
from skimage import feature


def addFeatureList(prefix, value, eps=1e-5):
    features = OrderedDict()
    for i in range(len(value)):
        features[prefix+'_'+str(i)] = value[i] / 255 + eps
    return features

class LocalBinaryPatterns:
	def __init__(self, numPoints, radius):
		# store the number of points and radius
		self.numPoints = numPoints
		self.radius = radius
	def describe(self, image, eps=1e-5):
		# compute the Local Binary Pattern representation
		# of the image, and then use the LBP representation
		# to build the histogram of patterns
		lbp = feature.local_binary_pattern(image, self.numPoints,
			self.radius, method="uniform")
		(hist, _) = np.histogram(lbp.ravel(),
			bins=np.arange(0, self.numPoints + 3),
			range=(0, self.numPoints + 2))
		# normalize the histogram
		hist = hist.astype("float")
		hist /= (hist.sum() + eps)
		# return the histogram of Local Binary Patterns
		return hist

    

def color_feature_ex(img):

    features = OrderedDict()
    color_featrue = []
    
    r,g,b = cv2.split(img)
    # 一阶矩
    r_mean = float(r.sum()) / np.count_nonzero(r)
    g_mean = float(g.sum()) / np.count_nonzero(g)
    b_mean = float(b.sum()) / np.count_nonzero(b)
    # 二阶矩
    r_std = np.std(r)
    g_std = np.std(g)
    b_std = np.std(b)
    #三阶矩
    r_offset = (np.mean(np.abs((r - r_mean)**3)))**(1./3)
    g_offset = (np.mean(np.abs((g - g_mean)**3)))**(1./3)
    b_offset = (np.mean(np.abs((b - b_mean)**3)))**(1./3)
    color_featrue.extend([r_mean,g_mean,b_mean,r_std,g_std,b_std,r_offset,g_offset,b_offset])

    features.update(addFeatureList('color', color_featrue))

    return features


def texture_feature_ex(img, hist_size=256, lbp_radius=1, lbp_point=8):

    img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    lbp = LocalBinaryPatterns(5,2)
    feature = lbp.describe(img)
    feature_ret = OrderedDict()
    for i in range(len(feature)):
        feature_ret['lbp_'+str(i)] = feature[i]


    return feature_ret

    


def brightness_feature_ex(img, eps=1e-5):

    feature_ret = OrderedDict()
    lab_img = cv2.cvtColor(img, cv2.COLOR_BGR2LAB)
    l,a,b = cv2.split(lab_img)   ##Lab颜色空间中的L分量用于表示像素的亮度,取值范围是[0,100],表示从纯黑到纯白
    brightness = np.max(l)
    feature_ret['brightness'] = brightness / 255.0 + eps
    return feature_ret


def image_feature_ex(img):

    img_feature_all = OrderedDict()

    feature_c =  color_feature_ex(img.copy())
    feature_t  = texture_feature_ex(img.copy())
    feature_b  = brightness_feature_ex(img.copy())


    img_feature_all.update(feature_c)
    img_feature_all.update(feature_t)
    img_feature_all.update(feature_b)

    return img_feature_all

每个图像块提取出来的特征如下所示
在这里插入图片描述
其中第一列是图像名,最后一列是标签,1代表有阴影,0代表没有阴影,中间的列则是这个图片的所有特征

svm模型训练

支持向量机(Support Vector Machine,SVM)是一种常用的监督学习算法,在数据分类和回归问题上都有良好的表现。在图像处理和计算机视觉领域中,SVM被广泛应用于图像分割、目标检测、阴影检测等任务中。

SVM算法的基本思想是找到最优的超平面,将不同类别的数据分离开来。在实际应用中,SVM分类器还可以通过引入核函数,将非线性可分的数据映射到高维空间中,从而使得它们可以线性分割。SVM算法的优点是可以得到较高的分类精度,并且在数据维度较高的情况下仍然能够处理。

import os
from image_feature_extract import image_feature_ex
from tqdm import tqdm
import pandas as pd
import numpy as np
import cv2
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
import pickle
from sklearn import svm
from sklearn.metrics import accuracy_score


def model_train(data_root):

    key_v = ['color_0', 'color_1', 'color_2', 'color_3', 'color_4', 'color_5', 'color_6', 'color_7', 
            'color_8', 'lbp_0', 'lbp_1', 'lbp_2', 'lbp_3', 'lbp_4', 'lbp_5', 'lbp_6', 'brightness']

    key_l = ['label']


    df = pd.read_csv(data_root)

    X = df[key_v].values

    Y = df[key_l].values

    X_train, X_test, y_train, y_test = train_test_split(X, Y,  test_size = 0.1, random_state=1237)

    print('Total {} train {} test {}'.format(len(X), len(X_train), len(X_test)))

    #model train
    clf = svm.SVC(C=0.8, kernel='rbf', gamma=20, decision_function_shape='ovr')

    clf.fit(X_train, y_train.ravel())

    #Calculate the accuracy of svc classifier
    print("训练集:",clf.score(X_train,y_train))
    print("测试集:",clf.score(X_test,y_test))

    ## save model
    s=pickle.dumps(clf)
    f=open('svm.model', "wb+")
    f.write(s)
    f.close()

    #model_predict
    f2=open('svm.model','rb')
    s2=f2.read()
    model1=pickle.loads(s2)

    predicted = model1.predict(X_test)

    acc = accuracy_score(y_test, predicted)

    print("test dataset acc ", acc)

在标注的2500条数据上训练svm模型,最终可以得到87.65左右的一个精度
在这里插入图片描述

结语

本博客介绍了一种结合传统算法和机器学习算法的阴影检测的技术方案,包括传统的加权中值滤波和Mean Shift算法,以及基于机器学习的图像特征提取和SVM分类器方法。通过最终的效果来看,可以看出这些技术方案都具有一定的优势和适用性,可以根据不同的场景和任务要求来选择使用,但是可能因为标注数据量不够,同时svm这类分类器效果也不够好(其实可以尝试xgb等其他更好的分类器)。

然而,阴影检测仍然是一个具有挑战性的课题,当前存在许多问题有待进一步解决。例如,如何在不同的场景和光照条件下提高阴影检测的准确率和鲁棒性,如何处理复杂场景下的遮挡、反射等问题,如何将阴影检测与目标检测、图像分割等任务结合起来,都是需要进一步探讨和研究的问题。

总之,随着计算机视觉技术的不断发展和应用场景的不断拓展,阴影检测将继续成为一个重要的研究方向和应用领域。我们相信随着学术界和工业界在这个领域的努力与创新,将会有更多更好的技术方案应用于实际场景中,为人类带来更多的实际价值。

SystemVerilog的听课学习笔记,包括讲义截取、知识点记录、注意事项等细节的标注。 目录如下: 第一章 SV环境构建常识 1 1.1 数据类型 1 四、二值逻辑 4 定宽数组 9 foreach 13 动态数组 16 队列 19 关联数组 21 枚举类型 23 字符串 25 1.2 过程块和方法 27 initial和always 30 function逻辑电路 33 task时序电路 35 动态 静态变量 39 1.3 设计例化和连接 45 第二章 验证的方法 393 动态仿真 395 静态检查 397 虚拟模型 403 硬件加速 405 效能验证 408 性能验证 410 第三章 SV组件实现 99 3.1 接口 100 什么是interface 101 接口的优势 108 3.2 采样和数据驱动 112 竞争问题 113 接口中的时序块clocking 123 利于clocking的驱动 133 3.3 测试的开始和结束 136 仿真开始 139 program隐式结束 143 program显式结束 145 软件域program 147 3.4 调试方法 150 第四章 验证的计划 166 4.1 计划概述 166 4.2 计划的内容 173 4.3 计划的实现 185 4.4 计划的进程评估 194 第五章 验证的管理 277 6.1 验证的周期检查 277 6.2 管理三要素 291 6.3 验证的收敛 303 6.4 问题追踪 314 6.5 团队建设 321 6.6 验证的专业化 330 第六章 验证平台的结构 48 2.1 测试平台 49 2.2 硬件设计描述 55 MCDF接口描述 58 MCDF接口时序 62 MCDF寄存器描述 65 2.3 激励发生器 67 channel initiator 72 register initiator 73 2.4 监测器 74 2.5 比较器 81 2.6 验证结构 95 第七章 激励发生封装:类 209 5.1 概述 209 5.2 类的成员 233 5.3 类的继承 245 三种类型权限 protected/local/public 247 this super 253 成员覆盖 257 5.4 句柄的使用 263 5.5 包的使用 269 第八章 激励发生的随机化 340 7.1 随机约束和分布 340 权重分布 353 条件约束 355 7.2 约束块控制 358 7.3 随机函数 366 7.4 数组约束 373 7.5 随机控制 388 第九章 线程与通信 432 9.1 线程的使用 432 9.2 线程的控制 441 三个fork...join 443 等待衍生线程 451 停止线程disable 451 9.3 线程的通信 458 第十章 进程评估:覆盖率 495 10.1 覆盖率类型 495 10.2 功能覆盖策略 510 10.3 覆盖组 516 10.4 数据采样 524 10.5 覆盖选项 544 10.6 数据分析 550 第十一章 SV语言核心进阶 552 11.1 类型转换 552 11.2 虚方法 564 11.3 对象拷贝 575 11.4 回调函数 584 11.5 参数化的类 590 第十二章 UVM简介 392 8.2 UVM简介 414 8.3 UVM组件 420 8.4 UVM环境 425
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

肥宅程序员aka不会游泳的鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值