语义分割技术介绍
语义分割技术是一种将图像中的每个像素分配到特定类别的计算机视觉方法,其核心目标是实现对图像内容的精细化理解。这项技术通过为画面中的每个点打上语义标签,能够精确划分出不同物体的边界和区域,与传统目标检测相比,语义分割的优势在于它能提供像素级的识别精度,不仅判断物体是否存在,还能清晰勾勒出物体的具体形状和位置。
语义分割用于调制信号处理
由于时频图与自然图像在结构特征上的相似性。时频图通过时频分析工具(如短时傅里叶变换或小波变换)将一维信号转换为二维图像表示,其中不同调制方式(如FSK、QPSK)会形成具有独特纹理和能量分布的区域模式,这与语义分割擅长处理的视觉模式高度吻合。通过训练深度神经网络对时频图像素进行端到端分类,可以实现调制类型的区域级识别,同时精确分离混合信号中相互交叠的调制成分。
- 预想中通过语义分割处理信号时频图流程如下:
调制信号经过傅里叶变换或连续小波变换后可转换为时频图,利用语义分割方法,可以将时频图中的不同调制模式区域进行精准标注与良好的可视化,并有效分离噪声与有用信号,进而提高调制方式分类的准确性和鲁棒性。
数据集准备与预处理
实验中使用原始信号数据使用MATLAB进行模拟,并进行傅里叶变换与灰度标注,最后使用python将灰度标注转换为色彩标注图片,制作用于语义分割的调制信号时频图数据集。用于灰度图转换彩色标注图片的部分代码如下:
代码位置:Signal_Segmentation/datasets/lable_to_color.py
def convert_gray_to_color(image_path, save_path):
# 打开灰度图像
image = Image.open(image_path).convert('L')
image = np.array(image)
# 创建彩色图像
color_image = np.zeros((*image.shape, 3), dtype=np.uint8)
# 遍历灰度值并应用颜色映射
for gray_val, color in COLORMAP.items():
color_image[image == gray_val] = color
# 保存彩色图像
color_image = Image.fromarray(color_image)
color_image.save(save_path)
print(f"Converted {image_path} to color map and saved as {save_path}")
网络训练过程
损失函数设置
代码位置:Signal_Segmentation/utils/criterion.py
分割实验过程中使用的损失函数为语义分割中常用的交叉熵损失函数
class CrossEntropy(nn.Module):
def __init__(self, ignore_label=-1, weight=None):
super(CrossEntropy, self).__init__()
self.ignore_label = ignore_label
self.criterion = nn.CrossEntropyLoss(
weight=weight,
ignore_index=ignore_label
)
def _forward(self, score, target):
loss = self.criterion(score, target)
return loss
def forward(self, score, target):
if config.MODEL.NUM_OUTPUTS == 1:
score = [score]
balance_weights = config.LOSS.BALANCE_WEIGHTS
sb_weights = config.LOSS.SB_WEIGHTS
if len(balance_weights) == len(score):
return sum([w * self._forward(x, target) for (w, x) in zip(balance_weights, score)])
elif len(score) == 1:
return sb_weights * self._forward(score[0], target)
else:
raise ValueError("lengths of prediction and target are not identical!")
训练过程
训练在GPU上完成,设置实验轮次100,没过一定轮次进行一次网络平均交并比计算,保留结果最好的网络参数
代码位置:Signal_Segmentation/utils/function.py
# 部分代码
def train(config, epoch, num_epoch, epoch_iters, base_lr,
num_iters, trainloader, optimizer, model, writer_dict):
# Training
model.train()
batch_time = AverageMeter()
ave_loss = AverageMeter()
ave_acc = AverageMeter()
avg_sem_loss = AverageMeter()
avg_bce_loss = AverageMeter()
tic = time.time()
cur_iters = epoch*epoch_iters
writer = writer_dict['writer']
global_steps = writer_dict['train_global_steps']
for i_iter, batch in enumerate(trainloader, 0):
images, labels, bd_gts, _, _ = batch
images = images.cuda()
labels = labels.long().cuda()
losses, _, acc, loss_list = model(images, labels, bd_gts)
loss = losses.mean()
acc = acc.mean()
model.zero_grad()
loss.backward()
optimizer.step()
# measure elapsed time
batch_time.update(time.time() - tic)
tic = time.time()
# update average loss
ave_loss.update(loss.item())
ave_acc.update(acc.item())
avg_sem_loss.update(loss_list[0].mean().item())
avg_bce_loss.update(loss_list[1].mean().item())
lr = adjust_learning_rate(optimizer,
base_lr,
num_iters,
i_iter+cur_iters)
if i_iter % config.PRINT_FREQ == 0:
msg = 'Epoch: [{}/{}] Iter:[{}/{}], Time: {:.2f}, ' \
'lr: {}, Loss: {:.6f}, Acc:{:.6f}, Semantic loss: {:.6f}, BCE loss: {:.6f}' .format(
epoch, num_epoch, i_iter, epoch_iters,
batch_time.average(), [x['lr'] for x in optimizer.param_groups], ave_loss.average(),
ave_acc.average(), avg_sem_loss.average(), avg_bce_loss.average())
logging.info(msg)
writer.add_scalar('train_loss', ave_loss.average(), global_steps)
writer_dict['train_global_steps'] = global_steps + 1
结果表征
平均交并比
- 交并比介绍
在语义分割中,平均交并比(Mean Intersection over Union,mIoU)是衡量模型性能的重要指标,尤其在图像分割任务中广泛使用。mIoU用于评估分割结果与真实标签之间的重叠程度,是通过计算各类别的交并比(IoU)值的均值来得出的。IoU是指预测区域与真实区域的交集与并集的比率,具体而言,它量化了模型预测的区域与真实标签区域之间的相似度。
计算IoU时,首先需要对每一类(通常为像素分类)计算交集与并集。交集是指模型预测与真实标签在某一类别下重叠的区域,并集则是模型预测区域与真实标签区域的总和。对于每个类别,IoU的计算公式如下:
I
o
U
i
=
∣
A
i
∩
B
i
∣
∣
A
i
∪
B
i
∣
IoU_i = \frac{|A_i \cap B_i|}{|A_i \cup B_i|}
IoUi=∣Ai∪Bi∣∣Ai∩Bi∣
其中,
A
i
A_i
Ai和
B
i
B_i
Bi分别代表第
i
i
i类的预测区域和真实标签区域,∩ 表示交集,∪ 表示并集,∣⋅∣ 表示集合的元素个数。
平均交并比(mIoU)是对所有类别的IoU值取平均,公式如下:
m
I
o
U
=
1
C
∑
i
=
1
C
I
o
U
i
mIoU = \frac{1}{C} \sum_{i=1}^{C} IoU_i
mIoU=C1∑i=1CIoUi
- 实验代码输出示例:
2024-11-30 20:43:17,350 => saving checkpoint to output/signal/signalcheckpoint_1_-5.pth.tar
2024-11-30 20:43:17,573 Loss: 0.613, MeanIU: 0.7219, Best_mIoU: 0.7219
2024-11-30 20:43:17,574 [0.99014089 0.67749995 0.80880212 0.87500909 0.27385732 0.70583797]
结果可视化
使用代码进行分割结果的可视化,根据分割结果与类别的颜色对应关系,将网络输出结果转为彩色分割结果图片,并将得到的标注图叠加到原始图像中
代码位置:Signal_Segmentation/tools/view_signal2.py
# 部分代码
# 存储不同类别的分割结果
for i, color in enumerate(color_map):
mask = (pred == i).astype(np.uint8) * 255
category_img = np.zeros_like(original_img).astype(np.uint8)
for j in range(3):
category_img[:, :, j] = mask * (color[j] / 255.0)
category_img = Image.fromarray(category_img)
category_path = os.path.join(sv_path, f"category_{i}")
os.makedirs(category_path, exist_ok=True)
category_img.save(os.path.join(category_path, img_name))
# 叠加分割结果到原始图像
seg_img = np.zeros_like(original_img, dtype=np.uint8)
for i, color in enumerate(color_map):
seg_img[pred == i] = color
#调整结果中原始图片与分割图片的颜色比例
overlay = cv2.addWeighted(original_img, 0.2, seg_img, 0.8, 0)
overlay_img = Image.fromarray(overlay)
os.makedirs(sv_path, exist_ok=True)
overlay_img.save(os.path.join(sv_path, img_name))
- 结果示例(展示结果进行混合信号视频图分割并将分割结果叠加到原始图像中)
总结
对比传统的黑盒预测,基于图分割方法的识别展现出良好的可解释性与可视化能力,分割同时实现了混合信号不同信号组分的识别与定位分离。使用时频图的分割过程对多信号的互相干扰具有良好的鲁棒性。
github项目完整代码地址
https://github.com/moser12138/Signal_Segmentation.git