-1

I have the problem that I cannot connect the implementation between two buttons. After I pressed the "Grayscale" button and got the grayscale image, I pressed the "Canny" button but the user interface suddenly closed. I don't know what's wrong in my code.

def getImage(self):
    global fname
    fname = QFileDialog.getOpenFileName(self, 'Open file', 
       'C:\\Users\binil-ping\Desktop\CODE',"Image Files (*.jpg *.gif *.bmp *.png)")
    pixmap = QPixmap(fname[0])
    self.label.setPixmap(QPixmap(pixmap))
    self.resize(pixmap.width(), pixmap.height())

def Grayscale(self):
    global edges
    edges = cv2.imread(fname[0], 0)
    edges = cv2.GaussianBlur(edges, (5, 5), 0)
    height, width = edges.shape[:2]
    ret,edges = cv2.threshold(edges,150,255,cv2.THRESH_BINARY)
    kernel = np.ones((5,5),np.uint8)
    edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)
    edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)

    edges = QImage(edges, width, height, QImage.Format_Grayscale8)
    pixmap = QPixmap.fromImage(edges)
    self.label.setPixmap(pixmap)
    self.resize(pixmap.width(), pixmap.height())

def Canny(self):
    edges2 = cv2.imread(edges[0],-1)
    edges2 = cv2.Canny(edges2,180,200)

    edges2 = QImage(edges2, width, height, QImage.Format_Grayscale8)
    pixmap = QPixmap.fromImage(edges2)
    self.label.setPixmap(pixmap)
    self.resize(pixmap.width(), pixmap.height())

enter image description here

eyllanesc
  • 235,170
  • 19
  • 170
  • 241

2 Answers2

1

There are a lot of problems with your code (including the fact that you didn't provide a minimal, reproducible example), some of them explaining the crash or possibly leading to another one (I've marked them with [*]):

  • as already pointed out, avoid using globals whenever possible (which is almost always), use class attributes instead;
  • you are continuously overwriting those globals, making everything very confusing also for debugging; in fact you're using edges for both a numpy array and a QImage;
  • [*] you are trying to cv2.imread(edges[0],-1), but not only imread expects a string, but, as a result of the previous point, at that point edges is even a QImage;
  • [*] some variables are not declared in the scope of the function (width/height in the Canny function);
  • you should really avoid using capitalized names for functions and variables, as they are usually only for class names and builtin constants;
  • there are some issues in the conversion back to QImage, I suppose that's due to the various transformations you're applying to to the array (some of them also seem unnecessary) that are not very friendly with the Format_Grayscale8 format, perhaps you should better investigate about that;

Here's a possible improvement over your code. I'm adding the widget creation parts, as it was missing in the original example.

class ShowImage(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        layout = QVBoxLayout(self)
        self.label = QLabel()
        layout.addWidget(self.label)

        self.getImageBtn = QPushButton()
        layout.addWidget(self.getImageBtn)
        self.getImageBtn.clicked.connect(self.getImage)

        self.grayBtn = QPushButton('gray')
        layout.addWidget(self.grayBtn)
        self.grayBtn.clicked.connect(self.grayScale)

        self.cannyBtn = QPushButton('canny')
        layout.addWidget(self.cannyBtn)
        self.cannyBtn.clicked.connect(self.canny)

        self.fileName = None
        self.edges = None

    def getImage(self):
        fname = QFileDialog.getOpenFileName(self, 'Open file', 
           'C:\\Users\binil-ping\Desktop\CODE',"Image Files (*.jpg *.gif *.bmp *.png)")
        if fname[0]:
            self.fileName = fname[0]
            pixmap = QPixmap(self.fileName)
            self.label.setPixmap(pixmap)

    def grayScale(self):
        if not self.fileName:
            return
        edges = cv2.imread(self.fileName, 0)
        edges = cv2.GaussianBlur(edges, (5, 5), 0)
        ret,edges = cv2.threshold(edges, 150, 255, cv2.THRESH_BINARY)
        kernel = np.ones((5, 5), np.uint8)
        edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)
        edges = cv2.morphologyEx(edges, cv2.MORPH_OPEN, kernel)

        self.edges = edges
        height, width = edges.shape[:2]
        image = QImage(edges, width, height, QImage.Format_Grayscale8)
        pixmap = QPixmap.fromImage(image)
        self.label.setPixmap(pixmap)

    def canny(self):
        if self.edges is None:
            return
        edges2 = cv2.Canny(self.edges, 180, 200)
        height, width = edges2.shape[:2]

        edges2 = QImage(edges2, width, height, QImage.Format_Grayscale8)
        pixmap = QPixmap.fromImage(edges2)
        self.label.setPixmap(pixmap)
musicamante
  • 41,230
  • 6
  • 33
  • 58
  • thank you very much for your code. btw, I have just joined the forum for few days so I made mistakes while posting the questions, thank for reminding me. – Nguyễn Anh Duy Oct 29 '19 at 11:41
  • No problem; maybe you can take some time reading [how to ask a good question](https://stackoverflow.com/help/how-to-ask); remember to mark this (or any) answer as accepted if it solves your problem, and upvote any answer that you consider as helpful or gave you some other help in any way. – musicamante Oct 29 '19 at 12:04
  • thank you. Btw, I tried your code and it worked. But "def canny(self)" took image from "def getImage" because of "self.fileName", not from "def grayScale(self)". Could you help me to modify that? Thank you very much. – Nguyễn Anh Duy Oct 29 '19 at 12:48
  • @NguyễnAnhDuy See the updated code. I just created a class attribute at the end of the grayScale() function, then use it in canny(). – musicamante Oct 29 '19 at 13:13
  • I'm sorry to bother you, but it did not work in canny(). :( – Nguyễn Anh Duy Oct 29 '19 at 14:04
  • Fixed. Anyway, it was explained in the error message: "The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()", but since we are using it to check if the object exists, we have to check against "None" instead. That said, that's *not* a suggested approach anyway; you should probably set the button as disabled on startup, and enable it when the grayScale function processes the image. – musicamante Oct 29 '19 at 15:23
-1

Rule #1: Don't use globals.

Now on to the real answer.

PyQt has this really, really bad, annoying habit of just crashing if you have an uncaught exception in your code. That makes it hard to debug. Personally, I use a decorator around slots when debugging, which print my exception before PyQt can kill the application.

import logging
from functools import wraps
logger = logging.getLogger(__name__)
# ...lots of other code...
def debugFunc(*errors):
    def decorator(func):
        @wraps(func)
        def runner(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except errors as err:
                logger.debug(f"Function {func.__name__} raised a {err.__class__.__name__}, which was ignored.",
                             exc_info=True)
            except Exception as err:
                logger.critical(f"{err.__class__.__name__} - {err}", exc_info=True)
        return runner
    return decorator

While debugging, this allows me to ignore certain errors, and sends all others to the logging system. If you don't use it yourself and aren't willing to start using it, you can instead import traceback and use it's functions, instead of my calls to my logger object.

Just use this function as the innermost decorator, like this:

@debugFunc()
def Canny(self):
    edges2 = cv2.imread(edges[0],-1)
    edges2 = cv2.Canny(edges2,180,200)

    edges2 = QImage(edges2, width, height, QImage.Format_Grayscale8)
    pixmap = QPixmap.fromImage(edges2)
    self.label.setPixmap(pixmap)
    self.resize(pixmap.width(), pixmap.height())[enter image description here][1]

While I'm not familiar enough with cv2 to say for sure, I think your problem may be cv2.Canny. It doesn't look like the naming of other cv2 functions, and may cause an AttributeError if I'm right.

Gloweye
  • 1,294
  • 10
  • 20
  • The question is "what is the problem", not "how can I find what is the problem". While yours *is* a good advice, it doesn't seem to me an *answer* to what is asked in the question. – musicamante Oct 29 '19 at 11:10
  • @musicamante The problem is that PyQt doesn't print exceptions before crashing, and this is how to fix that. Even if that's not the root problem, it's the problem that prevents OP from progressing. It's just as much an answer as any frame challenge answer. – Gloweye Oct 29 '19 at 11:15
  • It depends on the exception, and generally speaking, if the error is not due to (Py)Qt itself, python will throw its traceback anyway like in this case, which was a type error due to the fact that the imread function expects a file name string and got a QImage instead. That said, I still believe that yours is an useful *suggestion* (for which it can be found a method to explain how to use it or eventually link in the comments to the question), but not an useful *answer*. I'm sorry that you don't agree, but that's my point of view. – musicamante Oct 29 '19 at 11:28
  • I would say PyQt has some guilt, as it actively supresses the traceback from being printed. But different opinion is fair. – Gloweye Oct 29 '19 at 12:06
  • @Gloweye I didn't downvote, but the first section of your answer is based on some misunderstandings. The behaviour you describe is normal and entirely by design - see the PyQt5 Docs: [Unhandled Python Exceptions](https://www.riverbankcomputing.com/static/Docs/PyQt5/incompatibilities.html#unhandled-python-exceptions). If you always set an excepthook in your PyQt5 programs, you will never have any debugging problems. See [here](https://stackoverflow.com/a/33741755/984421) for a minimal example that restores the old behaviour. – ekhumoro Oct 29 '19 at 14:29
  • I am perfectly aware of PyQt's philosophy, and I couldn't disagree with their statements more. If I have something going terribly wrong in Python code, I want to see an exception printed about what went wrong. While you could argue it's not the wrapper's task to implement, I would say them raising a `SystemExit from UnhandledException` would be a much more pythonic response. IMHO. They could just do that in the mainloop. – Gloweye Oct 29 '19 at 14:33
  • @Gloweye But that's exactly what happens now: the program exits and prints the traceback to stdout/stderr. The old behaviour was totally unpythonic because it silently ignored unhandled exceptions. The new behaviour is exactly the same as you would see in a normal python script. – ekhumoro Oct 29 '19 at 14:39
  • @ekhumoro I'll admit to using Pycharm, but it catches both stdout and stderr in any other situation I've been in. And all I get when a slot crashes is an exit code, not a traceback. PyQt version 5.11.3, according to pip list. – Gloweye Oct 29 '19 at 14:44
  • @Gloweye That is an issue with PyCharm, then, rather than PyQt5 - and there are several other IDEs which have the same problem. This kind of thing regularly comes up on SO, especially from new users on Windows (which possibly includes the OP). Your answer has a good point to make about debugging, but you're putting the blame in the wrong place. – ekhumoro Oct 29 '19 at 14:51
  • @ekhumoro I disagree about that. The reason that PyCharm doesn't get to catch that output on stderr is because PyQt kills the process from the C-code (`qFatal()`), instead of the python code. If a program is started through Python, it should probably be terminated through Python as well. PyQt has that opportunity and doesn't take it. – Gloweye Oct 29 '19 at 15:00
  • @Gloweye I don't see why the IDE can't show the output like any normal console would. Does PyCharm still have an option like "emulate terminal in output console"? I have never used PyCharm myself (or any other IDE, for that matter), but I think I may have read somewhere that it might help. – ekhumoro Oct 29 '19 at 15:11
  • @Gloweye Definitely a PyCharm issue (and not specific to PyQt): see [here](https://intellij-support.jetbrains.com/hc/en-us/community/posts/207319305-No-stack-trace-printed-after-exception), for example. – ekhumoro Oct 29 '19 at 15:15