Search code examples
pythonnumpyimage-processing

Why is the image result flipped by 90 degrees?


I am trying to turn all of the white pixels in this image red, but when I run the program the shape is fine but the red pixels are rotated 90 degrees:

result

This is the current code that I am using to do this:

import cv2 as cv
import numpy as np
import os
from matplotlib import pyplot as plt
import cv2 as cv

def get_white_pixels(image_as_array):
    threshold = 40
    indices = np.where(image_as_array >= threshold)
    width=image.shape[1]
    height=image.shape[0]
    cartesian_y=height-indices[0]-1
    np_data_points=np.column_stack((indices[1],cartesian_y)) 
    return cartesian_y, np_data_points, width,height

image = cv.imread("framenumber0.jpg")
ind, pixels, width, height = get_white_pixels(image)

#Goes through every pixel and changes its values
for i in range(0, len(pixels)):
    loc_x = int(pixels[i][0])
    loc_y = int(pixels[i][1])
    image[loc_x,loc_y] = (0,0,255)

cv.imshow('Modified Image', image)
cv.waitKey(0)
cv.destroyAllWindows()

I do need the location of the white points as I will use them later for the second part of the project. I suspect the problem has something to do with the np.column_stack(). I have been reading the info page of the function but I still do not understand why this happens.

If you want to replicate here is also the image that I am using:

source


Solution

  • Change this line to:

        # image[loc_x,loc_y] = (0,0,255)
        image[loc_y,loc_x] = (0,0,255)
    

    along with

        # cartesian_y=height-indices[0]-1
        cartesian_y=indices[0]
    
    

    You need to know that numpy and OpenCV arrays are y,x ones, where other image processing packages work the x,y way.

    Here the result of running the fixed code:

    OK

    As mentioned in a comment by aRTy

    You are effectively flipping the y-coordinate top-down in this line: cartesian_y=height-indices[0]-1. If you are converting from OpenCV/numpy orientation (where y=0 is top) to matplotlib orientation (where y=0 is bottom) then you need to do that when you reach the matplotlib section, not before.


    The required effect of getting the thresholded image which is apparently a grayscale one can also be achieved using simplified code separating the channels and running OpenCV threshold on one of them:

    import cv2 as cv, numpy as np
    
    threshold = 40
    image = cv.imread("srcImage.jpg")
    _, G, _ = cv.split(image)
    _, thresh = cv.threshold(G, threshold, 255, cv.THRESH_BINARY)
    black=np.zeros_like(thresh)
    red_image = cv.merge([black, black, thresh])
    
    cv.imshow('White areas in source Image marked red', red_image)
    cv.waitKey(0)
    cv.destroyAllWindows()
    

    The result appears to be the same even if two channels are skipped from consideration.