当前位置:   article > 正文

PyTorch 11—简单图像定位_pytorch图像坐标与点云坐标对齐

pytorch图像坐标与点云坐标对齐

常见图像处理的任务

(1)分类

给定一幅图像,我们用计算机模型预测图片中有什么对象 。

(2)分类加定位

不仅需要我们知道图片中的对象是什么,还要在对象的附近画一个边框,确定该对象所处的位置。

(3)语义分割

区分到图中每一个像素点,而不仅仅是矩形框框住。

(4)目标检测 

目标检测简单来说就是回答图片里面有什么?分别在哪里?(把它们用矩形框框住)。

(5)实例分割 

实例分割是目标检测和语义分割的结合。相对目标检测的边界框,实例分割可精确到物体的边缘;相对语义分割,实例分割需要标注出图上同一物体的不同个体

 图像定位

对于单纯的分类问题,比较容易理解,给定一幅图片,我们输出一个标签类别,我们已经跟熟悉。

而定位有点复杂,需要输出四个数字(x,y,w,h),图像中某一个点的坐标(x,y),以及图像的宽度和高度,有了这四个数字,我们可以很容易地找到物体的边框。

简单定位网络架构

本质是回归问题,使用L2损失进行优化。

Oxford-IIIT数据集

The Oxford-IIIT Pet Dataset是一个宠物图像数据集,包含37种宠物,每种宠物200张左右宠物图片,该数据集同时包含宠物分类、头部轮廓标注和语义分割信息

代码实战

  1. import torch
  2. import torch.nn as nn
  3. import torch.nn.functional as F
  4. from torch.utils import data
  5. import numpy as np
  6. import matplotlib.pyplot as plt
  7. import torchvision
  8. from torchvision import transforms
  9. import os
  10. from lxml import etree # etree网页解析模块
  11. from matplotlib.patches import Rectangle # Rectangle画矩形
  12. import glob # 获取所有路径
  13. from PIL import Image # 读取图像

单张图片演示

  1. BATCH_SIZE = 8
  2. print('一、图片解析演示')
  3. pil_img = Image.open(r'dataset/images/Abyssinian_1.jpg')
  4. np_img = np.array(pil_img) # 转换成ndarray形式
  5. print(np_img.shape)
  6. plt.imshow(np_img)
  7. plt.show()
  8. # 打开对应的xml图像定位信息文件
  9. xml = open(r'dataset/annotations/xmls/Abyssinian_1.xml').read()
  10. # 使用Etree库解析xml文件
  11. sel = etree.HTML(xml) # 首先创建一个解析对象
  12. width = sel.xpath('//size/width/text()')[0] # 使用xpath方法获取它的位置。
  13. # //表示从根目录查找,width/text()表示查找width标签里的文本,返回对象是一个列表,所以要切片处理
  14. height = sel.xpath('//size/height/text()')[0]
  15. print(width,' ',height)
  16. # 查找头部所在的像素位置
  17. xmin = sel.xpath('//bndbox/xmin/text()')[0]
  18. ymin = sel.xpath('//bndbox/ymin/text()')[0]
  19. xmax = sel.xpath('//bndbox/xmax/text()')[0]
  20. ymax = sel.xpath('//bndbox/ymax/text()')[0]
  21. # 将获取到的文本转换成整数。
  22. #转换数据类型,因为matplotlib只能接受整形标注它的位置
  23. width = int(width)
  24. height = int(height)
  25. xmin = int(xmin)
  26. ymin = int(ymin)
  27. xmax = int(xmax)
  28. ymax = int(ymax)
  29. plt.imshow(np_img)
  30. rect = Rectangle((xmin, ymin), (xmax-xmin), (ymax-ymin), fill=False, color='blue') # 实例化Rectangle对象,需要标注它的一些位置
  31. # 参数1是xy,即最小值所在的点;第二、三个参数是width、height;fill=False表示不需要填充矩形。
  32. ax = plt.gca() # 获取当前坐标系
  33. ax.axes.add_patch(rect) # 在当前坐标系上添加矩形框。
  34. plt.show()


在原始数据集中,各张图片大小不一,我们在输入模型时,想要把它变成固定的大小。但是,图像改变尺寸后,对应的xmin和ymin就不对了,因为xmin和ymin是相对于原先图片的大小。实际上,我们可以将它转换为一个比值就可以了

  1. # 例如:
  2. img = pil_img.resize((224,224))
  3. xmin = (xmin/width)*224
  4. ymin = (ymin/height)*224
  5. xmax = (xmax/width)*224
  6. ymax = (ymax/height)*224
  7. plt.imshow(img)
  8. rect = Rectangle((xmin, ymin), (xmax-xmin), (ymax-ymin), fill=False, color='red')
  9. ax = plt.gca() # 获取当前坐标系
  10. ax.axes.add_patch(rect) # 在当前坐标系上添加矩形框。
  11. plt.show()

 输出是比值,使用比值作为目标值。


创建输入

  1. images = glob.glob('dataset/images/*.jpg') # 返回类型是列表。返回在images目录下所有以 jpg 结尾的文件的路径
  2. xmls = glob.glob('dataset/annotations/xmls/*.xml')
  3. len(images) #7390
  4. len(xmls) # 3686
  5. # 这说明了数据集并没有对全部的图片进行标注
  6. """
  7. 我们不知道对哪些图片做了标注,为了取出标注的图片;
  8. 我们要将这些被标注数据的文件名 ;
  9. 也即'dataset/annotations/xmls\\Abyssinian_1.xml'中的Abyssinian_1取出来;
  10. 然后使用文件名对原有的图片进行一个筛选。
  11. """
  12. xmls_names = [x.split('\\')[-1].split('.xml')[0] for x in xmls] # 获取到了所有被标注图片的文件名
  13. # xmls_names = [x.split('\\')[-1].replace('.xml','') for x in xmls] 这种办法和上面的效果一样
  14. len(xmls_names) # 3686
  15. # 根据标注图片的文件名对所有图片进行一个筛选
  16. imgs = [img for img in images if
  17. img.split('\\')[-1].split('.jpg')[0] in xmls_names]
  18. len(imgs) #3686
  19. # 在创建输入之前,要保证图片和标注信息是一一对应的
  20. print('len(imgs)==len(xmls_names)?:',len(imgs)==len(xmls_names))
  21. print('imgs[:5]:\n',imgs[:5])
  22. print('xmls[:5]:\n',xmls[:5])

将xml文件转换成标签的格式:下面我们需要将xml文件给它转换成标签的形式,在转换之前,我们首先要明确一点,我们的目标值不再是xmin这个实际的值,因为每一张图片的大小都是不一的,这个时候我们只是取出它的一个比例值。我们的预测值是头部宽高度所占的比值。

  1. # 为了将xml列表文件里的数值解析出来,我们专门定义一个toLabel函数
  2. def to_labels(path):
  3. xml = open(r'{}'.format(path)).read() # 用格式化形式,加r,防止转义。
  4. sel = etree.HTML(xml) # 创建选择器
  5. width = int(sel.xpath('//size/width/text()')[0])
  6. height = int(sel.xpath('//size/height/text()')[0])
  7. xmin = int(sel.xpath('//bndbox/xmin/text()')[0])
  8. ymin = int(sel.xpath('//bndbox/ymin/text()')[0])
  9. xmax = int(sel.xpath('//bndbox/xmax/text()')[0])
  10. ymax = int(sel.xpath('//bndbox/ymax/text()')[0])
  11. return [xmin/width, ymin/height, xmax/width, ymax/height]
  1. labels = [to_labels(path) for path in xmls]
  2. labels[0],type(labels)

  1. # zip() 函数用于将可迭代的对象作为参数,将对象中对应的元素打包成一个个元组,然后返回由这些元组组成的列表。
  2. out1_label, out2_label, out3_label, out4_label = list(zip(*labels))
  3. len(out1_label), len(out2_label), len(out3_label), len(out4_label)

 


 划分数据集

  1. index = np.random.permutation(len(imgs))
  2. # img和label都是一一对应的,但是顺序乱序了。
  3. images = np.array(imgs)[index]
  4. labels = np.array(labels)[index]
  5. out1_label = np.array(out1_label).astype(np.float32).reshape(-1, 1)[index]
  6. out2_label = np.array(out2_label).astype(np.float32).reshape(-1, 1)[index]
  7. out3_label = np.array(out3_label).astype(np.float32).reshape(-1, 1)[index]
  8. out4_label = np.array(out4_label).astype(np.float32).reshape(-1, 1)[index]
  9. labels = labels.astype(np.float32) # 由于之前做了除法,最好转换成浮点型数据,这样有利于模型不报错
  10. labels.shape # (3686,4)
  11. """
  12. out1_label = out1_label.astype(np.float32)
  13. out2_label = out2_label.astype(np.float32)
  14. out3_label = out3_label.astype(np.float32)
  15. out4_label = out4_label.astype(np.float32)
  16. """
  17. i = int(len(imgs)*0.8) # 训练集比例
  18. train_images = images[:i]
  19. train_labels = labels[:i]
  20. out1_train_label = out1_label[:i]
  21. out2_train_label = out2_label[:i]
  22. out3_train_label = out3_label[:i]
  23. out4_train_label = out4_label[:i]
  24. test_images = images[i:]
  25. test_labels = labels[i:]
  26. out1_test_label = out1_label[i: ]
  27. out2_test_label = out2_label[i: ]
  28. out3_test_label = out3_label[i: ]
  29. out4_test_label = out4_label[i: ]

创建输入模型 

 

  1. transform = transforms.Compose([
  2. transforms.Resize((224, 224)),
  3. transforms.ToTensor(),
  4. ])
  5. # 创建一个DataSet类
  6. class Oxford_dataset(data.Dataset):
  7. def __init__(self, img_paths, out1_label, out2_label,
  8. out3_label, out4_label, transform):
  9. self.imgs = img_paths
  10. self.out1_label = out1_label
  11. self.out2_label = out2_label
  12. self.out3_label = out3_label
  13. self.out4_label = out4_label
  14. self.transforms = transform
  15. def __getitem__(self, index):
  16. img = self.imgs[index] # 切出来是一条路径
  17. out1_label = self.out1_label[index]
  18. out2_label = self.out2_label[index]
  19. out3_label = self.out3_label[index]
  20. out4_label = self.out4_label[index]
  21. pil_img = Image.open(img)
  22. imgs_data = np.asarray(pil_img, dtype=np.uint8)
  23. if len(imgs_data.shape) == 2: # 如果不是rgb图像,就多增加一个维度
  24. imgs_data = np.repeat(imgs_data[:, :, np.newaxis], 3, axis=2)
  25. img_tensor = self.transforms(Image.fromarray(imgs_data))
  26. else:
  27. img_tensor = self.transforms(pil_img)
  28. return (img_tensor,
  29. out1_label,
  30. out2_label,
  31. out3_label,
  32. out4_label)
  33. def __len__(self):
  34. return len(self.imgs)
  35. train_dataset = Oxford_dataset(train_images, out1_train_label,
  36. out2_train_label, out3_train_label,
  37. out4_train_label, transform)
  38. test_dataset = Oxford_dataset(test_images, out1_test_label,
  39. out2_test_label, out3_test_label,
  40. out4_test_label, transform)
  41. train_dl = data.DataLoader(
  42. train_dataset,
  43. batch_size=BATCH_SIZE,
  44. shuffle=True,
  45. )
  46. test_dl = data.DataLoader(
  47. test_dataset,
  48. batch_size=BATCH_SIZE,
  49. )
  50. (imgs_batch,
  51. out1_batch,
  52. out2_batch,
  53. out3_batch,
  54. out4_batch) = next(iter(train_dl))
  55. imgs_batch.shape, out1_batch.shape

 

  1. plt.figure(figsize=(12, 8))
  2. for i,(img, label1, label2,
  3. label3,label4,) in enumerate(zip(imgs_batch[:2],
  4. out1_batch[:2],
  5. out2_batch[:2],
  6. out3_batch[:2],
  7. out4_batch[:2])):
  8. img = (img.permute(1,2,0).numpy() + 1)/2 # permute交换维度。
  9. plt.subplot(2, 3, i+1)
  10. plt.imshow(img)
  11. xmin, ymin, xmax, ymax = label1*224, label2*224, label3*224, label4*224,
  12. rect = Rectangle((xmin, ymin), (xmax-xmin), (ymax-ymin), fill=False, color='red')
  13. ax = plt.gca()
  14. ax.axes.add_patch(rect)


创建定位模型 

  1. resnet = torchvision.models.resnet101(pretrained=True) # 使用卷积基提取特征。使用预训练参数作为初始化参数
  2. """
  3. resnet101里面包含很多层,conv、batch.........
  4. 最后是avgpool和fc全连接层。
  5. avgpool之前的层都是我们需要的
  6. """
  7. in_f = resnet.fc.in_features # 全连接层的输入
  8. print(in_f) #2048
  9. resnet.children() # 会返回所有层的生成器
  10. list(resnet.children()) # 使用list将它返回
  11. print(len(list(resnet.children()))) # 一共包含10个子层
  12. print(list(resnet.children())[-1]) # 看看最后一层
  13. list(resnet.children())[:-1] # 这些层才是我们需要的,帮助我们提取特征。
  14. conv_base = nn.Sequential(*list(resnet.children())) # *代表解包。这样相当于每一层都列在了Sequential里面
  15. class Net(nn.Module):
  16. def __init__(self):
  17. super(Net, self).__init__() # 继承父类属性
  18. self.conv_base = nn.Sequential(*list(resnet.children())[:-1])
  19. # 使用全连接模型。分别输出4个坐标值
  20. self.fc1 = nn.Linear(in_f, 1) # 输出是1,因为我们要输出一个标量值。
  21. self.fc2 = nn.Linear(in_f, 1)
  22. self.fc3 = nn.Linear(in_f, 1)
  23. self.fc4 = nn.Linear(in_f, 1)
  24. def forward(self, x):
  25. x = self.conv_base(x) # 提取特征
  26. x = x.view(x.size(0), -1)
  27. x1 = self.fc1(x)
  28. x2 = self.fc2(x)
  29. x3 = self.fc3(x)
  30. x4 = self.fc4(x)
  31. return x1, x2, x3, x4
  32. model = Net()
  33. if torch.cuda.is_available():
  34. model.to('cuda')
  35. loss_fn = nn.MSELoss() # 回归问题,并不是分类问题。误差函数取平均绝对误差,MSE损失函数
  36. from torch.optim import lr_scheduler
  37. optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
  38. exp_lr_scheduler = lr_scheduler.StepLR(optimizer, step_size=7, gamma=0.1) #每次间隔7步,学习速率衰减0.1
  39. def fit(epoch, model, trainloader, testloader):
  40. total = 0
  41. running_loss = 0
  42. model.train()
  43. for x, y1, y2, y3, y4 in trainloader:
  44. if torch.cuda.is_available():
  45. x, y1, y2, y3, y4 = (x.to('cuda'),
  46. y1.to('cuda'), y2.to('cuda'),
  47. y3.to('cuda'), y4.to('cuda'))
  48. y_pred1, y_pred2, y_pred3, y_pred4 = model(x)
  49. loss1 = loss_fn(y_pred1, y1)
  50. loss2 = loss_fn(y_pred2, y2)
  51. loss3 = loss_fn(y_pred3, y3)
  52. loss4 = loss_fn(y_pred4, y4)
  53. loss = loss1 + loss2 + loss3 + loss4
  54. optimizer.zero_grad()
  55. loss.backward()
  56. optimizer.step()
  57. with torch.no_grad():
  58. running_loss += loss.item()
  59. exp_lr_scheduler.step()
  60. epoch_loss = running_loss / len(trainloader.dataset)
  61. test_total = 0
  62. test_running_loss = 0
  63. model.eval()
  64. with torch.no_grad():
  65. for x, y1, y2, y3, y4 in testloader:
  66. if torch.cuda.is_available():
  67. x, y1, y2, y3, y4 = (x.to('cuda'),
  68. y1.to('cuda'), y2.to('cuda'),
  69. y3.to('cuda'), y4.to('cuda'))
  70. y_pred1, y_pred2, y_pred3, y_pred4 = model(x)
  71. loss1 = loss_fn(y_pred1, y1)
  72. loss2 = loss_fn(y_pred2, y2)
  73. loss3 = loss_fn(y_pred3, y3)
  74. loss4 = loss_fn(y_pred4, y4)
  75. loss = loss1 + loss2 + loss3 + loss4
  76. test_running_loss += loss.item()
  77. epoch_test_loss = test_running_loss / len(testloader.dataset)
  78. print('epoch: ', epoch,
  79. 'loss: ', round(epoch_loss, 3),
  80. 'test_loss: ', round(epoch_test_loss, 3),
  81. )
  82. return epoch_loss, epoch_test_loss

开始训练

  1. epochs = 10 # 总共训练十次
  2. train_loss = []
  3. test_loss = []
  4. for epoch in range(epochs):
  5. epoch_loss, epoch_test_loss = fit(epoch, model, train_dl, test_dl)
  6. train_loss.append(epoch_loss)
  7. test_loss.append(epoch_test_loss)
  8. plt.figure()
  9. plt.plot(range(1, len(train_loss)+1), train_loss, 'r', label='Training loss')
  10. plt.plot(range(1, len(train_loss)+1), test_loss, 'bo', label='Validation loss')
  11. plt.title('Training and Validation Loss')
  12. plt.xlabel('Epoch')
  13. plt.ylabel('Loss Value')
  14. plt.legend()
  15. plt.show()


模型保存 

  1. PATH = 'location_model.pth'
  2. torch.save(model.state_dict(), PATH) # 保存的是权重,可训练参数。
  3. plt.figure(figsize=(8, 24))
  4. imgs, _, _, _, _ = next(iter(test_dl)) # _占位符。意思是我不需要你实际的位置,我们要自己去预测。
  5. imgs = imgs.to('cuda') # 将图片添加到显卡
  6. out1, out2, out3, out4 = model(imgs) # 进行预测,返回四个坐标值(头部位置)
  7. for i in range(6):
  8. plt.subplot(6, 1, i+1)
  9. plt.imshow(imgs[i].permute(1,2,0).cpu().numpy()) # 放到cpu上
  10. xmin, ymin, xmax, ymax = (out1[i].item()*224, # out[i]代表第i个batch的第一个位置
  11. out2[i].item()*224,
  12. out3[i].item()*224,
  13. out4[i].item()*224)
  14. rect = Rectangle((xmin, ymin), (xmax-xmin), (ymax-ymin), fill=False, color='red')
  15. ax = plt.gca()
  16. ax.axes.add_patch(rect)
  17. plt.show()

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/凡人多烦事01/article/detail/122775
推荐阅读
相关标签
  

闽ICP备14008679号