Define correctly a variable to be read in all parts of the code

0

Good I have this code and I am not clear what parameters I have to pass to the function or which to return.

It returns an error that "grayFrame" is not defined. I do not understand if I have to declare it in another way or in another place.

  

line 41, in search_circles       circles = cv2.HoughCircles (grayFrame, cv2.HOUGH_GRADIENT, 1.20, param1 = 50, param2 = 30, minRadius = 0, maxRadius = 0)    NameError: name 'grayFrame' is not defined

My code

import numpy as np
import cv2

cap = cv2.VideoCapture(1)

# Función main
def main():

    cv2.namedWindow('ventana')
    cv2.setMouseCallback('ventana',buscar_circulos)

    while(True):

        ret, frame = cap.read()

        grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        grayFrame = cv2.medianBlur(grayFrame,5)

        cv2.imshow('ventana',grayFrame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()


def buscar_circulos(event,x,y,flags,param):
    if event == cv2.EVENT_LBUTTONDOWN:
        circles = cv2.HoughCircles(grayFrame,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)

        if circles is not None:
            circles = np.round(circles[0, :]).astype(int)
            for (x, y, r) in circles:
                cv2.circle(grayFrame, (x, y), r, (255, 0, 0), 1)
                print (x,y)


if __name__ == '__main__':
    main()
    
asked by NEA 24.08.2018 в 12:11
source

3 answers

1

To do this, you should use global variables, these are after the import and can be called in the code as if one more variable were, unlike to change the value, you have to use the reserved word global .

Your code would look like this:

import numpy as np
import cv2
grayFrame = ""
cap = cv2.VideoCapture(1)

def main():
    global grayFrame #Con esto ya puedes modificar su valor en este método
    cv2.namedWindow('ventana')
    cv2.setMouseCallback('ventana',buscar_circulos)

    while(True):

        ret, frame = cap.read()

        grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        grayFrame = cv2.medianBlur(grayFrame,5)

        cv2.imshow('ventana',grayFrame)

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()


def buscar_circulos(event,x,y,flags,param):
    if event == cv2.EVENT_LBUTTONDOWN:
        circles = cv2.HoughCircles(grayFrame,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)

    if circles is not None:
        circles = np.round(circles[0, :]).astype(int)
        for (x, y, r) in circles:
            cv2.circle(grayFrame, (x, y), r, (255, 0, 0), 1)
            print (x,y)


if __name__ == '__main__':
    main()

As the user @Saelyth correctly indicates in the comments of this publication, we must be very careful with the global variables, here the reason:

link

    
answered by 24.08.2018 / 12:20
source
3

Another alternative to the suggestion of @XBoss would be to encapsulate everything in a class and define a parameter in its initializer, that parameter can be used by all the functions within the class.

import numpy as np
import cv2
cap = cv2.VideoCapture(1)

class MiProgramita(object):
"""Esta clase sirve para [inserta como funciona]"""

    def __init__(self):
        """Define variables que se inicializan a nivel de clase"""
        self.grayframe = ""

    def main(self):
        """Código principal"""
        cv2.namedWindow('ventana')
        cv2.setMouseCallback('ventana',buscar_circulos)

        while(True):

            ret, frame = cap.read()

            self.grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            self.grayFrame = cv2.medianBlur(self.grayFrame, 5)

            cv2.imshow('ventana', self.grayFrame)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

        cap.release()
        cv2.destroyAllWindows()


    def buscar_circulos(self, event,x,y,flags,param):
        """Función que busca circulos"""
        if event == cv2.EVENT_LBUTTONDOWN:
            circles = cv2.HoughCircles(self.grayFrame,cv2.HOUGH_GRADIENT,1,20,param1=50,param2=30,minRadius=0,maxRadius=0)

        if circles is not None:
            circles = np.round(circles[0, :]).astype(int)
            for (x, y, r) in circles:
                cv2.circle(self.grayFrame, (x, y), r, (255, 0, 0), 1)
                print (x,y)



if __name__ == '__main__':
    ClaseInstanciada = MiProgramita()  # Inicializa la clase
    ClaseInstanciada.main()  # Llama a la función main de la clase.

If you do not understand how this solution works, you should read about how the Classes work in Python (Or in object-oriented programming, really).

    
answered by 24.08.2018 в 12:30
2

There is a third way to do this without using global variables or encompassing it in a class; move to the callback via arguments everything you need. We can pass a dictionary with the key "grayFrame" and that has as value the array (frame). Being a mutable dictionary, we can modify it from both functions (in Python the arguments are passed through assignment). You can pass the arguments you want using the dictionary or another mutable object such as a list, DataClass, etc. In C ++ we could use a struct, for example.

In this case setMouseCallback puts at our disposal the argument param ( userdata in C ++) that exists for this precisely:

import numpy as np
import cv2


# Función main
def main():
    cap = cv2.VideoCapture(1)
    params = {"grayFrame": None}
    cv2.namedWindow('ventana')
    cv2.setMouseCallback('ventana', buscar_circulos, param=params)

    while True:
        ret, frame = cap.read()
        grayFrame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        grayFrame = cv2.medianBlur(grayFrame, 5)
        params["grayFrame"] = grayFrame

        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

        cv2.imshow('ventana', params["grayFrame"])

    cap.release()
    cv2.destroyAllWindows()


def buscar_circulos(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        grayFrame = param["grayFrame"]
        circles = cv2.HoughCircles(grayFrame, cv2.HOUGH_GRADIENT, 1, 20,
                                   param1=50, param2=30, minRadius=0, maxRadius=0
                                   )

        if circles is not None:
            circles = np.round(circles[0, :]).astype(int)
            for x, y, r in circles:
                cv2.circle(grayframe, (x, y), r, (255, 0, 0), 1)
                print(x, y)


if __name__ == '__main__':
    main()

With grayFrame = param["grayFrame"] we make the variable grayFrame point to the reference of the object pointed to by at that time the key "grayFrame" . This apart from allowing to write less code avoids the search in the dictionary every time we need the value, however, if we are going to assign a new object it must be done by param["grayFrame"] = obj , if it is assigned to grayFrame only this variable is modified local, not param["grayFrame"] and therefore does not affect main() . Always remember that the Python variables are only reference identifiers to an object in memory.

I do not know if your code will work as I think you expect, the detection will only take place in one frame (when you click) and only the circles in that frame will be drawn for a moment. If you want to activate or deactivate the detection by clicking on the left you can use a flag and perform the detection on the mainloop:

import numpy as np
import cv2



def buscar_circulos(event, x, y, flags, param):
    if event == cv2.EVENT_LBUTTONDOWN:
        param["buscar"] = not(param["buscar"])


def main():
    cap = cv2.VideoCapture(0)  # Cambiar dispositivo si procede
    params = {"buscar": False}
    cv2.namedWindow('ventana')
    cv2.setMouseCallback('ventana', buscar_circulos, param=params)

    while True:
        ret, frame = cap.read()
        if params["buscar"]:
            gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
            gray = cv2.GaussianBlur(gray,(9, 9), 2, 2);
            circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 75,
                                       param1=100, param2=50,
                                       minRadius=0, maxRadius=0
                                       )
            if circles is not None:
                circles = np.round(circles[0, :]).astype("int")
                for (x, y, r) in circles:
                    cv2.circle(frame, (x, y), r, (0, 255, 0), 4)
                    cv2.rectangle(frame, (x - 5, y - 5), (x + 5, y + 5), (0, 128, 255), -1)

        cv2.imshow('ventana', frame)
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    cap.release()
    cv2.destroyAllWindows()



if __name__ == "__main__":
    main()

Clicking on the window activates the detection, when clicking again it is deactivated and so on. The previous filter and the HoughCircles parameters must be fine-tuned for each particular case.

    
answered by 25.08.2018 в 15:56