ソースは、セマンティックセグメンテーションシュウPSPNet「ネットワークテスト」を解決します

入門

この記事では、その後に行ったセマンティックセグメンテーションシュウPSPNetソースコード解析「ネットワークトレーニング」、テストフェーズのセマンティクス部門を紹介し続けています。

トレーニングモデルの完了後、テストのための戦略の種類も非常に重要です。

一般に、単一のスケールに試験モデル単一スケールとマルチスケールマルチスケール、マルチスケールの結果は、単一のスケールよりも概して高いです。また、このような全体画像とその他の詳細は、ネットワークに送信され、又はスライディングウィンドウを使用することであるスライディングウィンドウ、テスト結果に影響を与える図の給電ネットワークの部分は、それぞれ、これらを取ります。以下に記載するコードに基づきます。

完全なコードを参照してくださいhttps://github.com/speedinghzl/pytorch-segmentation-toolbox/blob/master/evaluate.pyを

evaluate.py

メイン

ここでの主な機能テストの最初の半分であり、args.wholeそれは複数のスケールかどうかを示します。

場合args.wholefalseは、呼び出し、単一のスケールを取りpredict_slidingウィンドウをスライディング

場合args.wholetrueは、マルチスケールを取り、呼び出しpredict_multiscaleと渡し[0.75, 1.0, 1.25, 1.5, 1.75, 2.0]、スケーリング係数として予測することは、全体マップ

def main():
    """Create the model and start the evaluation process."""
    args = get_arguments()  #传入参数

    # gpu0 = args.gpu
    os.environ["CUDA_VISIBLE_DEVICES"]=args.gpu
    h, w = map(int, args.input_size.split(',')) #h = 769, w = 769
    if args.whole:
        input_size = (1024, 2048)
    else:
        input_size = (h, w) #(769,769)

    model = Res_Deeplab(num_classes=args.num_classes)   #构造模型
    
    saved_state_dict = torch.load(args.restore_from)    #导入权重
    model.load_state_dict(saved_state_dict) #模型加载权重

    model.eval()    #测试模式
    model.cuda()

    testloader = data.DataLoader(CSDataSet(args.data_dir, args.data_list, crop_size=(1024, 2048), mean=IMG_MEAN, scale=False, mirror=False), 
                                    batch_size=1, shuffle=False, pin_memory=True)

    data_list = []
    confusion_matrix = np.zeros((args.num_classes,args.num_classes))    #构造混淆矩阵 shape(19,19)
    palette = get_palette(256)  #上色板
    interp = nn.Upsample(size=(1024, 2048), mode='bilinear', align_corners=True)    #上采样

    if not os.path.exists('outputs'):
        os.makedirs('outputs')

    for index, batch in enumerate(testloader):
        if index % 100 == 0:
            print('%d processd'%(index))
        image, label, size, name = batch
        #image.shape(1,3,1024,2048)、label.shape(1,1024,2048)、size=[[1024,2048,3]]
        size = size[0].numpy()  #size=[1024,2048,3]
        with torch.no_grad():   #无需梯度回传
            if args.whole:  #若采用整图训练,则调用multiscale方法 output.shape(1024,2048,19)
                output = predict_multiscale(model, image, input_size, [0.75, 1.0, 1.25, 1.5, 1.75, 2.0], args.num_classes, True, args.recurrence)
            else:   #否则采用滑动窗口法
                output = predict_sliding(model, image.numpy(), input_size, args.num_classes, True, args.recurrence)

ここでは、単一のスケールで見ているpredict_slidingダウンし、マルチスケールpredict_wholeおよびpredict_multiscale実装。

predict_sliding

この方法は、出力フィードネットワークを得るために、バックル下部から、各画像を固定サイズのウィンドウを使用することです。スライディングウィンドウ1/3スライド前後重複領域は、部分と重なる確率が重畳されています。重複の総数で割った最終的な確率は、各画素の平均確率を得ます。

#image.shape(1,3,1024,2048)、tile_size=(769,769)、classes=19、flip=True、recur=1
def predict_sliding(net, image, tile_size, classes, flip_evaluation, recurrence):
    interp = nn.Upsample(size=tile_size, mode='bilinear', align_corners=True)   
    image_size = image.shape    #(1,3,1024,2048)
    overlap = 1/3   #每次滑动的重合率为1/3

    stride = ceil(tile_size[0] * (1 - overlap)) #滑动步长:769*(1-1/3) = 513
    tile_rows = int(ceil((image_size[2] - tile_size[0]) / stride) + 1)  #行滑动步数:(1024-769)/513 + 1 = 2
    tile_cols = int(ceil((image_size[3] - tile_size[1]) / stride) + 1)  #列滑动步数:(2048-769)/513 + 1 = 4
    print("Need %i x %i prediction tiles @ stride %i px" % (tile_cols, tile_rows, stride))
    full_probs = np.zeros((image_size[2], image_size[3], classes))  #初始化全概率矩阵 shape(1024,2048,19)
    count_predictions = np.zeros((image_size[2], image_size[3], classes))   #初始化计数矩阵 shape(1024,2048,19)
    tile_counter = 0    #滑动计数0

    for row in range(tile_rows):    # row = 0,1
        for col in range(tile_cols):    # col = 0,1,2,3
            x1 = int(col * stride)  #起始位置x1 = 0 * 513 = 0
            y1 = int(row * stride)  #        y1 = 0 * 513 = 0
            x2 = min(x1 + tile_size[1], image_size[3])  #末位置x2 = min(0+769, 2048) 
            y2 = min(y1 + tile_size[0], image_size[2])  #      y2 = min(0+769, 1024)
            x1 = max(int(x2 - tile_size[1]), 0)  #重新校准起始位置x1 = max(769-769, 0)
            y1 = max(int(y2 - tile_size[0]), 0)  #                y1 = max(769-769, 0)

            img = image[:, :, y1:y2, x1:x2] #滑动窗口对应的图像 imge[:, :, 0:769, 0:769]
            padded_img = pad_image(img, tile_size)  #padding 确保扣下来的图像为769*769
            # plt.imshow(padded_img)
            # plt.show()
            tile_counter += 1   #计数加1
            print("Predicting tile %i" % tile_counter)
            #将扣下来的部分传入网络,网络输出概率图。
            padded_prediction = net(Variable(torch.from_numpy(padded_img), volatile=True).cuda())   #[x, x_dsn]
            if isinstance(padded_prediction, list):
                padded_prediction = padded_prediction[0]    #x.shape(1,19,97,97)
            padded_prediction = interp(padded_prediction).cpu().data[0].numpy().transpose(1,2,0)    #上采样shape(769,769,19)
            prediction = padded_prediction[0:img.shape[2], 0:img.shape[3], :]   #扣下相应面积 shape(769,769,19)
            count_predictions[y1:y2, x1:x2] += 1    #窗口区域内的计数矩阵加1
            full_probs[y1:y2, x1:x2] += prediction  #窗口区域内的全概率矩阵叠加预测结果

    # average the predictions in the overlapping regions
    full_probs /= count_predictions #全概率矩阵 除以 计数矩阵 即得 平均概率
    # visualize normalization Weights
    # plt.imshow(np.mean(count_predictions, axis=2))
    # plt.show()
    return full_probs   #返回整张图的平均概率 shape(1024,2048,19)

predict_multiscale

異なるスケールの関数呼び出しは、フリップの使用、ピクチャを反転は、ネットワーク出力を得る、ネットワークに送信され、次いで、ネットワーク出力フリップオーバーレイ前出力および2で除算するpredict_whole。

#image.shape(1,3,1024,2048)、tile_size=(769,769)、scales=[0.75, 1.0, 1.25, 1.5, 1.75, 2.0]、
#classes=19、flip=True、recur=1
def predict_multiscale(net, image, tile_size, scales, classes, flip_evaluation, recurrence):
    """
    Predict an image by looking at it with different scales.
        We choose the "predict_whole_img" for the image with less than the original input size,
        for the input of larger size, we would choose the cropping method to ensure that GPU memory is enough.
    """
    image = image.data
    N_, C_, H_, W_ = image.shape    #1, 3, 1024, 2048
    full_probs = np.zeros((H_, W_, classes))    #shape(1024, 2048, 19)  
    for scale in scales:    #[0.75, 1.0, 1.25, 1.5, 1.75, 2.0]
        scale = float(scale)    #0.75
        print("Predicting image scaled by %f" % scale)
        #用不同比例对图片进行缩放
        scale_image = ndimage.zoom(image, (1.0, 1.0, scale, scale), order=1, prefilter=False)   #shape(1,3,768,1536)
        scaled_probs = predict_whole(net, scale_image, tile_size, recurrence)   #预测缩放后的整张图像
        if flip_evaluation == True: #若采取翻转
            flip_scaled_probs = predict_whole(net, scale_image[:,:,:,::-1].copy(), tile_size, recurrence)   #翻转后再次预测整张
            scaled_probs = 0.5 * (scaled_probs + flip_scaled_probs[:,::-1,:])   #翻转前后各占50%
        full_probs += scaled_probs  #全概率累加 shape(1024, 2048, 19)
    full_probs /= len(scales)   #求平均概率
    return full_probs   #shape(1024, 2048, 19)

predict_whole

あなたが予測マップ全体を取る場合は、ネットワーク入力(cropsize)で画像サイズが競合することがあります。従ってネットワークの出力は、長さと幅を変えることができる、指定された入力に出力(描画)をサンプリングする必要があります。

#image.shape(1,3,1024,2048)、tile_size=(769,769)
def predict_whole(net, image, tile_size, recurrence):
    image = torch.from_numpy(image)
    interp = nn.Upsample(size=tile_size, mode='bilinear', align_corners=True)   #上采样
    prediction = net(image.cuda())  #[x, x_dsn]
    if isinstance(prediction, list):
        prediction = prediction[0]  #x.shape(1,19,97,193)注意这里跟滑动窗口法不同,输出的h、w并不相等
    prediction = interp(prediction).cpu().data[0].numpy().transpose(1,2,0)  #插值 shape(1024,2048,19)
    return prediction

メイン

得られた動作の完了後にoutput正規化され、チャネルの寸法の最大値をとり、予測結果が得られseg_pred、我々が使用できるputpalette着色カラーセグメンテーション取得機能。

さらに重要なことは、我々が本明細書中で使用されるインデックスMIOUは、分割された計算する必要があり、混同行列のconfusion_matrix方法は、我々は、有効面積をseg_gt除去と1次元ベクトル、入力に引き込まseg_pred get_confusion_matrix機能。

        seg_pred = np.asarray(np.argmax(output, axis=2), dtype=np.uint8)    #对结果进行softmax归一化 shape(1024,2048)
        output_im = PILImage.fromarray(seg_pred)    #将数组转换为图像
        output_im.putpalette(palette)               #给图像上色
        output_im.save('outputs/'+name[0]+'.png')   #保存下来

        seg_gt = np.asarray(label[0].numpy()[:size[0],:size[1]], dtype=np.int)  #取出label shape(1024,2048)
    
        ignore_index = seg_gt != 255    #找到label中的有效区域即不为255的位置,用ignore_index来指示位置
        seg_gt = seg_gt[ignore_index]   #将有效区域取出并转换为1维向量
        seg_pred = seg_pred[ignore_index]   #同上转换为1维向量,位置一一对应
        # show_all(gt, output)
        confusion_matrix += get_confusion_matrix(seg_gt, seg_pred, args.num_classes)    #混淆矩阵加上本张图的预测结果

色予測結果は、1024x2048x1でRGBピクセルの各チャンネルに割り当てられた3つの値、1024x2048x3を得ました。

def get_palette(num_cls):
    """ Returns the color map for visualizing the segmentation mask.
    Args:
        num_cls: Number of classes
    Returns:
        The color map
    """

    n = num_cls
    palette = [0] * (n * 3)
    for j in range(0, n):
        lab = j
        palette[j * 3 + 0] = 0
        palette[j * 3 + 1] = 0
        palette[j * 3 + 2] = 0
        i = 0
        while lab:
            palette[j * 3 + 0] |= (((lab >> 0) & 1) << (7 - i))
            palette[j * 3 + 1] |= (((lab >> 1) & 1) << (7 - i))
            palette[j * 3 + 2] |= (((lab >> 2) & 1) << (7 - i))
            i += 1
            lab >>= 3
    return palette

get_confusion_matrix

初期化混同行列confusion_matrixi及び列j iは偽陽性の数に応じたクラスに属する表す寸法19路盤であり、行の混同行列は、j列目の画素でした。

だから我々は必要gt_labelpred_label混同行列上の各画素の位置を決定します。

我々は、ベクトルを作成するindex = (gt_label * class_num + pred_label)行に、二次元の情報を格納する1次元ベクトルの主要な方法を。

例えば[0] = 1、pred_label [0] = 3持つインデックスは[0] = 1 * 19が= 22 3は、インデックス[0] = 22誤った最初のカテゴリに属して0番目の画素を示し+ gt_label三つのカテゴリーにので、[1]〜[3]のいずれかでカウントconfusion_matrix。

#gt_label、pred_label都为1维向量
def get_confusion_matrix(gt_label, pred_label, class_num):
        """
        Calcute the confusion matrix by given label and pred
        :param gt_label: the ground truth label
        :param pred_label: the pred label
        :param class_num: the nunber of class
        :return: the confusion matrix
        """
        index = (gt_label * class_num + pred_label).astype('int32') #以行优先的方式用一维向量存储二维位置信息
        label_count = np.bincount(index)    #对各种情况进行计数,如第1类被误判为第2类的一共有x个像素点
        confusion_matrix = np.zeros((class_num, class_num)) #初始化混淆矩阵 shape(19,19)

        for i_label in range(class_num):    #0,1,2,...,18
            for i_pred_label in range(class_num):   #0,1,2,...,18
                cur_index = i_label * class_num + i_pred_label  #0*18+0, 0*18+1, ..., 18*18+18 每一次对应一种判断情况
                if cur_index < len(label_count):
                    confusion_matrix[i_label, i_pred_label] = label_count[cur_index]    #矩阵放入对应判断情况的次数

        return confusion_matrix

メイン

次のように評価mIoUセマンティックセグメンテーションが計算されます。

MIoU = \ FRAC {1} {K + 1} \ sum_ {i = 0} ^ {K} {\ FRAC {P_ {II}} {\ sum_ {J = 0} ^ {K} {P_ {IJ}} + \ sum_ {J = 0} ^ {K} {P_ {JI}  -  P_ {II}}}

その後、各クラスのために計算されIOUを平均しました。例えば、I = 1、として計算クラスIOU \(P_ {11} \)真陽性を示し、すなわち、これはクラス1であり、予測はクラス1であり、\(\ SUM ^ K_ {J = 0} P_ { 1J} \)これはクラス1の画素の他のクラスの番号と予測しているであることを示している(注、ここに含まれる\(P_ {11} \) )、\ (\ SUM ^ K_ {J = 0} P_ {J1} \)これは別のクラスに属するクラスが、1の予測のためのピクセルの数を表す(これも含むことに注意してください\(P_ {11} \)分母に、)\(P_ {11} \)のように、二回減算することによって計算されます\(P_ {11} \)

対角線上の定義既知混同行列要素である\(P_ {IIは} \)、i番目の行の合計である(\ \ J = SUM {^ K_ {P_ 0のIJ}})\ 用i番目の列加算される\(\ ^ SUM K_ {J}は0 {P_ JI} = \) 次いでmIoU混同行列を計算することにより、コードを見る、非常に簡単です。

pos = confusion_matrix.sum(1)   #混淆矩阵对行求和
    res = confusion_matrix.sum(0)   #混淆矩阵对列求和
    tp = np.diag(confusion_matrix)  #取出对角元素,即正确判断的次数

    IU_array = (tp / np.maximum(1.0, pos + res - tp))   #每一类的IoU = ∩/∪ shape(,19)
    mean_IU = IU_array.mean()   #对类取平均
    
    # getConfusionMatrixPlot(confusion_matrix)
    print({'meanIU':mean_IU, 'IU_array':IU_array})
    with open('result.txt', 'w') as f:
        f.write(json.dumps({'meanIU':mean_IU, 'IU_array':IU_array.tolist()}))

おすすめ

転載: www.cnblogs.com/vincent1997/p/10939747.html