3
\$\begingroup\$

Objective : Calculate True Positive, False Positive, True Negative and False negative and colourize the image accordignly, based on ground-truth and prediction from my classifier model.

Problem : Very Slow

Description:

The prediction is a gray-level image that comes from my classifier.

The truth_image is also a gray-level image, but its the correct image that prediction should try to approximate.

The mask_image segmentates the ROI ( region of interest), when its pixel value is 1, means its inside the region of interest, if its 0, then we should ignore and move on to the next pixel.

The output is the array where I want to keep the colourized image.

def coloured_prediction_truth(prediction, truth_image, mask_image,output_image):
    prediction = prediction.astype(np.uint8)
    truth_image = truth_image.astype(np.uint8)
    #output_image = np.empty(shape=(submission_image.shape[0], submission_image.shape[1], 4), dtype=np.uint8)

    #binarize values , values > 127 becomes 1. Else -> 0
    for i in xrange(0, prediction.shape[0]):
        for j in xrange(0, prediction.shape[1]):
            if prediction[i, j] > 127:
                prediction[i, j] = 1
            else:
                prediction[i, j] = 0

    for i in xrange(0, truth_image.shape[0]):
        for j in xrange(0, truth_image.shape[1]):
            if truth_image[i, j] > 127:
                truth_image[i, j] = 1
            else:
                truth_image[i, j] = 0

    for i in xrange(0, mask_image.shape[0]):
        for j in xrange(0, mask_image.shape[1]):
            if mask_image[i, j] > 127:
                mask_image[i, j] = 1
            else:
                mask_image[i, j] = 0



    # B-G-R-A
    blue = [255, 0, 0, 255]
    green = [0, 255, 0, 255]
    red = [0, 0, 255, 255]
    orange = [0, 128, 255, 255]
    black = [0, 0, 0, 255]

    TruePositive = 0.00
    TrueNegative = 0.00
    FalsePositive = 0.00
    FalseNegative = 0.00

    #Count pixel by pixel
    for i in xrange(0, output_image.shape[0]):
        for j in xrange(0, output_image.shape[1]):

            pred = prediction[i, j]
            truth = truth_image[i, j]
            mask = mask_image[i, j]

            if mask == 1:

                if pred == 1 and truth == 1:
                    output_image[i, j] = blue
                    TruePositive = TruePositive + 1
                else:
                    if pred == 0 and truth == 0:
                        output_image[i, j] = green
                        TrueNegative = TrueNegative + 1
                    else:
                        if pred == 0 and truth == 1:
                            output_image[i, j] = red
                            FalseNegative = FalseNegative + 1
                        else:
                            if pred == 1 and truth == 0:
                                output_image[i, j] = orange
                                FalsePositive = FalsePositive + 1
                            else:
                                output_image[i, j] = black
            else:
                output_image[i, j] = black

    accuracy = float((TruePositive + TrueNegative)) / float((TruePositive + FalsePositive + FalseNegative + TrueNegative))
    sensitivity = float((TruePositive)) / float((TruePositive + FalseNegative))
    specificity = float((TrueNegative)) / float((TrueNegative + FalsePositive))
    try:
        positivePredictiveValue = float((TruePositive)) / float((TruePositive + FalsePositive))
    except Exception:
        positivePredictiveValue = 0

    return output_image, TruePositive, TrueNegative, FalsePositive, FalseNegative, accuracy, sensitivity, specificity, positivePredictiveValue

Example :

Prediction, Mask, Ground_truth (truth_image) respectively

From these 3 images, the coloured image, would be:

enter image description here

\$\endgroup\$

2 Answers 2

2
\$\begingroup\$

Don't use manual iteration. It's slow and tedious. Use broadcasting instead.

For instance, you can create a true positives mask like this:

true_positive_mask = np.logical_and(prediction > 127, truth_image > 127)

After that, you can use this mask to index the output image:

output_image[true_positive_mask] = green # or whatever color you want

and to compute the total number of true positives:

true_positives = np.sum(true_positive_mask)

You can use the same idea to compute other values or for input binarization.

\$\endgroup\$
3
  • \$\begingroup\$ Is this faster? \$\endgroup\$
    – user108668
    Oct 14, 2017 at 17:04
  • 1
    \$\begingroup\$ @KenobiShan Yes. \$\endgroup\$
    – kraskevich
    Oct 14, 2017 at 17:18
  • 1
    \$\begingroup\$ Thank you so much! I made a little test and it saved me from 6sec for 1 image, to 0.06 \$\endgroup\$
    – user108668
    Oct 14, 2017 at 23:23
0
\$\begingroup\$

Thanks to @kraskevich, I came up with the following code, which Im posting here in case anyone needs it.

def coloured_prediction_truth_2(prediction, truth_image, submission_image, mask_image):
    prediction = prediction.astype(np.uint8)
    truth_image = truth_image.astype(np.uint8)
    mask_image = mask_image.astype(np.uint8)

    output_image = np.empty(shape=(submission_image.shape[0], submission_image.shape[1], 4), dtype=np.uint8)



    true_positive_mask = np.logical_and(prediction > 127, truth_image > 127)
    true_positive_mask = np.logical_and(true_positive_mask,mask_image > 127)


    true_negative_mask = np.logical_and(prediction <= 127, truth_image <= 127)
    true_negative_mask = np.logical_and(true_negative_mask, mask_image > 127)

    false_negative_mask = np.logical_and(prediction <= 127, truth_image > 127)
    false_negative_mask = np.logical_and(false_negative_mask, mask_image > 127)

    false_positive_mask = np.logical_and(prediction > 127, truth_image <= 127)
    false_positive_mask = np.logical_and(false_positive_mask, mask_image > 127)

    background_mask = np.logical_not(mask_image > 127)


    # B-G-R-A
    blue = [255, 0, 0, 255]
    green = [0, 255, 0, 255]
    red = [0, 0, 255, 255]
    orange = [0, 128, 255, 255]
    black = [0, 0, 0, 255]

    output_image[background_mask] = black  # or whatever color you want
    output_image[true_positive_mask] = blue  # or whatever color you want
    output_image[true_negative_mask] = green  # or whatever color you want
    output_image[false_positive_mask] = orange  # or whatever color you want
    output_image[false_negative_mask] = red  # or whatever color you want



    TruePositive = np.sum(true_positive_mask)
    TrueNegative = np.sum(true_negative_mask)

    FalseNegative = np.sum(false_negative_mask)
    FalsePositive = np.sum(false_positive_mask)

    accuracy = float((TruePositive + TrueNegative)) / float((TruePositive + FalsePositive + FalseNegative + TrueNegative))
    sensitivity = float((TruePositive)) / float((TruePositive + FalseNegative))
    specificity = float((TrueNegative)) / float((TrueNegative + FalsePositive))

    try:
        positivePredictiveValue = float((TruePositive)) / float((TruePositive + FalsePositive))
    except Exception:
        positivePredictiveValue = 0

    return output_image, TruePositive, TrueNegative, FalsePositive, FalseNegative, accuracy, sensitivity, specificity, positivePredictiveValue
\$\endgroup\$
1
  • \$\begingroup\$ You don't need a second logical_and for each mask if you apply the color of the background_mask after all other colors. You may also cache the values of truth_image > 127 and so on to avoid recomputing them, and use np.logical_not over this value instead of recomputing truth_image <= 127. \$\endgroup\$ Jul 19, 2018 at 14:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.