Diff of /detectcell.py [000000] .. [1ccdee]

Switch to unified view

a b/detectcell.py
1
from PyQt5 import QtCore, QtGui, QtWidgets
2
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView,QMessageBox
3
import subprocess,shutil,os, urllib.request
4
from PyQt5.QtGui import QPixmap
5
import pandas as pd
6
import matplotlib.pyplot as plt
7
import os
8
import glob
9
import subprocess
10
import imaplib
11
from PIL import Image
12
import copy,cv2
13
import numpy as np
14
destination_folder = "cell_application/uploaded_images/"
15
if not os.path.exists(destination_folder):
16
        os.makedirs(destination_folder)
17
output_folder="cell_application/output"
18
if not os.path.exists(output_folder):
19
        os.makedirs(output_folder)
20
output_red="cell_application/outred"
21
if not os.path.exists(output_red):
22
        os.makedirs(output_red)
23
output_pl="cell_application/outpl"
24
if not os.path.exists(output_pl):
25
        os.makedirs(output_pl)
26
model_path = "./models/yolov7"  # specify your model path
27
28
if not os.path.exists(model_path):
29
    
30
    # Create the directory if it doesn't exist
31
    os.makedirs(model_path, exist_ok=True)
32
33
    # Clone the repository
34
    subprocess.check_call(['git', 'clone', 'https://github.com/WongKinYiu/yolov7', model_path])
35
36
    # Install the requirements
37
    subprocess.check_call(['pip', 'install', '-r', os.path.join(model_path, 'requirements.txt')])
38
class GraphicsView(QGraphicsView):
39
    def __init__(self, parent=None):
40
        super(GraphicsView, self).__init__(parent)
41
        self._isPanning = False
42
        self._panStartX = 0
43
        self._panStartY = 0
44
45
    def wheelEvent(self, event):
46
        factor = 1.15 if event.angleDelta().y() > 0 else 1 / 1.15
47
        self.scale(factor, factor)
48
49
    def mousePressEvent(self, event):
50
        if event.button() == QtCore.Qt.MiddleButton:
51
            self._isPanning = True
52
            self._panStartX = event.x()
53
            self._panStartY = event.y()
54
            self.setCursor(QtCore.Qt.ClosedHandCursor)
55
            event.accept()
56
        else:
57
            super(GraphicsView, self).mousePressEvent(event)
58
59
    def mouseMoveEvent(self, event):
60
        if self._isPanning:
61
            self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - (event.x() - self._panStartX))
62
            self.verticalScrollBar().setValue(self.verticalScrollBar().value() - (event.y() - self._panStartY))
63
            self._panStartX = event.x()
64
            self._panStartY = event.y()
65
            event.accept()
66
        else:
67
            super(GraphicsView, self).mouseMoveEvent(event)
68
69
    def mouseReleaseEvent(self, event):
70
        if event.button() == QtCore.Qt.MiddleButton:
71
            self._isPanning = False
72
            self.setCursor(QtCore.Qt.ArrowCursor)
73
            event.accept()
74
        else:
75
            super(GraphicsView, self).mouseReleaseEvent(event)
76
class Ui_Dialog(object):
77
    def setupUi(self, Dialog):
78
        Dialog.setObjectName("Dialog")
79
        Dialog.setMinimumSize(800, 600)
80
        Dialog.resize(1147, 840)
81
82
        # Create a vertical layout for the main window
83
        main_layout = QtWidgets.QVBoxLayout(Dialog)
84
85
        # Create a horizontal layout for the buttons
86
        button_layout = QtWidgets.QHBoxLayout()
87
        main_layout.addLayout(button_layout)
88
89
        # Create the upload button and add it to the button layout
90
        self.uploadB = QtWidgets.QPushButton("upload")
91
        self.uploadB.setObjectName("uploadB")
92
        button_layout.addWidget(self.uploadB)
93
94
        # Create a stretchable spacer and add it to the button layout
95
        button_layout.addStretch()
96
        
97
        # Create an input field and add it to the button layout
98
        self.inputField = QtWidgets.QLineEdit()
99
        self.inputField.setObjectName("inputField")
100
        button_layout.addWidget(self.inputField)
101
102
       
103
104
        # Create labels for the results and add them to the results layout
105
        # Create a horizontal layout for the results
106
        self.results_layout = QtWidgets.QHBoxLayout()
107
        main_layout.addLayout(self.results_layout)
108
109
        # Create labels for the results and add them to the results layout
110
        self.results = {'Basophil':0, 'Eosinophil':0, 'Erythroblast':0, 'Ig':0, 'Lymphocyte':0, 'Monocyte':0, 'Neutrophil':0,'White Cells':0, 'Red Cells':0, 'platelets':0}
111
        self.labels = {}  # Dictionary to store the QLabel objects
112
113
        i=0
114
        for d in self.results:
115
            result_label = QtWidgets.QLabel()
116
            result_label.setObjectName(f"result{i+1}")
117
            result_label.setText(f"{d} {self.results[d]}")  # Set some initial text
118
            self.results_layout.addWidget(result_label)
119
            self.labels[d] = result_label  # Store the QLabel object in self.labels
120
            i+=1
121
           
122
       # Create the extract_lymphocyte button and add it to the button layout
123
        self.extract_lymphocyte = QtWidgets.QPushButton("extract_lymphocyte")
124
        self.extract_lymphocyte.setObjectName("extract_lymphocyte")
125
        button_layout.addWidget(self.extract_lymphocyte)
126
127
        self.applypl = QtWidgets.QPushButton("pl")
128
        self.applypl.setObjectName("applypl")
129
        button_layout.addWidget(self.applypl)
130
131
        # Create the apply button and add it to the button layout
132
        self.applyred = QtWidgets.QPushButton("red")
133
        self.applyred.setObjectName("applyb")
134
        button_layout.addWidget(self.applyred)
135
136
        # Create the apply button and add it to the button layout
137
        self.applyb = QtWidgets.QPushButton("white")
138
        self.applyb.setObjectName("applyb")
139
        button_layout.addWidget(self.applyb)
140
        
141
      
142
143
       # Create the QGraphicsView and add it to the main layout
144
        self.graphicsView = GraphicsView()
145
        main_layout.addWidget(self.graphicsView, stretch=3)
146
147
        # Create a QGraphicsScene for the QGraphicsView
148
        self.scene = QGraphicsScene()
149
        self.graphicsView.setScene(self.scene)
150
151
        self.graphicsViewly = GraphicsView()
152
        main_layout.addWidget(self.graphicsViewly, stretch=1)
153
154
        # Create a QGraphicsScene for the second QGraphicsView
155
        self.scenely = QGraphicsScene()
156
        self.graphicsViewly.setScene(self.scenely)
157
        
158
    
159
160
        self.uploadB.clicked.connect(self.handle_upload_button_clicked)
161
        self.extract_lymphocyte.clicked.connect(self.handle_extract_lymphocyte_button_clicked)
162
        self.applyb.clicked.connect(self.handle_detect_button_clicked)
163
        self.applyred.clicked.connect(self.handle_red_button_clicked)
164
        self.applypl.clicked.connect(self.handle_pl_button_clicked)
165
    def change_image(self, image_path):
166
        # Create a QPixmap from the new image path
167
        pixmap = QtGui.QPixmap(image_path)
168
169
        # Clear the QGraphicsScene
170
        self.scene.clear()
171
172
        # Add the QPixmap to the QGraphicsScene
173
        self.scene.addPixmap(pixmap)
174
175
        # Fit the QGraphicsView to the scene's content
176
        self.graphicsView.fitInView(self.scene.itemsBoundingRect(), QtCore.Qt.KeepAspectRatio)
177
        
178
    def handle_upload_button_clicked(self):
179
        global take_lymphocyte, yolo_img_resize
180
        
181
        # Open a file dialog and get the selected image file path
182
        file_path, _ = QtWidgets.QFileDialog.getOpenFileName(None, 'Select Image')
183
        global upload_name
184
185
        if file_path:
186
            upload_name  = os.path.basename(file_path)
187
            self.change_image(file_path)
188
189
            # Load the image into a QPixmap object
190
            pixmap = QPixmap(file_path)
191
192
            # Get the width and height
193
            width = pixmap.width()
194
            height = pixmap.height()
195
            ma = max(width, height)
196
            yolo_img_resize = max((ma // 640) *640 ,640)
197
            #yolo_img_resize = max((ma // 384) *384 ,384)
198
            #yolo_img_resize = (yolo_img_resize // 32) * 32 + (yolo_img_resize % 32 != 0) * 32
199
            self.inputField.setText(str(yolo_img_resize))
200
201
            # Define the destination folders
202
            destination_folder = "cell_application/uploaded_images/"
203
204
            source = file_path
205
            destination = os.path.join(destination_folder, upload_name)
206
207
            if os.path.normpath(source) != os.path.normpath(destination):
208
                # Copy the file
209
                shutil.copy(source, destination)
210
            else:
211
                # If source and destination are the same, delete and copy
212
                if os.path.exists(destination):
213
                    os.remove(destination)
214
                    shutil.copy(source, destination)
215
            
216
            # If the image is not a png or jpg make it jpg then save it
217
            if not (file_path.lower().endswith(('.png', '.jpg' ))):
218
               
219
                im = Image.open(file_path)
220
                rgb_im = im.convert('RGB')
221
                rgb_im.save(file_path[:-4] + 'jpg')
222
            lb = ['Basophil', 'Eosinophil', 'Erythroblast', 'Ig', 'Lymphocyte', 'Monocyte', 'Neutrophil','White Cells', 'Red Cells', 'platelets']
223
            for l in lb:
224
                self.labels[l].setText(l+': 0')
225
226
            
227
228
    
229
    def handle_detect_button_clicked(self):
230
        global yolo_img_resize
231
        yolo_img_resize = int(self.inputField.text())
232
        global take_lymphocyte
233
        image_path = "cell_application/uploaded_images/"+upload_name
234
        print(upload_name)
235
       
236
        model_path="models_weights/lastv7_aug_0.978_4.pt"
237
        command = f"python ./models/yolov7/detect.py --source {image_path} --weights {model_path} --img {yolo_img_resize} --conf 0.5 --project {output_folder} --save-txt"
238
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
239
        process.wait()
240
        
241
        if os.path.exists("cell_application/output/exp"):
242
            if os.path.exists("cell_application/output/"+upload_name[0:upload_name.find('.')]):
243
                 shutil.rmtree("cell_application/output/"+upload_name[0:upload_name.find('.')])
244
            os.rename("cell_application/output/exp","cell_application/output/"+upload_name[0:upload_name.find('.')])
245
246
        image_out_path="cell_application/output/"+upload_name[0:upload_name.find('.')]+"/"+upload_name
247
        take_lymphocyte=True
248
       
249
        txt_file = glob.glob(os.path.join("cell_application/output/"+upload_name[0:upload_name.find('.')]+"/labels/", '*.txt'))[0]
250
        
251
  
252
       
253
        df = pd.read_csv(txt_file,delimiter=' ', header=None)
254
        df.columns = ['label', 'x_center', 'y_center', 'width', 'height']
255
      
256
        # Count the appearance of each label in the DataFrame
257
        label_counts = df['label'].value_counts()
258
       
259
        # Map the label numbers to their names
260
        label_names = {0: 'Basophil', 1: 'Eosinophil', 2: 'Erythroblast', 3: 'Ig', 4: 'Lymphocyte', 5: 'Monocyte', 6: 'Neutrophil'}
261
262
        # Update the results dictionary with the counts
263
        w=0
264
        for label_number, label_name in label_names.items():
265
            if label_number in label_counts:
266
                self.labels[label_name].setText(label_name+": "+str(label_counts[label_number]))
267
                w+=label_counts[label_number]
268
                
269
    
270
        self.labels['White Cells'].setText('White Cells: '+str(w))
271
       
272
      
273
        
274
   
275
        
276
        print(image_out_path)
277
        self.change_image(image_out_path)
278
    def handle_red_button_clicked(self):
279
        img_size = int(self.inputField.text())
280
        
281
        image_path = "cell_application/uploaded_images/"+upload_name
282
        
283
        model_path="models_weights/bestv7_red.pt"
284
        command = f"python ./models/yolov7/detect.py --source {image_path} --weights {model_path} --img {img_size} --conf 0.5 --project {output_red} --save-txt"
285
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
286
        process.wait()
287
        if os.path.exists("cell_application/outred/exp"):
288
            if os.path.exists("cell_application/outred/"+upload_name[0:upload_name.find('.')]):
289
                 shutil.rmtree("cell_application/outred/"+upload_name[0:upload_name.find('.')])
290
            os.rename("cell_application/outred/exp","cell_application/outred/"+upload_name[0:upload_name.find('.')])
291
        image_out_path="cell_application/outred/"+upload_name[0:upload_name.find('.')]+"/"+upload_name
292
        txt_file = glob.glob(os.path.join("cell_application/outred/"+upload_name[0:upload_name.find('.')]+"/labels/", '*.txt'))[0]
293
        df = pd.read_csv(txt_file,delimiter=' ', header=None)
294
        df.columns = ['label', 'x_center', 'y_center', 'width', 'height']
295
       
296
        
297
    
298
        self.labels['Red Cells'].setText('Red Cells: '+str(len(df)))
299
       
300
      
301
     
302
        
303
        self.change_image(image_out_path)
304
    def handle_pl_button_clicked(self):
305
        img_size = int(self.inputField.text())
306
        
307
        image_path = "cell_application/uploaded_images/"+upload_name
308
        print(upload_name)
309
        model_path="models_weights/bestv7_pl_640_3.pt"
310
        command = f"python ./models/yolov7/detect.py --source {image_path} --weights {model_path} --img {img_size} --conf 0.5 --project {output_pl} --save-txt"
311
        process = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
312
        process.wait()
313
        if os.path.exists("cell_application/outpl/exp"):
314
            if os.path.exists("cell_application/outpl/"+upload_name[0:upload_name.find('.')]):
315
                 shutil.rmtree("cell_application/outpl/"+upload_name[0:upload_name.find('.')])
316
            os.rename("cell_application/outpl/exp","cell_application/outpl/"+upload_name[0:upload_name.find('.')])
317
        image_out_path="cell_application/outpl/"+upload_name[0:upload_name.find('.')]+"/"+upload_name
318
        txt_file = glob.glob(os.path.join("cell_application/outpl/"+upload_name[0:upload_name.find('.')]+"/labels/", '*.txt'))[0]
319
        df = pd.read_csv(txt_file,delimiter=' ', header=None)
320
        df.columns = ['label', 'x_center', 'y_center', 'width', 'height']
321
       
322
        
323
    
324
        self.labels['platelets'].setText('platelets: '+str(len(df)))
325
       
326
      
327
        
328
        
329
        self.change_image(image_out_path)
330
    
331
332
    def handle_extract_lymphocyte_button_clicked(self):
333
            global take_lymphocyte
334
                
335
                
336
337
            def get_most_recently_modified_folder(rootDir):
338
                # Create a list to store tuples of (folder_path, last_modified_time)
339
                folders = [(os.path.join(rootDir, dir), os.path.getmtime(os.path.join(rootDir, dir)))
340
                        for dir in os.listdir(rootDir) if os.path.isdir(os.path.join(rootDir, dir))]
341
342
                # Sort the list by last_modified_time
343
                folders.sort(key=lambda x: x[1], reverse=True)
344
345
                # The first element in the list is the most recently modified folder
346
                most_recently_modified_folder = folders[0][0] if folders else None
347
               
348
                return most_recently_modified_folder
349
350
            if not take_lymphocyte:
351
                msg = QMessageBox()
352
                msg.setIcon(QMessageBox.Information)
353
                msg.setText("You should make a detection for white blood cell first press White button.")
354
                msg.setWindowTitle("Information")
355
                msg.exec_()
356
                
357
               
358
                
359
            
360
            else :
361
                main_folder_path = "cell_application/output"
362
                lastfolder_path=get_most_recently_modified_folder(main_folder_path)
363
            
364
                
365
                txt_file = glob.glob(os.path.join(lastfolder_path+"/labels/", '*.txt'))[0]
366
                
367
                df = pd.read_csv(txt_file,delimiter=' ', header=None)
368
                df.columns = ['label', 'x_center', 'y_center', 'width', 'height']
369
                df=df[df['label']==4]
370
                imagename=txt_file[txt_file.rfind('\\')+1:txt_file.find('.')]
371
                imagepath=os.path.join("cell_application/uploaded_images",imagename)
372
                
373
                image = Image.open(imagepath+".jpg")
374
                npimage=np.array(image)
375
                # Get the original size of the image
376
                ma=max(npimage.shape[0],npimage.shape[1])
377
                yolo_img_resize= (ma*2144) //2592
378
                yolo_img_resize=(yolo_img_resize//32)*32 +(yolo_img_resize%32!=0)*32
379
            
380
                height, width = yolo_img_resize,yolo_img_resize
381
                height_i, width_i=npimage.shape[:2]
382
            
383
                height_scale = height_i/height
384
                width_scale=width_i/width
385
                df['x_center'] *= width_i
386
                df['y_center'] *= height_i
387
                df['width'] *= width_i
388
                df['height'] *= height_i
389
                    
390
                # Calculate the top-left and bottom-right corners of the bounding boxes
391
                df1 = pd.DataFrame()
392
                df1['xmin'] = df['x_center'] - df['width'] / 2
393
                df1['ymin'] = df['y_center'] - df['height'] / 2
394
                df1['xmax'] = df['x_center'] + df['width'] / 2
395
                df1['ymax'] = df['y_center'] + df['height'] / 2
396
                
397
                new_box=[]
398
                crop_dir = lastfolder_path+"/crop_results"
399
                sam_dir = lastfolder_path+"/sam_results"
400
401
                # Check if the directory already exists
402
                if not os.path.exists(crop_dir):
403
                    # If not, create the directory
404
                    os.mkdir(crop_dir)
405
                if not os.path.exists(sam_dir):
406
                    # If not, create the directory
407
                    os.mkdir(sam_dir)
408
                boxes = [np.array(row) for row in df1.values]
409
                for i in range(len(boxes)):
410
                    box =copy.copy(boxes[i])
411
                    padding = 70
412
                    box[0] = max(0, box[0] - padding)
413
                    box[1] = max(0, box[1] - padding)
414
                    box[2] = min(image.width, box[2] + padding)
415
                    box[3] = min(image.height, box[3] + padding)
416
417
                    # Crop the image using the bounding box coordinates
418
                    cropped_image1 = np.array(image.crop(boxes[i]))
419
                    cropped_image = image.crop(box)
420
                    cropped_image.save(f'{lastfolder_path}/crop_results/{imagename}{i}.jpg')
421
                    # Save the cropped image
422
                    # Calculate the new bounding box coordinates
423
                    new_box.append(np.array([ padding,  padding,  padding+cropped_image1.shape[1],   padding+cropped_image1.shape[0]]))
424
                box = new_box
425
                import torch
426
                CHECKPOINT_PATH="models_weights/sam_vit_h_4b8939.pth"
427
                DEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
428
                MODEL_TYPE = "vit_h"
429
                import cv2
430
                    
431
                from segment_anything import sam_model_registry, SamAutomaticMaskGenerator, SamPredictor
432
                sam = sam_model_registry[MODEL_TYPE](checkpoint=CHECKPOINT_PATH)
433
                mask_predictor = SamPredictor(sam)
434
                mask_generator = SamAutomaticMaskGenerator(sam)
435
                from keras.models import load_model
436
                import cv2
437
                import supervision as sv
438
                # Load the model from the .h5 file
439
                model = load_model('models_weights/EfficientNetB3-leukemia-0.96.h5')
440
                i=-1
441
                image_list=[]
442
                cropimagespath=lastfolder_path+"/crop_results"
443
                for file in os.listdir(cropimagespath):
444
                
445
                    i+=1
446
                    image_bgr = cv2.imread(os.path.join(cropimagespath,file))
447
                    
448
449
                    image_rgb = cv2.cvtColor(image_bgr, cv2.COLOR_BGR2RGB)
450
451
                    mask_predictor.set_image(image_rgb)
452
453
                    masks, scores, logits = mask_predictor.predict(
454
                        box=new_box[i],
455
                        multimask_output=True
456
                    )
457
                        
458
459
                    img=np.zeros(image_bgr.shape)
460
461
                    img[:,:,:]=[0,0,0]
462
                    img[masks[2,:,:]]=image_bgr[masks[2,:,:]]
463
                    #imgtostore=img.copy()
464
                    #cv2.resize(imgtostore,(300,300))
465
                    cv2.imwrite(f'{sam_dir}/{imagename}{i}.jpg',img)
466
                
467
                    annotated_image = img.astype(np.uint8)
468
                
469
                
470
                    
471
                    """sam_result = mask_generator.generate(img)
472
                    mask_annotator = sv.MaskAnnotator()
473
                    
474
                    detections = sv.Detections.from_sam(sam_result=sam_result)
475
476
                    annotated_image = mask_annotator.annotate(scene=img.copy(), detections=detections)
477
                    print("ok2")"""
478
                    
479
                
480
481
                        
482
                    img_size_ly = (300, 300)
483
                    
484
                    def evaluate(model ,img):
485
                        
486
487
                        img = cv2.resize(img, img_size_ly)
488
                        
489
490
                        img = np.expand_dims(img, axis=0)
491
                        
492
                        # now predict the image
493
                        pred = model.predict(img,verbose=0)
494
                        print('the shape of prediction is ', pred.shape)
495
                        print(pred)
496
                
497
                        index = np.argmax(pred[0])
498
499
                        return index
500
                    image_list.append([(evaluate(model,annotated_image)),f'{sam_dir}/{imagename}{i}.jpg'])
501
            
502
                print(image_list)
503
                x_offset = 0 
504
                label_dict = {0: "all", 1: "hem"}
505
                for image_info in image_list:
506
                    label_number, image_path = image_info
507
                    label = label_dict[label_number]
508
                    pixmap = QtGui.QPixmap(image_path)
509
510
                    # Scale the pixmap without preserving aspect ratio
511
                    scaled_pixmap = pixmap.scaled(300, 300)
512
513
                    item = QtWidgets.QGraphicsPixmapItem(scaled_pixmap)
514
                    item.setPos(x_offset, 0)
515
                    self.scenely.addItem(item)
516
517
                    # Create a QGraphicsTextItem for the label
518
                    text_item = QtWidgets.QGraphicsTextItem(label)
519
520
                    # Set the color of the text to white
521
                    text_item.setDefaultTextColor(QtGui.QColor('white'))
522
523
                    # Calculate the center position of the image for x-coordinate
524
                    center_x = x_offset + scaled_pixmap.width() / 2 - text_item.boundingRect().width() / 2
525
526
                    # Set the position of the text item on the image and centered horizontally
527
                    text_item.setPos(center_x, scaled_pixmap.height() / 2)
528
                    
529
                    self.scenely.addItem(text_item)
530
531
                    # Update x offset for next image
532
                    x_offset += scaled_pixmap.width()
533
534
                
535
536
                
537
538
539
540
541
                 
542
                 
543
                 
544
    
545
            
546
      
547
548
549
        
550
551
552
553
if __name__ == "__main__":
554
    import sys
555
    upload_name = ""
556
    yolo_img_resize=1216
557
    lastfolder_path=""
558
    take_lymphocyte=False
559
    app = QtWidgets.QApplication(sys.argv)
560
    Dialog = QtWidgets.QDialog()
561
    ui = Ui_Dialog()
562
    ui.setupUi(Dialog)
563
    
564
    # Add a maximize button to the window
565
    Dialog.setWindowFlag(QtCore.Qt.WindowMaximizeButtonHint, True)
566
567
    Dialog.show()
568
    sys.exit(app.exec_())