热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

图像语义分割_图像处理——路面语义分割

检测坑洼,水坑,不同类型的地形等本期是关于路面语义分割方法的。因此,这里的重点是路面模式,例如:车辆行驶在哪种

检测坑洼,水坑,不同类型的地形等

本期是关于路面语义分割方法的。因此,这里的重点是路面模式,例如:车辆行驶在哪种路面上或道路上是否有损坏,还有道路标记和减速带等等。

0.1 简介

有时我们需要确定路面是青沥路面、鹅卵石路面亦或是未铺砌的路面?出于对驾驶员的安全以及车内人员的舒适性的考虑我们需要提前知道路面情况。为了实现这些目标,将使用卷积神经网络(CNN)进行路面的语义分割。CNN体系结构是U-NET [4],该体系结构旨在执行医学图像中的语义分割任务,但已成功应用于许多问题当中。另外,使用resnet34和resnet50完成此方法的实验。对于数据增强步骤,使用来自fastai库的标准选项,并进行了水平旋转和透视变形。

为了训练神经网络并测试和验证结果,使用来自RTK数据集中的701张图像创建了以下路况(GT):

d8c5b2c86cd8fb88809dd7f50f96856a.png
58127094dc9c3440325fcd428478acd6.png

02. 实现步骤

第一步-初始设置

from fastai.vision import *from fastai.vision.interpret import *from fastai.callbacks.hooks import *from pathlib import Pathfrom fastai.utils.mem import *torch.backends.cudnn.benchmark=True

由于我们将使用Google驱动器中的数据集,因此需要对其进行挂载:

from google.colab import drivedrive.mount('/content/gdrive')

大家将看到类似下图的内容,单击链接,我们就获得授权码,因此只需将授权码复制并粘贴到期望的字段中即可。

3e5fcb2c8d170ebb7ce225b69c7d47cd.png

现在,只需将我们的Google云端硬盘作为文件系统访问即可。接下来加载我们的数据。

第二步-准备数据

path = Path('gdrive/My Drive/Colab Notebooks/data/')path.ls()

其中“ image ”是包含原始图像的文件夹。“ labels ”是一个文件夹,其中包含我们将用于训练和验证的图像,这些图像是8位灰度图。在“ colorLabels ”中,有原始的彩色图像,可以将其用于视觉比较。“ valid.txt ”文件包含随机选择用于验证的图像名称列表。最后,“ codes.txt ”文件包含带有类名称的列表。

60e4e676e9616a2d4659eababa6d5785.png

codes = np.loadtxt(path/'codes.txt', dtype=str); code

ab41b69760e8d9fbd5b155e5c5f36127.png

现在,我们定义原始图像和GT图像的路径,从而可以访问文件夹中的所有图像。

path_lbl = path/'labels'path_img = path/'images'fnames = get_image_files(path_img)fnames[:3]len(fnames)lbl_names = get_image_files(path_lbl)lbl_names[:3]len(lbl_names)img_f = fnames[139]img = open_image(img_f)img.show(figsize=(5,5))

我们可以看到一个示例,数据集中的图像139。

3ce87fb4840e4c9ffbd89540f38831d8.png

接下来,我们使用一个函数来从原始图像中推断文件名,该文件名负责每个像素的颜色编码。

get_y_fn = lambda x: path_lbl/f'{x.stem}{x.suffix}'mask = open_mask(get_y_fn(img_f))mask.show(figsize=(5,5), alpha=1)src_size = np.array(mask.shape[1:])src_size,mask.data

第三步 —无权重检测

现在我们进入第3步。让我们创建一个DataBunch,使用数据块API训练我们的第一个模型。定义图像来源,将用于验证的图像与原始图像建立对应关系。对于数据扩充,fastai库提供了很多选项,但是在这里,我们将仅使用带有的默认选项get_transforms(),该选项由随机的水平旋转和透视变形组成。在transform调用时我们要令tfm_y=True,以确保每个蒙版及其原始图像的数据集中数据扩充的转换都相同。想象一下,如果我们旋转原始图像,但是与该图像相对应的蒙版没有旋转,那将是多么混乱!

size = src_sizefree = gpu_mem_get_free_no_cache()# the max size of bs depends on the available GPU RAMif free > 8200: bs=8else: bs=4print(f"using bs={bs}, have {free}MB of GPU RAM free")src = (SegmentationItemList.from_folder(path_img) .split_by_fname_file('../valid.txt') .label_from_func(get_y_fn, classes=codes)) data = (src.transform(get_transforms(), size=size, tfm_y=True) .databunch(bs=bs) .normalize(imagenet_stats))

使用lesson3-camvid定义准确度度量和权衰减。我们使用resnet34模型,定义学习率lr_find(learn)为1e-4。

name2id = {v:k for k,v in enumerate(codes)}def acc_rtk(input, target): target = target.squeeze(1) mask = target != 0 return (input.argmax(dim=1)[mask]==target[mask]).float().mean() metrics=acc_rtkwd=1e-2learn = unet_learner(data, models.resnet34, metrics=metrics, wd=wd)lr_find(learn)learn.recorder.plot()

bcf0eb97f7e2e8b6e7140301a1b044b9.png

接下来,我们运行fit_one_cycle()10次以检查模型的运行情况。

lr=1e-4learn.fit_one_cycle(10, slice(lr), pct_start=0.9)

1eb94bc7dfe06505600ae13f77ba6f20.png

interp = SegmentationInterpretation.from_learner(learn)top_losses, top_idxs = interp.top_losses((288,352))mean_cm, single_img_cm = interp._generate_confusion()df = interp._plot_intersect_cm(mean_cm, "Mean of Ratio of Intersection given True Label")

48679e427304eeef8c9fa8246f9b8931.png

别忘了保存我们到目前为止训练的模型。

learn.save('stage-1')

slice关键字用于获取起始值和终止值,在第一层以起始值开始训练,并且在到达终止值时结束。

learn.unfreeze()lrs = slice(lr/400,lr/4)learn.fit_one_cycle(100, lrs, pct_start=0.9)learn.save('stage-2')

31e437f41c5070bc1e0383292f12f1b8.png

这是我们的第一个没有权重的模型,该模型在路面上可以正常使用,但并不普适。

f0b64cfdb7c51dfbca639b9340c9e01a.png

第四步-带有权重的模型

我们还要继续使用第一个模型。这部分与第3步几乎完全相同,因为数据绑定,我们只需要记住加载先前的模型即可。

learn.load('stage-2')

在我们开始培训过程之前,我们需要加权重。我定义了这些权重,以便尝试与每个类在数据集中出现的数量(像素数)成正比。

balanced_loss = CrossEntropyFlat(axis=1, weight=torch.tensor([1.0,5.0,6.0,7.0,75.0,1000.0,3100.0,3300.0,0.0,270.0,2200.0,1000.0,180.0]).cuda())learn = unet_learner(data, models.resnet34, metrics=metrics, loss_func=balanced_loss, wd=wd)

其余部分与前面介绍的第三步完全一样。得到的结果有什么变化。

d199cc0126bd295099bd4b7a21d9760f.png

现在,对于所有类来说,我们似乎都有一个更合理的结果。记住要保存!

learn.save('stage-2-weights')

结果

最后,让我们看看我们的图像。首先,最好保存我们的结果或测试图像。

img_f = fnames[655]img = open_image(img_f)img.show(figsize=(5,5))prediction = learn.predict(img)prediction[0].show(figsize=(5,5))results_save = 'results'path_rst = path/results_savepath_rst.mkdir(exist_ok=True)def save_preds(names): i=0 #names = dl.dataset.items for b in names: img_s = fnames[i] img_toSave = open_image(img_s) img_split = f'{img_s}' img_split = img_split[44:] predictionSave = learn.predict(img_toSave) predictionSave[0].save(path_rst/img_split) #Save Image i += 1 print(i) save_preds(fnames)

可是等等!图像全部看起来都是黑色的,我们的结果在哪里???冷静一下,这些就是结果,只是没有颜色图,如果在整个屏幕上以高亮度打开这些图像之一,则可以看到小的变化,即“十一色灰色”。因此,让我们对结果进行上色以使其更具表现力吗?现在,我们将使用OpenCV并创建一个新文件夹来保存彩色结果。

import osimport globimport base64import cv2 as cvcolored_results = 'results_color'path_crst = path/colored_resultspath_crst.mkdir(exist_ok=True)

因此,我们创建了一个函数来识别每个变化并为每个像素着色。

def colorfull(image): # grab the image dimensions #height = image.shape[0] #width = image.shape[1] width = 288 height = 352 # loop over the image, pixel by pixel for x in range(width): for y in range(height): b, g, r = frame[x, y] if (b, g, r) == (0,0,0): #background frame[x, y] = (0,0,0) elif (b, g, r) == (1,1,1): #roadAsphalt frame[x, y] = (85,85,255) elif (b, g, r) == (2,2,2): #roadPaved frame[x, y] = (85,170,127) elif (b, g, r) == (3,3,3): #roadUnpaved frame[x, y] = (255,170,127) elif (b, g, r) == (4,4,4): #roadMarking frame[x, y] = (255,255,255) elif (b, g, r) == (5,5,5): #speedBump frame[x, y] = (255,85,255) elif (b, g, r) == (6,6,6): #catsEye frame[x, y] = (255,255,127) elif (b, g, r) == (7,7,7): #stormDrain frame[x, y] = (170,0,127) elif (b, g, r) == (8,8,8): #manholeCover frame[x, y] = (0,255,255) elif (b, g, r) == (9,9,9): #patchs frame[x, y] = (0,0,127) elif (b, g, r) == (10,10,10): #waterPuddle frame[x, y] = (170,0,0) elif (b, g, r) == (11,11,11): #pothole frame[x, y] = (255,0,0) elif (b, g, r) == (12,12,12): #cracks frame[x, y] = (255,85,0) # return the colored image return image

接下来,我们读取每个图像,调用函数并保存最终结果。

fqtd = 0filenames = [img for img in glob.glob(str(path_rst/"*.png"))]filenames.sort()for img in filenames: frame = cv.imread(img) frame = colorfull(frame) frame = cv.cvtColor(frame,cv.COLOR_BGR2RGB) name = "%09d.png"%fqtd cv.imwrite(os.path.join(path_crst, name), frame) fqtd += 1 print(fqtd)print("Done!")

使用以下过程,%timeit我们可以达到以下目的,因此此过程可能会花费不必要的时间:

b358439555f330617092837ec9f6dc1f.png
9b018da6729bee49b324ef834a5358f0.png

03. 总结

在很多情况下,识别路面状况都很重要,基于此车辆或驾驶员可以做出调整,使驾驶变的更加安全,舒适和高效。这在可能存在更多道路维护问题或相当数量的未铺设道路的发展中国家中尤其重要。对于处理路面变化的环境,对于高速公路分析和养护部门也很有用,以便使他们在评估道路质量和确定需要维护的地方的工作自动化。



推荐阅读
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文介绍了使用Spark实现低配版高斯朴素贝叶斯模型的原因和原理。随着数据量的增大,单机上运行高斯朴素贝叶斯模型会变得很慢,因此考虑使用Spark来加速运行。然而,Spark的MLlib并没有实现高斯朴素贝叶斯模型,因此需要自己动手实现。文章还介绍了朴素贝叶斯的原理和公式,并对具有多个特征和类别的模型进行了讨论。最后,作者总结了实现低配版高斯朴素贝叶斯模型的步骤。 ... [详细]
  • 超级简单加解密工具的方案和功能
    本文介绍了一个超级简单的加解密工具的方案和功能。该工具可以读取文件头,并根据特定长度进行加密,加密后将加密部分写入源文件。同时,该工具也支持解密操作。加密和解密过程是可逆的。本文还提到了一些相关的功能和使用方法,并给出了Python代码示例。 ... [详细]
  • 本文介绍了在Android开发中使用软引用和弱引用的应用。如果一个对象只具有软引用,那么只有在内存不够的情况下才会被回收,可以用来实现内存敏感的高速缓存;而如果一个对象只具有弱引用,不管内存是否足够,都会被垃圾回收器回收。软引用和弱引用还可以与引用队列联合使用,当被引用的对象被回收时,会将引用加入到关联的引用队列中。软引用和弱引用的根本区别在于生命周期的长短,弱引用的对象可能随时被回收,而软引用的对象只有在内存不够时才会被回收。 ... [详细]
  • DSP中cmd文件的命令文件组成及其作用
    本文介绍了DSP中cmd文件的命令文件的组成和作用,包括链接器配置文件的存放链接器配置信息、命令文件的组成、MEMORY和SECTIONS两个伪指令的使用、CMD分配ROM和RAM空间的目的以及MEMORY指定芯片的ROM和RAM大小和划分区间的方法。同时强调了根据不同芯片进行修改的必要性,以适应不同芯片的存储用户程序的需求。 ... [详细]
  • OpenMap教程4 – 图层概述
    本文介绍了OpenMap教程4中关于地图图层的内容,包括将ShapeLayer添加到MapBean中的方法,OpenMap支持的图层类型以及使用BufferedLayer创建图像的MapBean。此外,还介绍了Layer背景标志的作用和OMGraphicHandlerLayer的基础层类。 ... [详细]
  • 本文介绍了在PostgreSQL中批量导入数据时的优化方法。包括使用unlogged表、删除重建索引、删除重建外键、禁用触发器、使用COPY方法、批量插入等。同时还提到了一些参数优化的注意事项,如设置effective_cache_size、shared_buffer等,并强调了在导入大量数据后使用analyze命令重新收集统计信息的重要性。 ... [详细]
  • 1Lock与ReadWriteLock1.1LockpublicinterfaceLock{voidlock();voidlockInterruptibl ... [详细]
  • Mono为何能跨平台
    概念JIT编译(JITcompilation),运行时需要代码时,将Microsoft中间语言(MSIL)转换为机器码的编译。CLR(CommonLa ... [详细]
  • 我用Tkinter制作了一个图形用户界面,有两个主按钮:“开始”和“停止”。请您就如何使用“停止”按钮终止“开始”按钮为以下代码调用的已运行功能提供建议 ... [详细]
  • 在本教程中,我们将看到如何使用FLASK制作第一个用于机器学习模型的RESTAPI。我们将从创建机器学习模型开始。然后,我们将看到使用Flask创建AP ... [详细]
  • 由于同源策略的限制,满足同源的脚本才可以获取资源。虽然这样有助于保障网络安全,但另一方面也限制了资源的使用。那么如何实现跨域呢,以下是实现跨域的一些方法。 ... [详细]
  • 数学建模入门python绘制频率直方图
    文章目录例题数据处理绘图操作调用演示例题数据处理将以下的数据保存到磁盘上17275169551696417165167471716216867165521696216865 ... [详细]
  • Matlab 中的一些小技巧(2)
    1.Ctrl+D打开子程序  在MATLAB的Editor中,将输入光标放到一个子程序名称中间,然后按Ctrl+D可以打开该子函数的m文件。当然这个子程序要在路径列表中(或在当前工作路径中)。实际上 ... [详细]
author-avatar
玩在青岩堡欢乐长桌宴_840
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有