3
\$\begingroup\$

I have been trying to find defects (brown/copper parts) on this sample using PyQt, OpenCV and Python:

Sample with copper parts

And I can find defects, too. My problem is, I have nobody to ask, get feedback etc. I need some feedback about my code. Of course, I don't want someone to review my code line-by-line, but even a simple reviewing would be good, because I am very new to Python. I will using this on laptop

view.ui file

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>Form</class>
 <widget class="QWidget" name="Form">
  <property name="windowModality">
   <enum>Qt::NonModal</enum>
  </property>
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>751</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>Form</string>
  </property>
  <widget class="QLabel" name="label">
   <property name="geometry">
    <rect>
     <x>30</x>
     <y>40</y>
     <width>320</width>
     <height>240</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="frameShape">
    <enum>QFrame::Box</enum>
   </property>
   <property name="text">
    <string/>
   </property>
   <property name="scaledContents">
    <bool>true</bool>
   </property>
  </widget>
  <widget class="QLabel" name="label_2">
   <property name="geometry">
    <rect>
     <x>420</x>
     <y>90</y>
     <width>70</width>
     <height>100</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="frameShape">
    <enum>QFrame::Box</enum>
   </property>
   <property name="text">
    <string/>
   </property>
   <property name="scaledContents">
    <bool>true</bool>
   </property>
  </widget>
  <widget class="QPushButton" name="buton2">
   <property name="geometry">
    <rect>
     <x>340</x>
     <y>10</y>
     <width>75</width>
     <height>23</height>
    </rect>
   </property>
   <property name="text">
    <string>Find Defects</string>
   </property>
  </widget>
  <widget class="QLabel" name="label_4">
   <property name="geometry">
    <rect>
     <x>380</x>
     <y>240</y>
     <width>171</width>
     <height>16</height>
    </rect>
   </property>
   <property name="text">
    <string>Size of defects on the lower part</string>
   </property>
  </widget>
  <widget class="QScrollArea" name="scrollArea">
   <property name="geometry">
    <rect>
     <x>380</x>
     <y>280</y>
     <width>161</width>
     <height>80</height>
    </rect>
   </property>
   <property name="widgetResizable">
    <bool>true</bool>
   </property>
   <widget class="QWidget" name="scrollAreaWidgetContents">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>159</width>
      <height>78</height>
     </rect>
    </property>
   </widget>
  </widget>
  <widget class="QTextEdit" name="textEdit">
   <property name="geometry">
    <rect>
     <x>400</x>
     <y>420</y>
     <width>104</width>
     <height>71</height>
    </rect>
   </property>
  </widget>
  <widget class="QLabel" name="label_3">
   <property name="geometry">
    <rect>
     <x>620</x>
     <y>90</y>
     <width>70</width>
     <height>100</height>
    </rect>
   </property>
   <property name="sizePolicy">
    <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
     <horstretch>0</horstretch>
     <verstretch>0</verstretch>
    </sizepolicy>
   </property>
   <property name="frameShape">
    <enum>QFrame::Box</enum>
   </property>
   <property name="text">
    <string/>
   </property>
   <property name="scaledContents">
    <bool>true</bool>
   </property>
  </widget>
  <widget class="QLabel" name="label_5">
   <property name="geometry">
    <rect>
     <x>580</x>
     <y>240</y>
     <width>171</width>
     <height>16</height>
    </rect>
   </property>
   <property name="text">
    <string>Size of defects on the upper part</string>
   </property>
  </widget>
  <widget class="QTextEdit" name="textEdit_2">
   <property name="geometry">
    <rect>
     <x>600</x>
     <y>420</y>
     <width>104</width>
     <height>71</height>
    </rect>
   </property>
  </widget>
  <widget class="QScrollArea" name="scrollArea_2">
   <property name="geometry">
    <rect>
     <x>580</x>
     <y>280</y>
     <width>161</width>
     <height>80</height>
    </rect>
   </property>
   <property name="widgetResizable">
    <bool>true</bool>
   </property>
   <widget class="QWidget" name="scrollAreaWidgetContents_2">
    <property name="geometry">
     <rect>
      <x>0</x>
      <y>0</y>
      <width>159</width>
      <height>78</height>
     </rect>
    </property>
   </widget>
  </widget>
 </widget>
 <resources/>
 <connections/>
</ui>

main.py

from PyQt5.QtWidgets import QApplication
from view import loadUi_example
from model import Camera

if __name__ == '__main__':

    camera = Camera(0)
    app = QApplication([])
    start_window = loadUi_example(camera)
    start_window.show()
    app.exit(app.exec_())

view.py

from PyQt5.uic import loadUi
from PyQt5 import QtGui
from PyQt5.QtCore import pyqtSlot
from PyQt5.QtCore import QTimer
from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtGui import QPixmap, QImage


class loadUi_example(QMainWindow):

    def __init__(self, cam_num):
        super().__init__()
        loadUi("view.ui", self)
        self.cam_num = cam_num
        self.scrollArea.setWidget(self.textEdit) #size of defects will be shown in scrollArea
        self.scrollArea_2.setWidget(self.textEdit_2)
        self.timer = QTimer()
        self.timer.timeout.connect(self.readNextFrame)
        self.timer.start(1000 / 24) #24 fps

    def readNextFrame(self):
        self.frame = self.cam_num.read()

    @pyqtSlot()
    def on_buton2_clicked(self):
        if self.frame is not None:
            image = QImage(self.frame, self.frame.shape[1], self.frame.shape[0],
                           QImage.Format_RGB888)  # The image is stored using a 24-bit RGB format (8-8-8).
            self.pixmap = QPixmap.fromImage(image)
        self.label.setPixmap(self.pixmap)

        lower_sample,upper_sample = self.cam_num.find_sample()

        lowerPartDefects,the_list = self.cam_num.find_defects(lower_sample)

        qImg = self.fit_image(lowerPartDefects)
        self.label_2.setPixmap(QtGui.QPixmap.fromImage(qImg))

        self.list_defects(the_list)
        self.textEdit.setText(self.line)

        upperPartDefects, the_list = self.cam_num.find_defects(upper_sample)

        qImg = self.fit_image(upperPartDefects)
        self.label_3.setPixmap(QtGui.QPixmap.fromImage(qImg))

        self.list_defects(the_list)
        self.textEdit_2.setText(self.line)

    def list_defects(self,the_list):
        self.line = ""
        i = 1
        for x in the_list:
            self.line += "Size of defect %d = %d\n" % (i, x)
            i = i + 1
        return self.line

    def fit_image(self,image):
        h, w, ch = image.shape
        bytesPerLine = ch * w
        qImg = QtGui.QImage(image.data, w, h, bytesPerLine, QtGui.QImage.Format_RGB888)
        return qImg

model.py

import cv2
import numpy as np


class Camera:

    def __init__(self, cam_num):
        self.cap = None

    def read(self):
        self.frame = cv2.imread('input.png')
        self.frame = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
        return self.frame

    def find_sample(self):
        hsv = cv2.cvtColor(self.frame, cv2.COLOR_RGB2HSV)
        lower_limit = np.array([0, 0, 93])
        upper_limit = np.array([255, 255, 255])
        mask = cv2.inRange(hsv, lower_limit, upper_limit)  #created a mask to remove background
        mask_inv = cv2.bitwise_not(mask)
        bg = cv2.bitwise_and(self.frame, self.frame, mask=mask)
        fg = cv2.bitwise_and(self.frame, self.frame, mask=mask_inv)
        gray = cv2.cvtColor(bg,cv2.COLOR_BGR2GRAY)
        thresh = cv2.threshold(gray, 127, 255, cv2.THRESH_BINARY_INV)[1] #findContours function works better with binary images
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (11, 11))
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel) #remove noise
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
        cntrs = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1] #It accounts for the fact that different versions of OpenCV return different number of values for findContours().
        area_thresh = 5000
        cnt = 0
        for c in cntrs:
            area = cv2.contourArea(c)
            if area > area_thresh:
                cnt= cnt + 1
            if cnt > 0:
                rect = cv2.minAreaRect(c) #minArearect returns - ( center (x,y), (width, height), angle of rotation ).
                box = cv2.boxPoints(rect) # The function finds the four vertices of a rotated rectangle.
                box = np.int0(box) #converting numbers to integer
                # crop image inside bounding box
                centerX = rect[0][0]
                centerY = rect[0][1]
                W = rect[1][0] #width of contour
                H = rect[1][1] #height of contour
                Xs = [i[0] for i in box]
                Ys = [i[1] for i in box]
                x1 = min(Xs)
                x2 = max(Xs)
                y1 = min(Ys)
                y2 = max(Ys)
                angle = rect[2]
                rotated = False
                if angle < -45:
                    angle += 90
                    rotated = True
                center = (round(centerX), round(centerY))
                size = (int((x2 - x1)), int( (y2 - y1)))
                M = cv2.getRotationMatrix2D((size[0] / 2, size[1] / 2), angle, 1.0)
                #Calculates an affine matrix of 2D rotation.
                cropped = cv2.getRectSubPix(self.frame, size, center) #crop contour
                cropped = cv2.warpAffine(cropped, M, size) #rotate contour using 2D-RotationMatrix
                croppedW = W if not rotated else H
                croppedH = H if not rotated else W
                self.image = cv2.getRectSubPix(
                    cropped, (int(croppedW ), int(croppedH )), (size[0] / 2, size[1] / 2)) #crop contour
                if croppedH > croppedW:
                    if cnt == 1:
                        output1 = self.image[0:self.image.shape[0] - 70, 0: self.image.shape[1]]
                    if cnt == 2:  # bitis
                        output2 = self.image[70:self.image.shape[0] , 0: self.image.shape[1]]
        return output1,output2

    def find_defects (self, sample):
        self.the_list = []
        gray = cv2.cvtColor(sample,
                            cv2.COLOR_BGR2GRAY)

        mask = cv2.inRange(gray, 4, 43)
        bg = cv2.bitwise_and(gray, gray, mask=mask)
        thresh = cv2.threshold(bg, 4, 255, cv2.THRESH_BINARY)[1] #remove non-defect parts
        kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
        thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
        cntrs = cv2.findContours(thresh, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
        # APPROX_SIMPLE compresses horizontal, vertical, and diagonal segments and leaves only their end points.
        # For example, an up-right rectangular contour is encoded with 4 points.
        cntrs = cntrs[0] if len(cntrs) == 2 else cntrs[1]
        for c in cntrs:
            area = cv2.contourArea(c)
            area_thresh = 500
            if area > area_thresh:
                x, y, w, h = cv2.boundingRect(c)
                cv2.rectangle(sample, (x, y), (x + w, y + h), (200, 0, 0), 2)
                self.the_list.append(area)
        return sample, self.the_list

    def close_camera(self):
        self.cap.release()
        cv2.destroyAllWindows()
\$\endgroup\$
2
  • 1
    \$\begingroup\$ That's not quite complete - I think you need the view.ui file for anybody to test the program. \$\endgroup\$ Feb 4, 2020 at 9:48
  • 1
    \$\begingroup\$ Welcome to Code Review. I've added the contents of the file to your question. For context, will you be running this on a laptop, PC, server or an embedded platform (like Raspberry)? OpenCV handles just a bit different on different platforms and this may be relevant for the resource usage as well. \$\endgroup\$
    – Mast
    Feb 4, 2020 at 10:20

0

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.

Browse other questions tagged or ask your own question.