Python Forum
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
can't find libpHash
#11
Dear community,

with your great support I coded a perceptual-hash-hanger-finder for digitized super8-films.

I got the hangerfinder working in pycharm.

I created an exe with pyinstaller - but outside of pycharm a new pyqt window is opened again and again.

I guess that I have to hide the hangerfinder behind:
if __name__ == "__main__":
to avoid this Behave.

I'm trying to achieve the following:

When the button "button_HF_Start" is clicked, it is checked, if a film, which is to be checked for hangers, is marked with x in excel.
If the film was marked, the hangerfinder starts examining the frames in folder and when this is done, the timestamps are written to excel.
Then the script ends.

Now I'm a bit confused, because I'm not sure how I have to rearrange my script.

I have a big request (and know it takes a lot of effort):

Could you please show me how I have to rearrange my code?

That would be great, because it was much work for you and me and this is the final step (rearrange the script and creating an exe).

Thanks a lot for your help!! I will be very thankful.

this works in pycharm:

from PyQt5.QtWidgets import QApplication, QWidget, QPushButton, QMessageBox,QHBoxLayout, QVBoxLayout, QLineEdit, QLabel
from PyQt5.QtCore import QCoreApplication
import ctypes
import os, sys
import time
import openpyxl
from itertools import zip_longest
from multiprocessing import Pool
from PIL import Image
import imagehash

p = "C:/hangerfinder/bild_analyse/"

def create_phash(frame):
    # load frames
    frame = Image.open(p + str(frame))
    # create pHash
    # Compare hashes to determine whether the frames are the same or not
    phash = str(imagehash.phash(frame))
    return phash

def difference_count(a: str, b: str) -> int:
    # count differences between a and b
    return sum(1 for a, b in zip_longest(a, b) if a != b)

def phash_iter(d):
    # frame phash iterator
    # use multiprocessing to hash frames in parallel
    pool = Pool(processes=6)
    for index, phash in enumerate(pool.imap(create_phash, d, chunksize=1)):
        yield index, phash

def detect_hangers(phash_iter, threshold, min_count):
    """Return list of "hangers" detected in frame_hash_list.
    A "hanger" is consecutive frames that are the same.

    frame_hash_list : list of frame hash strings.  Frames are considered
    same or different by counting the differences in their hash strings.

    threshold : Maximum number of diffences allowed for two frames to be
    considered "same".

    min_count : Minimum length of a hanger.  Short hangers aren't noticable
    and don't have to be removed.
    """
    hangers = []  # List of hanger start, stop frame indexes
    start_index, start_phash = next(phash_iter)
    for index, phash in phash_iter:
        print(index, phash)
        # Are frame and start_frame disimilar enough?
        if abs(difference_count(start_phash, phash)) > threshold:
            if index - start_index >= min_count:
                # Add hanger to list
                hangers.append((start_index, index - 1))
            start_phash = phash
            start_index = index
    # Check if we end with a hanger
    if index - start_index > 10:
        hangers.append((start_index, index))
    return hangers

def convert_frame_nr_in_time(d):
    # S8-Movie (avi-file) is checked of hangers
    #####################################################
    # 1 hour contains 72000 frames
    c1 = 72000
    # 1 minute contains 1200 frames
    c2 = 1200
    # 1 second contains 20 frames
    c3 = 20

    def find_even_frame_nr(a, b, c):
        while True:
            if a % c == 0:
                break
            else:
                a -= 1
                b += 1
        return a, b

    frame_nr_full_hour, rest_1 = find_even_frame_nr(d, 0, c1)
    number_of_hours = frame_nr_full_hour / c1
    ###########################################################
    frame_nr_full_minute, rest_2 = find_even_frame_nr(rest_1, 0, c2)
    number_of_minutes = frame_nr_full_minute / c2
    ###########################################################
    frame_nr_full_second, rest_3 = find_even_frame_nr(rest_2, 0, c3)
    number_of_seconds = frame_nr_full_second / c3

    return number_of_hours, number_of_minutes, number_of_seconds


def fill_hanger_information_in_excel(hangers, numberOfHangers):
    pH = "C:\hangerfinder\S8-Hanger_Positionen.xlsm"
    fileXLSX = openpyxl.load_workbook(pH, keep_vba=True)
    sheet = fileXLSX["Blatt"]

    # find film to check for hangers in excel
    # film was marked with "x" by me
    r = 2
    for c in range(2, 82, 4):
        if sheet.cell(r, c).value == "x":
            hanger_start_col = c
            hanger_end_col = c + 2

    # clear old hanger information
    # film doesn't have more than 100 hangers
    clear_row = 5
    clear_col = hanger_start_col
    for z in range(clear_row, clear_row + 100):
        for s in range(clear_col, clear_col + 3):
            sheet.cell(row=z, column=s).value = None

    # fill in hanger information
    r = 5
    for i in hangers:
        frame_nr_hanger_start = i[0]
        frame_nr_hanger_end = i[1]
        number_of_hours_start, number_of_minutes_start, number_of_seconds_start = convert_frame_nr_in_time(
            frame_nr_hanger_start)
        number_of_hours_end, number_of_minutes_end, number_of_seconds_end = convert_frame_nr_in_time(
            frame_nr_hanger_end)
        number_of_hours_start_int = int(number_of_hours_start)
        number_of_minutes_start_int = int(number_of_minutes_start)
        number_of_seconds_start_int = int(number_of_seconds_start)
        number_of_hours_end_int = int(number_of_hours_end)
        number_of_minutes_end_int = int(number_of_minutes_end)
        number_of_seconds_end_int = int(number_of_seconds_end)

        number_of_hours_start_str = str(number_of_hours_start_int)
        if len(number_of_hours_start_str) == 1:
            number_of_hours_start_str = "0" + number_of_hours_start_str
        number_of_minutes_start_str = str(number_of_minutes_start_int)
        if len(number_of_minutes_start_str) == 1:
            number_of_minutes_start_str = "0" + number_of_minutes_start_str
        number_of_seconds_start_str = str(number_of_seconds_start_int)
        if len(number_of_seconds_start_str) == 1:
            number_of_seconds_start_str = "0" + number_of_seconds_start_str
        number_of_hours_end_str = str(number_of_hours_end_int)
        if len(number_of_hours_end_str) == 1:
            number_of_hours_end_str = "0" + number_of_hours_end_str
        number_of_minutes_end_str = str(number_of_minutes_end_int)
        if len(number_of_minutes_end_str) == 1:
            number_of_minutes_end_str = "0" + number_of_minutes_end_str
        number_of_seconds_end_str = str(number_of_seconds_end_int)
        if len(number_of_seconds_end_str) == 1:
            number_of_seconds_end_str = "0" + number_of_seconds_end_str

        # create timestamp
        timestamp_start_str = number_of_hours_start_str + ":" + number_of_minutes_start_str + ":" + number_of_seconds_start_str
        timestamp_end_str = number_of_hours_end_str + ":" + number_of_minutes_end_str + ":" + number_of_seconds_end_str
        hangerNrs = "(" + str(frame_nr_hanger_start) + ", " + str(frame_nr_hanger_end) + ")"
        hangerNrs_col = hanger_start_col - 1
        sheet.cell(row=r, column=hangerNrs_col).value = hangerNrs
        sheet.cell(row=r, column=hanger_start_col).value = timestamp_start_str
        sheet.cell(row=r, column=hanger_end_col).value = timestamp_end_str
        r += 1
        hanger_text_col = hanger_start_col-1
    sheet.cell(row=r, column=hanger_text_col).value = "gefundene Hänger:"
    sheet.cell(row=r, column=hanger_start_col).value = numberOfHangers
    fileXLSX.save(pH)
    fileXLSX.close
    ctypes.windll.user32.MessageBoxW(0, "All hangers were written to excel.", "Cool...", 1)



if __name__ == "__main__":

    def button_HF_Start_click():
        # check if film is marked with x in excel
        pH = "C:\hangerfinder\S8-Hanger_Positionen.xlsm"
        fileXLSX = openpyxl.load_workbook(pH, keep_vba=True)
        sheet = fileXLSX["Blatt"]
        r = 2
        xOcurrence = 0
        for c in range(2, 82, 4):
            if sheet.cell(r, c).value == "x":
                xOcurrence = 1
        if xOcurrence == 0:
            ctypes.windll.user32.MessageBoxW(0,
                                             "Please mark the film to be checked for hangers in excel with an ""X"".",
                                             "You forgot something...", 1)
            sys.exit()

        begin = time.time()
        all_frames = os.listdir(p)
        threshold = textboxThreshold.text()
        min_count = textboxMinCount.text()
        hangers = detect_hangers(phash_iter(all_frames), int(threshold), int(min_count))
        numberOfHangers = len(hangers)
        fill_hanger_information_in_excel(hangers, numberOfHangers)
        end = time.time()
        print('hangers:')
        print(hangers)
        print('number of hangers: ' + str(numberOfHangers))
        print('time: ' + str(end - begin))
        return hangers

    app = QApplication([sys.argv])
    window = QWidget()

    button_HF_Start = QPushButton("Hängerfinder ausführen")
    labelThreshold = QLabel()
    labelThreshold.setText("erlaubter Unterschied für gleiche Bilder (threshold):")
    textboxThreshold = QLineEdit()
    textboxThreshold.setText(str(0))
    labelMinCount = QLabel()
    labelMinCount.setText("kleinster Hänger, der gefunden wird (Anzahl Bilder, min_count):")
    textboxMinCount = QLineEdit()
    textboxMinCount.setText(str(5))
    button_close = QPushButton("schließen")

    layout = QVBoxLayout()
    layout.addWidget(labelThreshold)
    layout.addWidget(textboxThreshold)
    layout.addWidget(labelMinCount)
    layout.addWidget(textboxMinCount)

    layout2 = QHBoxLayout()
    layout2.addWidget(button_HF_Start)
    layout2.addWidget(button_close)

    layout.addLayout(layout2)

    window.setLayout(layout)
    window.show()
    button_HF_Start.clicked.connect(button_HF_Start_click)
    button_close.clicked.connect(QCoreApplication.instance().quit)
    sys.exit(app.exec())
Reply
#12
Sounds like you have something that imports this code like a module, and it does it multiple times. What happens if you try to run the code from the Python interpreter?

I don't like how you make the window. Make a class that defines the window. Don't write the window code like a script.
class MyWindow(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        # make all the widgets
        button_HF_Start.clicked.connect(self.start_processing)
        button_close.clicked.connect(self.close)

    def start_processing(self):
        """Called when start button is pressed"""
        # code

def main():
    """Do not run this code when file is imported."""
    app = QtWidgets.QApplication()
    window = MyWindow()
    window.show()
    sys.exit(window.exec())

if __name__ == "__main__":
    main()
And don't use QCoreApplication.instance().quit. Close the main window and Qt will gracefully shut down the application.
Reply
#13
Dear deanhystad,

Thanks a lot for your answer!

I just want to inform you that I can't change the code before monday, because I will be back at work at this day.

Then I can continue working on this.

I wish you a great evening!
Reply
#14
Hi,

I read a tutorial from "realpython.com" about building GUIs.

And I have designed the following:
class MyWindow(QWidget):
    def __init__(self):
        super().__init__()
        # make all the widgets
        labelThreshold = QLabel()
        labelThreshold.setText("erlaubter Unterschied für gleiche Bilder (threshold):")
        textboxThreshold = QLineEdit()
        textboxThreshold.setText(str(0))
        labelMinCount = QLabel()
        labelMinCount.setText("kleinster Hänger, der gefunden wird (Anzahl Bilder, min_count):")
        textboxMinCount = QLineEdit()
        textboxMinCount.setText(str(5))
        button_HF_Start = QPushButton("Hängerfinder ausführen")
        button_close = QPushButton("schließen")
        layout = QVBoxLayout()
        layout.addWidget(labelThreshold)
        layout.addWidget(textboxThreshold)
        layout.addWidget(labelMinCount)
        layout.addWidget(textboxMinCount)
        layout2 = QHBoxLayout()
        layout2.addWidget(button_HF_Start)
        layout2.addWidget(button_close)
        layout.addLayout(layout2)
        button_HF_Start.clicked.connect(self.start_processing)
        button_close.clicked.connect(self.close)

    def start_processing(self):
        # called when startbutton is clicked
        # check if film is marked with x in excel
        pH = "C:\hangerfinder\S8-Hanger_Positionen.xlsm"
        fileXLSX = openpyxl.load_workbook(pH, keep_vba=True)
        sheet = fileXLSX["Blatt"]
        r = 2
        xOcurrence = 0
        for c in range(2, 82, 4):
            if sheet.cell(r, c).value == "x":
                xOcurrence = 1
        if xOcurrence == 0:
            ctypes.windll.user32.MessageBoxW(0, "Please mark the film to be checked for hangers in excel with an ""X"".", "You forgot something...", 1)
            sys.exit()
        begin = time.time()
        all_frames = os.listdir(p)
        threshold = textboxThreshold.text()
        min_count = textboxMinCount.text()
        hangers = detect_hangers(phash_iter(all_frames), int(threshold), int(min_count))
        numberOfHangers = len(hangers)
        fill_hanger_information_in_excel(hangers, numberOfHangers)
        end = time.time()

    def close(self):
        quit()

def main(QWidget):
    # Is not executed when file is imported
    app = QApplication([])
    window = MyWindow(QWidget)
    window.setLayout(layout)
    window.setWindowTitle("hangerfinder")
    window.setGeometry(100, 100, 200, 200)
    sys.exit(app.exec())

if __name__ == "__main__":
    main(QWidget)
But I get the error:
Error:
Traceback (most recent call last): File "D:\programmierOrdner\python-Programmierung\hangerfinder\main.py", line 227, in <module> main(QWidget) File "D:\programmierOrdner\python-Programmierung\hangerfinder\main.py", line 220, in main window = MyWindow(QWidget) ^^^^^^^^^^^^^^^^^ TypeError: MyWindow.__init__() takes 1 positional argument but 2 were given
I tried it very long, but now I'm stuck...
I'm not sure, what to do, because I'm (not yet) experienced in this matter...
Perhaps someone could help me out...

Thanks a lot for your help!!
Reply
#15
This is a new topic. You should not tack new topics on the end of existing threads.

To create an instance of your class you call the "call" the class and any arguments needed by the __init__() method. Your __init__() method doesn't take any arguments, so you should call it like this.
window = MyWindow()
__init__() is an instance method. Python automatically provides the "self" argument for instance methods.
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020