如果已经获取到了矢量数据,可直接跳过此步骤。由于本文是以制作用于进行遥感影像中农村道路提取的数据集无法从网络上获得矢量数据,所以自行获取矢量数据。直接在arcmap中进行矢量化,生成shp文件,由于本文矢量化后的shp文件是线文件,道路是面,直接进行缓冲区分析构建道路的面文件。
这一步主要是提取原始遥感影像中是道路的像素点,并将这些像素点的像素值置为1。经过掩膜提取后的图像大小和原始图像大小一样,只是将目标区域的rgb值保留为原始图像的的rgb值,而将其他区域的rgb值设置为0,操作方法及掩膜提取结果如下图所示。
从上图中可以看到提取的掩膜的rgb值还是原始遥感影像中的rgb值,我要进行深度学习的标签数据像素值应该是只有0和1(我进行的是二分类,非道路和道路),所以我再进行重分类,将所有为道路像素点的值设置为1,操作如下图。
结果重分类后的标签数据还是arcgis的地理数据库中的栅格数据,不是我们要的png,jpg,tif等格式的数据,这里我们将其导出为png格式的图片,具体操作如下图所示。
如果按掩膜提取的mask图片和原始遥感影像图片的大小一样可以跳过次步骤。这里需要注意的是经过掩膜提取后的mask图片如果和原始遥感影像的大小不一致的话,可以以提取的mask图片建立一个渔网,对原始遥感影像进行裁剪,使提取的mask图片大小和原始遥感影像的大小一致,治理我只对原始遥感影像矢量化了一部分,掩膜提取的也是原始遥感影像中一部分的道路,所以需要建立渔网提取mask图片与原始遥感影像对应的部分。
1)导出的mask图片深度是24位的在Windows上是无法显示的,只会看到一篇漆黑,可进行深度转换,代码如下
# 将图片进行深度转换,24位转8位
# 24位转8位灰度
from PIL import Image
im_r = Image.open(r'E:/image_to_label/orig_image/reclass_road1.png')
im_rr = Image.fromarray(np.uint8(im_r))
print(im_rr.mode)
t = im_rr.convert("L")
print(t.mode)
im_rrr = Image.fromarray(np.uint8(t)*255)
print(im_rrr.mode)
im_rrr.save(r'E:/image_to_label/orig_image/label.png')
2)输出的mask图片和对应的遥感影像图片都很大,需要进行裁剪才能fit给神经网络,裁剪的方法有很多,可以设置步长,进行选装,这里只进行随机裁剪,代码如下(这里我参考的是别人的代码,原本是想进行旋转裁剪,但写的有问题,只能进行随机裁剪了,但也不影响使用咯)
import random
import cv2
import math
import numpy as np
from PIL import Image
import glob
def RandomCut(ImagePath,LabelPath,IamgeSavePath,LabelSavePath,CutSize,CutNum):
# 读取图像的像素矩阵
img = cv2.imread(ImagePath)
# 初始化裁剪图像像素矩阵的大小
imgCut = img[0:CutSize,0:CutSize]
# 读取标签的像素矩阵
label = cv2.imread(LabelPath)
# 初始化裁剪图像像素矩阵的大小
labelCut = label[0:CutSize,0:CutSize]
# 裁剪的循环,当裁剪数量达到预期数量时停止
i = 0
while(i<CutNum):
# 生成剪切图像的左上角XY坐标以及倾斜角度
TopRightX = random.randint(0, img.shape[0])
TopRightY = random.randint(0, img.shape[1])
Angle = random.uniform(0, math.pi/2)
# 设定防止裁剪图像超过原图像的边界的判断条件
if(TopRightY-int(CutSize*math.sin(Angle)) >= 0 and
TopRightX+int((CutSize*math.sqrt(2)*math.sin(Angle+math.pi/4))) < img.shape[0]-1 and
TopRightY+int(CutSize*math.cos(Angle)) < img.shape[1]-1):
try:
for j in range(CutSize):
for k in range(CutSize):
imgCut[j][k] = img[TopRightX+int(math.sin(Angle))+j][TopRightY+int(math.cos(Angle))+k]
labelCut[j][k] = label[TopRightX+int(math.sin(Angle))+j][TopRightY+int(math.cos(Angle))+k]
cv2.imwrite(IamgeSavePath+"/%d.png"%(i+1),imgCut)
cv2.imwrite(LabelSavePath+"/%d.png"%(i+1),labelCut)
print("%d.png is cropped successfully!"%(i+1))
i = i+1
except:
print("Error: TopRightX: ",TopRightX,
" TopRightY: ",TopRightY,
" Angle: ",Angle,
">=0: ",TopRightY-int(CutSize*math.sin(Angle)),
"<3284: ",TopRightX+int((CutSize*math.sqrt(2)*math.sin(Angle+math.pi/4))),
"<3903: ",TopRightY+int(CutSize*math.cos(Angle)))
'''
随机裁剪函数
ImagePath 原始影像路径
LabelPath 标签影像路径
IamgeSavePath 原始影像裁剪后保存目录
LabelSavePath 标签影像裁剪后保存目录
CutSize 裁剪尺寸
CutNum 裁剪数量
'''
裁剪生成训练数据集
RandomCut(r"./orig_data/image.png",
r"./orig_data/label.png",
r"./orig_data/image",
r"./orig_data/mask",
512,30)
3)训练数据集、测试集划分。经过以上步骤后,所有的mask图片和原始遥感影像图都放在mask和image两个文件夹中,现在将mask和image中的标签图像和原始遥感影像分别按照一定比例划分为测试集数据。
import os
import numpy
import random, shutil
import shutil
import glob
# 从image文件夹中移动遥感图像到testimage文件夹
def image_to_testimage(rate):
image_paths=glob.glob(r"./orig_data/image/*.png")
path_list=[]
for image_path in image_paths:
num=image_path[image_path.rindex("\\")+1:-4]
path_list.append(num)
rate=rate
picknumber = int(len(path_list) * rate) # 按照rate比例从文件夹中取一定数量图片
samples = random.sample(path_list, picknumber)#随机划分图片
for name in samples :
old_file=r"./orig_data/image/"+name+".png"
new_dir=r"./orig_data/testimage"
shutil.move(old_file,new_dir)
print("move image {}.png ".format(int(name)))
#从mask文件夹中移动mask图片到testmask文件夹中
def mask_to_testmask():
test_paths=glob.glob(r"./orig_data/testimage/*.png")
for test_path in test_paths:
name=test_path[test_path.rindex("\\")+1:]
shutil.move(r"./orig_data/mask/"+name,r"./orig_data/testmask")
print("move mask "+name)
image_to_testimage(0.1)
mask_to_testmask()
*总体而言制作过程很复杂,因为制作的农村道路宽度太小,在labelme中画面很繁琐,所以才会在arcgis中使用缓冲区分析,如果是要制作遥感影像中城市道路、建筑、水域等的数据集,用labelme应该会更方便。