Skip to content

Commit 6beff2e

Browse files
authored
Merge pull request #16 from TomSlater/painting_segmentation
Adding manual segmentation
2 parents fc03e98 + a46a51a commit 6beff2e

21 files changed

Lines changed: 271 additions & 56 deletions
4.13 KB
Binary file not shown.
0 Bytes
Binary file not shown.
8.18 KB
Binary file not shown.

ParticleSpy/ParticleAnalysis.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def ParticleAnalysis(acquisition,parameters,particles=None,mask=np.zeros((1))):
5757
image = acquisition
5858
ac_types = 'Image only'
5959

60-
if mask.sum()==0:
60+
if mask == 'UI':
61+
labeled = label(np.load(inspect.getfile(process).rpartition('\\')[0]+'/Parameters/manual_mask.npy'))
62+
elif mask.sum()==0:
6163
labeled = process(image,parameters)
6264
#labels = np.unique(labeled).tolist() #some labeled number have been removed by "remove_small_holes" function
6365
else:

ParticleSpy/SegUI.py

Lines changed: 148 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,18 @@
66
"""
77

88
from PyQt5.QtWidgets import QCheckBox, QPushButton, QLabel, QMainWindow, QSpinBox
9-
from PyQt5.QtWidgets import QApplication, QWidget, QVBoxLayout, QComboBox
10-
from PyQt5.QtGui import QPixmap, QImage
9+
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QComboBox, QTabWidget
10+
from PyQt5.QtWidgets import QVBoxLayout
11+
from PyQt5.QtGui import QPixmap, QImage, QColor, QPainter, QBitmap
1112
from PyQt5.QtCore import Qt
1213
import sys
1314

1415
import inspect
1516
import numpy as np
16-
from skimage.segmentation import mark_boundaries
17+
from skimage.segmentation import mark_boundaries, flood_fill
1718
from skimage.util import invert
19+
import matplotlib.pyplot as plt
20+
from PIL import Image
1821

1922
from ParticleSpy.segptcls import process
2023
from ParticleSpy.ParticleAnalysis import parameters
@@ -33,47 +36,55 @@ def __init__(self,im_hs):
3336
self.prev_params.generate()
3437

3538
offset = 50
39+
40+
self.layout = QHBoxLayout(self)
41+
42+
# Initialize tab screen
43+
self.tabs = QTabWidget()
44+
self.tab1 = QWidget()
45+
self.tab2 = QWidget()
46+
47+
# Add tabs
48+
self.tabs.addTab(self.tab1,"Auto")
49+
self.tabs.addTab(self.tab2,"Manual")
3650

3751
#self.central_widget = QWidget()
3852
#self.setCentralWidget(self.central_widget)
39-
lay = QVBoxLayout()
53+
lay = QHBoxLayout()
54+
leftlay = QVBoxLayout()
55+
rightlay = QVBoxLayout()
56+
self.tab1.setLayout(lay)
4057

4158
self.label = QLabel(self)
4259
qi = QImage(self.image.data, self.image.shape[1], self.image.shape[0], self.image.shape[1], QImage.Format_Grayscale8)
4360
pixmap = QPixmap(qi)
44-
pixmap2 = pixmap.scaled(512, 512, Qt.KeepAspectRatio)
45-
self.label.setPixmap(pixmap2)
46-
self.label.setGeometry(10,10,pixmap2.width(),pixmap2.height())
61+
self.pixmap2 = pixmap.scaled(512, 512, Qt.KeepAspectRatio)
62+
self.label.setPixmap(self.pixmap2)
63+
self.label.setGeometry(10,10,self.pixmap2.width(),self.pixmap2.height())
4764

48-
height = max((pixmap2.height()+50,300 + offset)) #300 +50
65+
height = max((self.pixmap2.height()+50,300 + offset)) #300 +50
4966

50-
self.resize(pixmap2.width()+130, height)
67+
self.resize(self.pixmap2.width()+130, height)
5168

5269
self.filt_title = QLabel(self)
5370
self.filt_title.setText('Pre-filtering options')
54-
self.filt_title.move(pixmap2.width()+20, 0)
5571

5672
self.sptxt = QLabel(self)
5773
self.sptxt.setText('Rolling ball size')
58-
self.sptxt.move(pixmap2.width()+20,20)
5974

6075
self.sp = QSpinBox(self)
6176
self.sp.setMaximum(self.image.shape[0])
6277
self.sp.valueChanged.connect(self.rollingball)
63-
self.sp.move(pixmap2.width()+20, 45)
6478

6579
self.gausstxt = QLabel(self)
6680
self.gausstxt.setText('Gaussian filter kernel size')
67-
self.gausstxt.move(pixmap2.width()+20,70)
6881

6982
self.gauss = QSpinBox(self)
7083
self.gauss.setMaximum(self.image.shape[0])
7184
self.gauss.valueChanged.connect(self.gaussian)
72-
self.gauss.move(pixmap2.width()+20, 95)
7385

7486
self.thresh_title = QLabel(self)
7587
self.thresh_title.setText('Thresholding options')
76-
self.thresh_title.move(pixmap2.width()+20, 135)
7788

7889
self.comboBox = QComboBox(self)
7990
self.comboBox.addItem("Otsu")
@@ -85,65 +96,90 @@ def __init__(self,im_hs):
8596
self.comboBox.addItem("Local")
8697
self.comboBox.addItem("Local Otsu")
8798
self.comboBox.addItem("Local+Global Otsu")
88-
self.comboBox.move(pixmap2.width()+20, 160)
8999
self.comboBox.activated[str].connect(self.threshold_choice)
90100

91101
self.localtxt = QLabel(self)
92102
self.localtxt.setText('Local filter kernel')
93-
self.localtxt.move(pixmap2.width()+20,195)
94103

95104
self.local_size = QSpinBox(self)
96105
self.local_size.setMaximum(self.image.shape[0])
97106
self.local_size.valueChanged.connect(self.local)
98-
self.local_size.move(pixmap2.width()+20, 220)
99107

100108
cb = QCheckBox('Watershed', self)
101-
cb.move(pixmap2.width()+20, 260)
102109
cb.stateChanged.connect(self.changeWatershed)
103110

104111
cb2 = QCheckBox('Invert', self)
105-
cb2.move(pixmap2.width()+20, 260 + offset /2 )
106112
cb2.stateChanged.connect(self.changeInvert)
107113

108114
self.minsizetxt = QLabel(self)
109115
self.minsizetxt.setText('Min particle size (px)')
110-
self.minsizetxt.move(pixmap2.width()+20, 280+offset)
111116

112117
self.minsizev = QSpinBox(self)
113118
self.minsizev.setMaximum(self.image.shape[0]*self.image.shape[1])
114119
self.minsizev.valueChanged.connect(self.minsize)
115-
self.minsizev.move(pixmap2.width()+20, 305+offset)
116120

117121
updateb = QPushButton('Update',self)
118-
updateb.move(pixmap2.width()+20,355+offset)
119122
updateb.clicked.connect(self.update)
120123

121124
paramsb = QPushButton('Get Params',self)
122-
paramsb.move(pixmap2.width()+20,385+offset)
123125

124126
paramsb.clicked.connect(self.return_params)
125127

126128
self.imagetxt = QLabel(self)
127129
self.imagetxt.setText('Display:')
128-
self.imagetxt.move(75, pixmap2.height()+15)
129130

130131
self.imBox = QComboBox(self)
131132
self.imBox.addItem("Image")
132133
self.imBox.addItem("Labels")
133-
self.imBox.move(pixmap2.width()/2-10, pixmap2.height()+15)
134134

135135
self.imBox.activated[str].connect(self.changeIm)
136+
137+
leftlay.addWidget(self.label)
138+
leftlay.addWidget(self.imagetxt)
139+
leftlay.addWidget(self.imBox)
140+
141+
rightlay.addWidget(self.filt_title)
142+
rightlay.addWidget(self.sptxt)
143+
rightlay.addWidget(self.sp)
144+
rightlay.addWidget(self.gausstxt)
145+
rightlay.addWidget(self.gauss)
146+
rightlay.addStretch(1)
147+
rightlay.addWidget(self.thresh_title)
148+
rightlay.addWidget(self.comboBox)
149+
rightlay.addStretch(1)
150+
rightlay.addWidget(self.localtxt)
151+
rightlay.addWidget(self.local_size)
152+
rightlay.addStretch(1)
153+
rightlay.addWidget(cb)
154+
rightlay.addWidget(cb2)
155+
rightlay.addStretch(1)
156+
rightlay.addWidget(self.minsizetxt)
157+
rightlay.addWidget(self.minsizev)
158+
rightlay.addStretch(2)
159+
rightlay.addWidget(updateb)
160+
rightlay.addWidget(paramsb)
161+
162+
lay.addLayout(leftlay)
163+
lay.addLayout(rightlay)
164+
165+
self.layout.addWidget(self.tabs)
166+
self.setLayout(self.layout)
167+
168+
self.setCentralWidget(self.tabs)
169+
170+
#Tab 2
171+
self.canvas = Canvas(self.pixmap2)
172+
#self.canvas = Drawer(self.pixmap2)
173+
174+
self.getarrayb = QPushButton('Save Segmentation',self)
175+
self.getarrayb.clicked.connect(self.save_array)
176+
177+
tab2layout = QVBoxLayout()
178+
tab2layout.addWidget(self.canvas)
179+
tab2layout.addWidget(self.getarrayb)
180+
tab2layout.addStretch(1)
181+
self.tab2.setLayout(tab2layout)
136182

137-
lay.addWidget(self.thresh_title)
138-
lay.addWidget(self.filt_title)
139-
lay.addWidget(self.label)
140-
lay.addWidget(self.comboBox)
141-
lay.addWidget(self.sp)
142-
lay.addWidget(self.sptxt)
143-
lay.addWidget(self.gauss)
144-
lay.addWidget(self.gausstxt)
145-
lay.addWidget(self.imagetxt)
146-
lay.addWidget(self.minsizev)
147183
self.show()
148184

149185
def getim(self,im_hs):
@@ -179,8 +215,8 @@ def changeInvert(self, state):
179215
qi = QImage(self.image.data, self.image.shape[1], self.image.shape[0], self.image.shape[1], QImage.Format_Indexed8)
180216

181217
pixmap = QPixmap(qi)
182-
pixmap2 = pixmap.scaled(512, 512, Qt.KeepAspectRatio)
183-
self.label.setPixmap(pixmap2)
218+
self.pixmap2 = pixmap.scaled(512, 512, Qt.KeepAspectRatio)
219+
self.label.setPixmap(self.pixmap2)
184220

185221
def rollingball(self):
186222
if self.sp.value() == 1:
@@ -254,7 +290,78 @@ def threshold_choice(self):
254290
self.params.segment['threshold'] = "local_otsu"
255291
if str(self.comboBox.currentText()) == "Local+Global Otsu":
256292
self.params.segment['threshold'] = "lg_otsu"
293+
294+
def save_array(self):
295+
self.canvas.savearray(self.image)
296+
297+
298+
class Canvas(QLabel):
299+
300+
def __init__(self,pixmap):
301+
super().__init__()
302+
self.setPixmap(pixmap)
303+
304+
self.last_x, self.last_y = None, None
305+
self.pen_color = QColor(255, 0, 0, 20)
306+
307+
def set_pen_color(self, c):
308+
self.pen_color = QColor(c)
309+
310+
def mousePressEvent(self, e):
311+
if e.button() == Qt.RightButton:
312+
image = self.pixmap().toImage()
313+
b = image.bits()
314+
b.setsize(512 * 512 * 4)
315+
arr = np.frombuffer(b, np.uint8).reshape((512, 512, 4))
316+
317+
arr_test = arr[:,:,0]/arr[:,:,2]
318+
319+
painted_arr = np.zeros_like(arr[:,:,0:3])
320+
painted_arr[:,:,2][arr_test!=1] = 255
321+
322+
painted_arr[:,:,2] = flood_fill(painted_arr[:,:,2],(e.y(),e.x()),255)
323+
324+
qi = QImage(painted_arr.data, painted_arr.shape[1], painted_arr.shape[0], 3*painted_arr.shape[1], QImage.Format_RGB888)
325+
pixmap = QPixmap(qi)
326+
327+
painter = QPainter(self.pixmap())
328+
painter.setOpacity(0.01)
329+
330+
painter.drawPixmap(0, 0, pixmap)
331+
painter.end()
332+
self.update()
333+
334+
self.array = painted_arr[:,:,2]
335+
336+
def mouseMoveEvent(self, e):
337+
if e.buttons() == Qt.LeftButton:
338+
if self.last_x is None: # First event.
339+
self.last_x = e.x()
340+
self.last_y = e.y()
341+
return # Ignore the first time.
342+
343+
painter = QPainter(self.pixmap())
344+
p = painter.pen()
345+
p.setWidth(4)
346+
p.setColor(self.pen_color)
347+
painter.setPen(p)
348+
painter.drawLine(self.last_x, self.last_y, e.x(), e.y())
349+
painter.end()
350+
self.update()
257351

352+
# Update the origin for next time.
353+
self.last_x = e.x()
354+
self.last_y = e.y()
355+
356+
def mouseReleaseEvent(self, e):
357+
self.last_x = None
358+
self.last_y = None
359+
360+
def savearray(self,image):
361+
resized = np.array(Image.fromarray(self.array).resize((image.shape[0],image.shape[1])))
362+
np.save(inspect.getfile(process).rpartition('\\')[0]+'/Parameters/manual_mask',resized)
363+
364+
258365
def main(haadf):
259366

260367
ex = Application(haadf)
@@ -280,6 +387,9 @@ def SegUI(image):
280387
import hyperspy.api as hs
281388
filename = "Data/JEOL HAADF Image.dm4"
282389
haadf = hs.load(filename)
390+
391+
image_out = np.zeros_like(haadf)
392+
283393
app = QApplication(sys.argv)
284394
app.aboutToQuit.connect(app.deleteLater)
285395

6.92 KB
Binary file not shown.
10.8 KB
Binary file not shown.
177 Bytes
Binary file not shown.
711 Bytes
Binary file not shown.
3.68 KB
Binary file not shown.

0 commit comments

Comments
 (0)