From 114ab399408620e36e837bb9f53907d72f0369a5 Mon Sep 17 00:00:00 2001 From: Erika Hauschild Date: Thu, 19 Sep 2019 16:11:46 -0400 Subject: [PATCH 1/4] Erika: Changed whitePixels to work on grayscale images --- Count and Size | 2 +- application/visual.py | 11 ++- count_and_size.py | 179 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 190 insertions(+), 2 deletions(-) create mode 100644 count_and_size.py diff --git a/Count and Size b/Count and Size index b92c49b..8c1f322 100644 --- a/Count and Size +++ b/Count and Size @@ -149,7 +149,7 @@ def whitePixelAreas(i_flocBounds, j_flocBounds, img): # Example usage -img = cv2.imread("pureblack.jpg") +img = cv2.imread("openCV/flocs/Image \32346.jpg") count_and_size_flocs(img) diff --git a/application/visual.py b/application/visual.py index 6770577..e5f6e19 100644 --- a/application/visual.py +++ b/application/visual.py @@ -1 +1,10 @@ -#Hi everyone -Erika \ No newline at end of file +import wx + +Panel() + +Panel(parent, id=ID_ANY, pos=DefaultPosition, size=DefaultSize, + style=TAB_TRAVERSAL, name=PanelNameStr) + + + + diff --git a/count_and_size.py b/count_and_size.py new file mode 100644 index 0000000..47f8421 --- /dev/null +++ b/count_and_size.py @@ -0,0 +1,179 @@ + + +# Written by Richard Yu (ry275@cornell.edu) + +import cv2 +import numpy as np + + +# Returns: A 2-dimensional list/grid that contains value True or False, which denotes whether a coordinate is a +# "near-white" pixel (R >= 225, G >= 225, B >= 225) or not +# Requires: an RGB 3-dimensional list/picture +def whitePixels(grid): + res = [[True for y in range(len(grid[0]))] for x in range(len(grid))] + for i in range(len(grid)): + for j in range(len(grid[i])): + curPixel = grid[i][j] + if curPixel < 255: + res[i][j] = False + #for rgb in curPixel: + #if rgb < 225: + #res[i][j] = False + #break + return res + + + +import sys + +# Returns: the count of non-zero (black) pixels for an inputted picture/3-dimensional list +# Requires: a 3-dimensinoal list/picture +def nonBlackPixels(img): + return cv2.countNonZero(img) + +''' +Purpose: + - Given an image of a floc, will apply a set of image proccessing Functions + to identify in focus flocs from the background. +Parameters: + - A .jpg image read in as a grayscale image i.e cv2.imread('path',0) +Returns: + - A processed .jpg image +Raises: + - +''' +def flocID(img): + blur = cv2.GaussianBlur(img, (5,5), 10) + t = cv2.adaptiveThreshold(blur, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, + cv2.THRESH_BINARY_INV,9,7) + kernel = np.ones((4,4),np.uint8) + dilation = cv2.dilate(t,kernel,iterations = 2) + opened = cv2.morphologyEx(dilation, cv2.MORPH_OPEN, kernel) + closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel) + return closed + +# Returns: scales down img's [i],[j] dimensions by a factor of 5, effectively shrinking the image to 1/25 its original size +# Requires: a 3-dimensional list/picture +def shrink5x(img): + shrunk = cv2.resize(img,None,fx=.2, fy=.2, interpolation = cv2.INTER_CUBIC) + return shrunk + + + +# Returns: a 5-tuple, containing the count of "near-white" pixel (R >= 225, G >= 225, B >= 225) islands, a +# list containing the counts of near-white pixels for each individual island, the total count of near-white pixels, +# the total count of non-black pixels, and a string explaining if there is a noticeable difference between the +# sum of individual near-white pixel counts and the total count of non-black pixels. +# Requires: an RGB 3-dimensional list/picture +def count_and_size_flocs(img): + + # Increases the recursive call stack limit to 100,000 to ensure + # that the method does not give a "recursion/stack overflow" error + sys.setrecursionlimit(100000) + + # Shrinks img to reduce the number of pixels this method has to iterate over + shrunk = shrink5x(img) + shrunk = whitePixels(shrunk) + islands = 0 + + i_flocBounds = [] + j_flocBounds = [] + + for i in range(len(shrunk)): + for j in range(len(shrunk[i])): + if shrunk[i][j] == True: + iCoords = [i] + jCoords = [j] + part_of_island(i, j, shrunk, iCoords, jCoords) + i_flocBounds_raw = ([min(iCoords) * 5 - 5,max(iCoords) * 5 + 5]) + j_flocBounds_raw = ([min(jCoords) * 5 - 5, max(jCoords) * 5 + 5]) + if (i_flocBounds_raw[0] <= 0): + i_flocBounds_raw[0] = 0 + if (i_flocBounds_raw[1] >= len(img) - 1): + i_flocBounds_raw[1] = len(img) - 1 + if (j_flocBounds_raw[0] <= 0): + j_flocBounds_raw[0] = 0 + if (j_flocBounds_raw[1] >= len(img[0]) - 1): + j_flocBounds_raw[1] = len(img[0]) - 1 + i_flocBounds.append(i_flocBounds_raw) + j_flocBounds.append(j_flocBounds_raw) + islands += 1 + + #print(i_flocBounds, j_flocBounds) + flocAreas = whitePixelAreas(i_flocBounds, j_flocBounds, img) + nonBlacks = nonBlackPixels(img) + flocAreasTotal = sum(flocAreas) + description = "" + if (abs(flocAreasTotal - nonBlacks) >= (nonBlacks/5)): + description = "There is GREATER THAN a 20% discrepancy between the sum of individual near-white pixel counts and the total count of non-black pixels! There are many non-black AND non-near-white pixels!" + else: + description = "There is LESS THAN a 20% discrepancy between the sum of individual near-white pixel counts and the total count of non-black pixels." + return islands, flocAreas, nonBlacks, description + + + +# Returns: nothing, but sets all adjacent near-white pixels to black pixels and appends the coordinates of adjacent +# near-white pixels to a list of [i] coordinates and a list of [j] coordinates +# Requires: a single [i][j] coordinate of a near-white pixel for a 3-dimensional list/picture, +# a T/F 2-dimensional list/grid, a list of [i] cooridinates for adjacent near-white pixels, +# and a list of [j] coodinates for adjacent near-white pixels. +def part_of_island(i, j, grid, iCoords, jCoords): + if i < 0 or j < 0 or i == len(grid) or j == len(grid[i]) or (grid[i][j] == False): + return + else: + grid[i][j] = False + iCoords.append(i) + jCoords.append(j) + part_of_island(i,j+1,grid, iCoords, jCoords) + part_of_island(i,j-1,grid, iCoords, jCoords) + part_of_island(i+1,j,grid, iCoords, jCoords) + part_of_island(i-1,j,grid, iCoords, jCoords) + part_of_island(i+1,j+1,grid, iCoords, jCoords) + part_of_island(i+1,j-1,grid, iCoords, jCoords) + part_of_island(i-1,j+1,grid, iCoords, jCoords) + part_of_island(i-1,j-1,grid, iCoords, jCoords) + + + +# Returns: a 2-dimensional list that is a "subgrid" of the inputted 2-dimensional list +# Requires: a min [j] and max [j] coordinate, a min [i] and max [i] coordinate, and a 2-dimensional list (a grid) +def getsubgrid(j1, i1, j2, i2, grid): + return [item[j1:j2] for item in grid[i1:i2]] + + + +# Returns: a list containing the counts of near-white pixels for every "near-white pixel island" in a picture +# Requires: a list of tuples containing the [i] coordinate bounds and a list of tuples containing +# the [j] coordinate bounds that locates each near-white pixel island for an inputted RGB 3-dimensional list/picture. +def whitePixelAreas(i_flocBounds, j_flocBounds, img): + subgrids = [] + floc = 0 + for flocBound in i_flocBounds: + i_Bounds = i_flocBounds[floc] + j_Bounds = j_flocBounds[floc] + subgrids.append(getsubgrid(j_Bounds[0], i_Bounds[0], j_Bounds[1], i_Bounds[1],img)) + floc = floc+1 + + whitePixelGrids = [] + for subgrid in subgrids: + whitePixelGrids.append(whitePixels(subgrid)) + + flocAreas = [] + for img in whitePixelGrids: + pixels = 0 + for i in range(len(img)): + for j in range(len(img[i])): + if img[i][j] == True: + pixels = pixels + 1 + flocAreas.append(pixels) + return flocAreas + + + +# Example usage +img = cv2.imread("openCV/flocs/Image 32346.jpg", 0) +processed_img = flocID(img) +print(count_and_size_flocs(processed_img)) + + + From e71c89f6d1fbc12b6768ba0a9da86863da8288bd Mon Sep 17 00:00:00 2001 From: Erika Hauschild Date: Tue, 1 Oct 2019 16:16:05 -0400 Subject: [PATCH 2/4] GUI --- application/visual.py | 133 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 3 deletions(-) diff --git a/application/visual.py b/application/visual.py index e5f6e19..63ec527 100644 --- a/application/visual.py +++ b/application/visual.py @@ -1,10 +1,137 @@ +import glob import wx +import eyed3 -Panel() -Panel(parent, id=ID_ANY, pos=DefaultPosition, size=DefaultSize, - style=TAB_TRAVERSAL, name=PanelNameStr) +# Editing the code from a tutorial +class Mp3Panel(wx.Panel): + def __init__(self, parent): + super().__init__(parent) + main_sizer = wx.BoxSizer(wx.VERTICAL) + self.row_obj_dict = {} + self.list_ctrl = wx.ListCtrl( + self, size=(-1, 100), + style=wx.LC_REPORT | wx.BORDER_SUNKEN + ) + self.list_ctrl.InsertColumn(0, 'Name', width=140) + self.list_ctrl.InsertColumn(1, 'Floc Count', width=140) + self.list_ctrl.InsertColumn(2, 'Floc Size', width=200) + main_sizer.Add(self.list_ctrl, 0, wx.ALL | wx.EXPAND, 5) + edit_button = wx.Button(self, label='Edit') + edit_button.Bind(wx.EVT_BUTTON, self.on_edit) + main_sizer.Add(edit_button, 0, wx.ALL | wx.CENTER, 5) + self.SetSizer(main_sizer) + def on_edit(self, event): + selection = self.list_ctrl.GetFocusedItem() + if selection >= 0: + mp3 = self.row_obj_dict[selection] + dlg = EditDialog(mp3) + dlg.ShowModal() + self.update_mp3_listing(self.current_folder_path) + dlg.Destroy() + def update_mp3_listing(self, folder_path): + self.current_folder_path = folder_path + self.list_ctrl.ClearAll() + self.list_ctrl.InsertColumn(0, 'Artist', width=140) + self.list_ctrl.InsertColumn(1, 'Album', width=140) + self.list_ctrl.InsertColumn(2, 'Title', width=200) + self.list_ctrl.InsertColumn(3, 'Year', width=200) + + mp3s = glob.glob(folder_path + '/*.mp3') + mp3_objects = [] + index = 0 + for mp3 in mp3s: + mp3_object = eyed3.load(mp3) + self.list_ctrl.InsertItem(index, + mp3_object.tag.artist) + self.list_ctrl.SetItem(index, 1, + mp3_object.tag.album) + self.list_ctrl.SetItem(index, 2, + mp3_object.tag.title) + mp3_objects.append(mp3_object) + self.row_obj_dict[index] = mp3_object + index += 1 + + +class Mp3Frame(wx.Frame): + + def __init__(self): + wx.Frame.__init__(self, parent=None, + title='Mp3 Tag Editor') + self.panel = Mp3Panel(self) + self.create_menu() + self.Show() + + def create_menu(self): + menu_bar = wx.MenuBar() + file_menu = wx.Menu() + open_folder_menu_item = file_menu.Append( + wx.ID_ANY, 'Open a File', + 'Open a folder with MP3s' + ) + menu_bar.Append(file_menu, '&File') + self.Bind( + event=wx.EVT_MENU, + handler=self.on_open_file, + source=open_folder_menu_item, + ) + self.SetMenuBar(menu_bar) + + def on_open_file(self, event): + # otherwise ask the user what new file to open + with wx.FileDialog(self, "Open JPG file", wildcard="*.jpg", + style=wx.FD_OPEN | wx.FD_FILE_MUST_EXIST) as fileDialog: + + if fileDialog.ShowModal() == wx.ID_CANCEL: + return # the user changed their mind + + # Proceed loading the file chosen by the user + pathname = fileDialog.GetPath() + try: + with open(pathname, 'r') as file: + # self.doLoadDataOrWhatever(file) + print(file) + except IOError: + wx.LogError("Cannot open file '%s'." % file) + + +class EditDialog(wx.Dialog): + def __init__(self, mp3): + title = f'Editing "{mp3.tag.title}"' + super().__init__(parent=None, title=title) + self.mp3 = mp3 + self.main_sizer = wx.BoxSizer(wx.VERTICAL) + self.artist = wx.TextCtrl( + self, value=self.mp3.tag.artist) + self.add_widgets('Artist', self.artist) + self.album = wx.TextCtrl( + self, value=self.mp3.tag.album) + self.add_widgets('Album', self.album) + self.title = wx.TextCtrl( + self, value=self.mp3.tag.title) + self.add_widgets('Title', self.title) + btn_sizer = wx.BoxSizer() + save_btn = wx.Button(self, label='Save') + save_btn.Bind(wx.EVT_BUTTON, self.on_save) + btn_sizer.Add(save_btn, 0, wx.ALL, 5) + btn_sizer.Add(wx.Button( + self, id=wx.ID_CANCEL), 0, wx.ALL, 5) + self.main_sizer.Add(btn_sizer, 0, wx.CENTER) + self.SetSizer(self.main_sizer) + + def on_save(self, event): + self.mp3.tag.artist = self.artist.GetValue() + self.mp3.tag.album = self.album.GetValue() + self.mp3.tag.title = self.title.GetValue() + self.mp3.tag.save() + self.Close() + + +if __name__ == '__main__': + app = wx.App(False) + frame = Mp3Frame() + app.MainLoop() From 467e67643ed3b83d039d4fc7137db89c6d19b599 Mon Sep 17 00:00:00 2001 From: Erika Hauschild Date: Tue, 1 Oct 2019 16:17:41 -0400 Subject: [PATCH 3/4] GUI --- camera/.DS_Store | Bin 6148 -> 6148 bytes openCV/.DS_Store | Bin 0 -> 6148 bytes src/pq.py | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 openCV/.DS_Store diff --git a/camera/.DS_Store b/camera/.DS_Store index 50c27e7d42157bcfb77412dd29282b4ae01178b3..d42278aa4b632e812f29f97a6fdb820430d21df5 100644 GIT binary patch literal 6148 zcmeHK&2G~`5S~p#YX>2GKx&V^AaRJQwEUzJLUPmekOLCJ2o8W+JGNR&uARmXX&Zuk z=|hw^;8A!29tXbJU8*_(jtHS0Y4)3)oo{V_Yk9pyBzlu*i^w4&3(nZE(fq=AoPEg} zIE#Ah*5qup28n|mxhRFl@ink zbOcT@NNcrU{!|-Ii;Za}vRw$-Znm~V!c+D~@ zsQ|mAGb$JOmkWG!jTr@u0!9HSpg(BVZKH-!z$jo8SWtk!4=$Y1(^x5#pAIzg2mov% zEe$dMFfzy0=xMAJVgx1>6{x5}A2EcYBk$@uPh+J}(MjmThtO9R`h+6n)iJ*-%Sm_& zO=}b|3e*)?QC)}k|NY;e|LY{vGYS|5{woDUv+wtNcqF~IE!fOfw ixfP?9x8e=BG~`_lfS$%mAu=%YBOqlkjZxsQD)0l!`HcYp delta 92 zcmZoMXfc=|#>CJzu~2NHo}wrt0|NsP3otOGG8Cs2C+8&P=jTi;RA*$I?9VK-S(HPF rWwQW_FY{)04t@@xw#|$z-K<^8~~+uel0n6h#f)*LAme{ z@Bm0W3Qxd;@Br}5>{2;taz%i4q}^{kJKt{h^LjT+M5;b+9}>AlIb7n9}ho;YO!7vKbtWx=|wzdm9yL*mPa!S`quNwVi(#ZU5oYwu( z3x0ayXZ^tVPm?6N@crOf*qJol8;_DW^TW6^5^~t?Am!!rFm5N4dNPjNsr1v(4USWE zicR-mHaj}0mc65s`}49lt5z#z@7~?x`Ml`t-#UDFc0QQCo4ucZ_$1RMg$+*Zy20P@ z8BR`&H;qvoC2O{vUQ-6D*&*7uo!&)yTBaJV9nq{ zBU)feQ-PYQ%oRgvI^v$ms~KEq)O1qj@}bPg%G^+tjE??2Nhej)Xltv0RbW|xZT;x- z`TuzH{ePKcpR58_f&WSYRXA;&)-fe>wqBYXpS2#!ISL!+Ei?)Rl{t>Z;iLEfMGS2o XJHVR3g+@eR_K$$bU@NP@A64KNFwEnm literal 0 HcmV?d00001 diff --git a/src/pq.py b/src/pq.py index 9b84b5a..3394c56 100644 --- a/src/pq.py +++ b/src/pq.py @@ -29,7 +29,7 @@ def handleOpen(self): fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "","All Files (*)", options=options) if fileName: hbox = QHBoxLayout(self) - pixmap = QPixmap("IMG_9870.jpeg") + pixmap = QPixmap("../openCV/flocs/Image 32339.jpg") pixmap_resized = pixmap.scaled(400, 600) lbl = QLabel(self) lbl.setPixmap(pixmap_resized) From 353240ec3b993e7640028f9201a07294bb1ffc5e Mon Sep 17 00:00:00 2001 From: YooNa Chang Date: Tue, 1 Oct 2019 16:19:21 -0400 Subject: [PATCH 4/4] cameraappwithwxpython --- camera/camera.py | 6 ++++-- camera/camerawithwx.py | 43 ++++++++++++++++++++++++++++++++++++++++++ src/pq.py | 2 +- 3 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 camera/camerawithwx.py diff --git a/camera/camera.py b/camera/camera.py index 5ed455a..3bbd8ee 100755 --- a/camera/camera.py +++ b/camera/camera.py @@ -1,9 +1,11 @@ from PyQt5.QtGui import * +from PyQt5.QtMultimedia import QCamera, QCameraInfo, QCameraImageCapture +from PyQt5.QtMultimediaWidgets import QCameraViewfinder from PyQt5.QtWidgets import * from PyQt5.QtCore import * from PyQt5.QtPrintSupport import * -from PyQt5.QtMultimedia import * -from PyQt5.QtMultimediaWidgets import * +# from PyQt5.QtMultimedia import * +# from PyQt5.QtMultimediaWidgets import * # import os import sys diff --git a/camera/camerawithwx.py b/camera/camerawithwx.py new file mode 100644 index 0000000..8457c56 --- /dev/null +++ b/camera/camerawithwx.py @@ -0,0 +1,43 @@ +import cv2 +import wx + + +class Camera (wx.Panel): + def __init__(self, *args, **kw): + super (Camera, self).__init__(*args, **kw) + self.setWindowTitle("Floc App") + self.initUI() + width = self.width() + height = self.height() + self.setGeometry(10, 10, 800, 400) + + self.show() + + def InitUI(self): + self.init_menuBar() + self.init_statusBar() + self.init_camera() + self.init_cameraBar() + self.init_filterSideBar() + + self.show() + + def init_menuBar(self): + mainMenu = self.menuBar() + mainMenu.setNativeMenuBar(False) + fileMenu = mainMenu.addMenu('File') + viewMenu = mainMenu.addMenu('View') + toolMenu = mainMenu.addMenu('Tools') + + self.init_fileMenu(fileMenu) + self.init_viewMenu(viewMenu) + self.init_toolMenu(toolMenu) + + def init_camera(self): + self.available_cameras = QCameraInfo.availableCameras() + if not self.available_cameras: + pass + self.viewfinder = QCameraViewfinder() + self.viewfinder.show() + self.setCentralWidget(self.viewfinder) + self.select_camera(0) \ No newline at end of file diff --git a/src/pq.py b/src/pq.py index 9b84b5a..b461070 100644 --- a/src/pq.py +++ b/src/pq.py @@ -26,7 +26,7 @@ def initUI(self): def handleOpen(self): options = QFileDialog.Options() options |= QFileDialog.DontUseNativeDialog - fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "","All Files (*)", options=options) + fileName, _ = QFileDialog.getOpenFileName(self,"QFileDialog.getOpenFileName()", "", "All Files (*)", options=options) if fileName: hbox = QHBoxLayout(self) pixmap = QPixmap("IMG_9870.jpeg")