-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathErrorDetector.py
6358 lines (5304 loc) · 370 KB
/
ErrorDetector.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
from IPython.display import display
import os
import random
import requests
from PIL import Image
import torch
#from transformers import AutoProcessor, LlavaOnevisionForConditionalGeneration
import numpy as np
import gc
import io
import base64
import gc
try:
from . import Projection as prj
except:
import Projection as prj
import tempfile
import shutil
import copy
import matplotlib.pyplot as plt
from concurrent.futures import ThreadPoolExecutor
import csv
BodyRegionTextSkeleton=("The image I am sending is frontal projections of one CT scan, focusing on showing the skeleton. Look at it carefully, and answer the questions below:\n"
"Q1- Which bones are on the top of the image? Which bones are on its bottom?\n"
"Q2- Which of the following landmarks are present in the image? Answer ‘yes’ or ‘no’ using the template below, substituting _ by Yes or No:\n"
"skull = _"
"neck = _"
"trachea = _"
"upper ribs = _"
"lower ribs = _"
"lumbar spine = _"
"pelvis = _"
"femurs = _\n"
"Q3- Considering these landmarks and the bones on the image top and bottom, give me a complete list of all organs (not bones) usually contained within this image limits (just list their names).\n"# In your list, carefully consider if the following organs are usually contained or not: liver, gallbladder, stomach, spleen, pancreas and kidneys. \n"
"Q4- Based on your answer to Q2 and Q3, is the %(organ)s usually present within this image limits? Answer ‘yes’ or ‘no’ using the template below, substituting _ by Yes or No:\n"
"Q4 = _\n")
AorticArchTextSkeleton=("If you answered no to Q4, skip Q5 and Q6. If you answered yes, continue:\n"
"Q5- What parts of the respiratory system are contained in the image region?\n"
"Q6- Based on your answer to Q3, is the trachea usually present in this region and in your list? Answer ‘yes’ or ‘no’ using the template below, substituting _ by Yes or No:\n"
"Q6 = _\n")
BodyRegionText=("The image I am sending is frontal projections of one CT scan. It is not a CT slice, instead, they have transparency and let you see through the entire human body, like an X-ray does. It looks like an AP (anterior-to-posterior) X-ray. Answer the questions below:\n"
"Q1- Look at the image carefully, tell me which body region it represents and which organs and bones inside this region are crossing the image's upper boundary and its lower boundary. Present a complete list of all organs usually present in the body region contained in the image (just list their names).\n"
"Q2- Based on your answer to Q1, is the %(organ)s usually present in this region and in your list? Answer ‘yes’ or ‘no’ using the template below, substituting _ by Yes or No.:\n"
"Q2 = _\n")
AorticArchText=(
"If you answered 'no' to Q2, skip Q3, Q4 and Q5. If you answered 'yes', continue.\n"
"Q3: Based on your answer to Q1, are the lungs present in the image? \n"
"Answer 'yes' or 'no' using the template below, substituting _ with Yes or No.\n"
"Q3 = _\n"
"If you answered 'no' to Q3, skip Q4 and Q5. If you answered 'yes', continue.\n"
"Q4: Repeat from you answer to Q1, which organs and bones are crossing the image upper boundary (none, brain, neck, lungs, heart or others)? \n"
"Q5: Considering your answer to Q3 and Q4, does the image include the aortic arch? The aortic arch is placed just above the heart. It is not present if you listed the heart, lower ribs, or lower lungs in Q4. It is present if you listed none, brain or neck. \n"#It is probably not present if you listed the lungs. \n"
"Answer 'yes' or 'no' using the template below, substituting _ with Yes or No.\n"
"Q5 = _\n"
)
#90% localization accuracy
DescendingAortaErrorDetectV0=('Consider the following questions, were positive answers indicate a correct annotation: '
'Point 1: Consider the bones in the image. Is the lumbar spine visible? If it is, does the red shape come down to the lumbar spine height? '
'Point 2: Is the red shape a single connected part? If you see two or more red shapes in the image, it is wrong.')
DescendingAortaErrorDetectV1="""Consider the following questions, were positive answers indicate a correct annotation:
Point 1: Does the red shape resemble a line? It does not matter if the line is curved or straight. Also, the line can have a round structure in the upper chest.
Point 2: Is the red shape a single connected part? If you see two or more red shapes in the image, it is wrong."""
#70% acc in 2 shot correct annos
DescendingAortaErrorDetect=('Consider the following questions, were positive answers indicate a correct annotation: '
'Point 1: Does the red shape resemble a line? It does not matter if the line is curved or straight. Also, the line may have a round bulge at the top, representing the aortic arch (upper chest).'
'Point 2: Is the red shape a single connected part? If you see two or more red shapes in the image, it is wrong.')
FullAortaErrorDetect=DescendingAortaErrorDetect
FullAortaErrorDetectV0=('Consider the following questions, were positive answers indicate a correct annotation: '
'Point 1: Does the red shape reach the thoracic region (high ribs), showing a curve in this area?'
'Point 2: Consider the bones in the image. Is the lumbar spine visible? If it is, does the red shape come down to the lumbar spine height? '
'Point 3: Is the red shape a single connected part? If you see two or more red shapes in the image, it is wrong. ')
PostcavaErrorDetect=('Consider the following questions, were positive answers indicate a correct annotation: '
'Point 1: Does the red shape reach the thoracic region (high ribs)?'
'Point 2: Consider the bones in the image. Is the lumbar spine visible? If it is, does the red shape come down to the lumbar spine height? '
'Point 3: Is the red shape a single connected part? If you see two or more red shapes in the image, it is wrong. ')
KidneysErrorDetectOld=("Consider the following anatomical information: A person usually has two kidneys, check if the image display one, two or more red objects, this is a very important point. "
"Each kidney has a bean-shaped structure, with a slightly concave surface facing the spine, and a clearly convex surface facing outward. Check if the red objects resemble this shape and are complete. "
" The kidneys are located on either side of the spine, at the level of the lower ribs. Check if the red objects, if a pair, are on either side of the spine and at the level of the lower ribs. \n")
LiverErrorDetect=("When evaluating the overlays, consider the following anatomical information:\n"
"a) The liver is a large organ, with triangular or wedge-like shape.\n"
"b) The liver is located in the upper right quadrant of the abdomen (left of the figure, like an AP X-ray), just below the diaphragm. It spans across the midline, partially extending into the left upper quadrant of the abdomen. The liver is not near the pelvis.\n"
"c) The liver position is primarily under the rib cage. The overlay must show red in the ribs region. \n")
KidneysErrorDetect=("When evaluating the overlays, consider the following anatomical information:\n"
"a) A person usually has two kidneys, check if the image display one, two or more red objects.\n"
"b) Each kidney has a bean-shaped structure, with a slightly concave surface facing the spine, and a clearly convex surface facing outward. Check if the red objects resemble this shape.\n"
"c) The kidneys are located on either side of the spine, at the level of the lower ribs. Check if the red objects are on either side of the spine and at the level of the lower ribs. \n")
StomachErrorDetect="""Consider the following anatomical information:
a) Shape: The shape of the stomach red overlay should resemble the letter J, an inverted letter L, a sac with a downwards curvature, or a hourglass.
b) Shape 2: The stomach red overlay should not be a random shape. It should not have many random points, nor internal gaps.
c) Unity: The stomach red overlay must be a single connected structure. Showing multiple strucutres is a major error.
d) Location: The stomach red overlay should be located mainly in the upper abdomen, starting just below the diaphragm. It lies mostly under the ribs."""
PancreasErrorDetectV0="""Consider the following anatomical information:
a) Shape: The pancreas is an elongated organ with a tadpole-like shape. The pancreas head is its thickest part and points to the left side of the image, which is the right side of the body because the image is oriented like an AP X-ray. The other side of the pancreas is thin.
b) Position: The pancreas is located in the upper abdomen, behind the stomach and near the bottom of the rib cage. The organ is mostly horizontal, but may be slightly curved and its head usually sits lower than its tail.
C) Smoothness: The pancreas is a single smooth shape and it does not have very sharp edges."""
SpleenErrorDetect="""Consider the following anatomical information:
a) Shape: The shape of the spleen red overlay should resemble an oval or crescent. It should follow the natural curve of the spleen, with no small recesses.
b) Shape 2: The spleen red overlay should not have irregular or random shapes. It should not include internal gaps or sharp, angular points.
c) Unity: The spleen red overlay must be a single, continuous structure. Multiple structures are a significant error.
d) Location: The spleen red overlay should be located in the upper left quadrant of the abdomen (right side of the image, which is oriented like an AP X-ray), slightly under the ribs and diaphragh, and adjacent to the stomach and left kidney."""
GallbladderErrorDetect="""Consider the following anatomical information:
a) Shape: The gallbladder red overlay should be pear-shaped or an elongated curved sack.
b) Shape 2: The gallbladder red overlay should be smooth. It should not have many random points.
c) Unity: The gallbladder red overlay must be a single connected structure. Showing multiple strucutres is a major error.
d) Location: The gallbladder is located in the upper right quadrant of the abdomen (left side of the figure, like an AP X-ray). It sits near the lower edge of the liver and the rib cage."""
PancreasErrorDetect="""When evaluating the overlays, consider the following anatomical information:
a) Shape: Check if the red overlay resembles the overall shape of a pancreas.
b) Head: The pancreas is the thickest on its head, which points to the right side of the human body or the left side of the image, which is oriented like an AP X-ray.
c) Position: The pancreas is located in the upper abdomen, behind the stomach and near the bottom of the rib cage.
d) Smoothness: The pancreas is a single smooth shape and it does not have very sharp edges."""
DescriptionsErrorDetect={
"aorta":FullAortaErrorDetect,
"descending aorta":DescendingAortaErrorDetect,
"postcava":PostcavaErrorDetect,
"liver":LiverErrorDetect,
"kidneys":KidneysErrorDetect,
"spleen":SpleenErrorDetect,
"stomach":StomachErrorDetect,
"pancreas":PancreasErrorDetect,
"gall_bladder":GallbladderErrorDetect}
StomachErrorDetectShapeless="""Consider the following anatomical information, you must ignore the shape of the overlay, and evaluate only the location and unity of the red overlay:
a) Unity: The stomach red overlay must be a single connected structure. Showing multiple strucutres is a major error.
b) Location: The stomach red overlay should be located mainly in the upper abdomen, starting just below the diaphragm. It lies mostly under the ribs."""
GallbladderErrorDetectShapeless="""Consider the following anatomical information, you must ignore the shape of the overlay, and evaluate only the location and unity of the red overlay:
a) Unity: The gallbladder red overlay must be a single connected structure. Showing multiple strucutres is a major error.
b) Location: The gallbladder is located in the upper right quadrant of the abdomen (left side of the figure, like an AP X-ray). It sits near the lower edge of the liver and the rib cage."""
PancreasErrorDetectShapeless="""Consider the following anatomical information, you must ignore the shape of the overlay, and evaluate only the location and unity of the red overlay:
a) Unity: The pancreas red overlay must be a single connected structure. Showing multiple strucutres is a major error.
b) Location: The pancreas is located in the upper abdomen, behind the stomach and near the bottom of the rib cage."""
DescriptionsErrorDetectShapeless={
"aorta":FullAortaErrorDetect,
"descending aorta":DescendingAortaErrorDetect,
"postcava":PostcavaErrorDetect,
"liver":LiverErrorDetect,
"kidneys":KidneysErrorDetect,
"spleen":SpleenErrorDetect,
"stomach":StomachErrorDetectShapeless,
"pancreas":PancreasErrorDetectShapeless,
"gall_bladder":GallbladderErrorDetectShapeless}
ZeroShotInstructions=("The image I am sending is a frontal projection of a CT scan. "
"It is not a CT slice, we have transparency and can see through the entire body, "
"like a X-ray. The left side of the image represents the right side of the human body. "
"The %(organ)s region in the image should be marked in red, "
"using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s. Check if the red region is coherent with "
"the expected shape and location of a %(organ)s.")
ZeroShotInstructionsShapeless=("The image I am sending is a frontal projection of a CT scan. "
"It is not a CT slice, we have transparency and can see through the entire body, "
"like a X-ray. The left side of the image represents the right side of the human body. "
"The %(organ)s region in the image should be marked in red, "
"using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s location.")
FindErrors=("The image I am sending now, we can call it Image %(number)s, is a frontal projection of a CT scan. "
"It is not a CT slice, we have transparency and can see through the entire body, "
"like a X-ray. The left side of the image represents the right side of the human body, it looks like an AP (anterior-to-posterior) X-ray. "
"The %(organ)s region in the image should be marked in red, "
"using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s. Check if the red region is coherent with "
"the expected shape and location of a %(organ)s, and analyze potential mistakes, if any.")
FindErrorsShapeless=("The image I am sending now, we can call it Image %(number)s, is a frontal projection of a CT scan. "
"It is not a CT slice, we have transparency and can see through the entire body, "
"like a X-ray. The left side of the image represents the right side of the human body, it looks like an AP (anterior-to-posterior) X-ray. "
"The %(organ)s region in the image should be marked in red, "
"using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s location.")
FindErrorsSkeleton=("The image I am sending now, we can call it Image %(number)s, is a frontal projection of a CT scan. "
"It is not a CT slice, we have transparency and can see through the entire body, "
"like a X-ray. However, I have confugured the image to mostly display the bones. "
"The left side of the image represents the right side of the human body, it looks like an AP (anterior-to-posterior) X-ray. "
"The %(organ)s region in the image should be marked in red, "
"using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s. Check if the red region is coherent with "
"the expected shape and location of a %(organ)s, and analyze potential mistakes, if any.")
SummarizeInstructions=("Summarize your last answer, using only 2 words: "
"'good annotation' or 'bad annotation'.")
SummarizeInstructionsFewShot=("Summarize your last answer, using only 2 words: "
"'good annotation' or 'bad annotation'. "
"Is the annotation for the last image I sent a 'good annotation' or a 'bad annotation'?")
CompareSummarizeED=("The text below represents an evaluation of an overlay. The overlay was a red shape over a CT scan projection, which was an image similar to an X-ray. "
"The overlay was supposed to mark the %(organ)s region, being an annotation for the organ. "
"A VLM like you analyzed the overlay and evaluated if it was a 'good annotation' or a 'bad annotation'. Its answer is the text below."
"I want you ro read the VLM's answer and tell me if the VLM considered the annotation good or bad. Answer me with only these words: 'good annotation' or 'bad annotation'. "
"The text is:\n")
OneShotFirstPart=("The images I am sending are a frontal projections of CT scans. "
"They are not CT slices, instead, they have transparency and let you see throgh "
"the entire human body, like an X-ray does. They are oriented like AP X-rays, "
"i.e., the left side of the image represents the right side of the human body. "
"The %(organ)s region in the images should "
"be marked in red, using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s. You must analyze if the red region is coherent with "
"the expected shape and location of a %(organ)s."
"However, now I am sending you just an example image, which is a 'good annotation'. "
"I will send you the image for evaluation after you see the example. "
"Take a good look and learn with it.")
OneShotFirstPartShapeless=("The images I am sending are a frontal projections of CT scans. "
"They are not CT slices, instead, they have transparency and let you see throgh "
"the entire human body, like an X-ray does. They are oriented like AP X-rays, "
"i.e., the left side of the image represents the right side of the human body. "
"The %(organ)s region in the images should "
"be marked in red, using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s location."
"However, now I am sending you just an example image, which is a 'good annotation'. "
"I will send you the image for evaluation after you see the example. "
"Take a good look and learn with it.")
FewShotFirstPart=("The images I am sending are a frontal projections of CT scans. "
"They are not CT slices, instead, they have transparency and let you see throgh "
"the entire human body, like an X-ray does. They are oriented like AP X-rays, "
"i.e., the left side of the image represents the right side of the human body. "
"The %(organ)s region in the images should "
"be marked in red, using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s. You must analyze if the red region is coherent with "
"the expected shape and location of a %(organ)s."
"However, now I am sending you just an example image, which is a 'good annotation'. "
"I will send mutiple examples in diverse prompts. "
"Take a good look and learn with the examples.")
FewShotFirstPartShapeless=("The images I am sending are a frontal projections of CT scans. "
"They are not CT slices, instead, they have transparency and let you see throgh "
"the entire human body, like an X-ray does. They are oriented like AP X-rays, "
"i.e., the left side of the image represents the right side of the human body. "
"The %(organ)s region in the images should "
"be marked in red, using an overlay. However, I am not sure if the red overlay correctly "
"or incorrectly marks the %(organ)s location."
"However, now I am sending you just an example image, which is a 'good annotation'. "
"I will send mutiple examples in diverse prompts. "
"Take a good look and learn with the examples.")
OneShotSecondPart=("I am sending you now the image for evaluaiton. Check if it has a 'good annotation' or a 'bad annotation'. "
"The example image should help you evaluate it. Think throughly and provide a detailed explanation for "
"why the last image represents a 'good annotation' or a 'bad annotation'.")
FewShotSecondPart=("I am sending you now the image for evaluaiton. Check if the red overlay on it is a 'good annotation' or a 'bad annotation'. "
"The example images should help you evaluate it. Think throughly and provide a detailed explanation for "
"why the last image represents a 'good annotation' or a 'bad annotation'.")
#OneShotInstructions=("The image I am sending are a frontal projections of a CT scans. "
# "It is not CT slices, instead, they have transparency and let you see throgh "
# "the entire human body, like an X-ray does. The left side of the image represents the right side of the human body. The %(organ)s region in the images should "
# "be marked in red, using an overlay. However, some images present correct overlays, "
# "while others present incorrect overlays. Image 1 is an example of a good %(organ)s annotation."
# "I want you to evaluate the correctness of the %(organ)s annotation in the second image. "
# "The example (Image 1) should help you evaluate it. Think throughly and provide a detailed explanation for "
# "why the last image represents a 'good annotation' or a 'bad annotation'.")
#FewShotInstructions=("The images I am sending are a frontal projections of a CT scans. "
# "They are not CT slices, instead, they have transparency and let you see throgh "
# "the entire human body, like an X-ray does. The left side of the image represents the right side of the human body. The %(organ)s region in the images should "
# "be marked in red, using an overlay. However, some images present correct overlays, "
# "while others present incorrect overlays. The first %(examples)s are examples, and "
# "I will inform you (below) if each one of them is a 'good annotation' or a 'bad annotation': \n")
#FewShotInstructionsEnd=("I want you to evaluate the correctness of the %(organ)s annotation in the last image, %(last)s. "
# "The examples should help you evaluate it. Think throughly and provide a detailed explanation for "
# "why image %(last)s represents a 'good annotation' or a 'bad annotation'.")
liver=(" Consider the following anatomical information: the liver is a large, triangular organ located in the upper right quadrant of the abdomen, "
"just below the diaphragm. It is a single structure. It spans across the midline, partially extending"
" into the left upper quadrant. "
"The liver position is primarily under the rib cage. In assessing the annotation, I want you to answer the following questions: \n"
"1. Is the red overlay a single contiguous object? \n"
"2. Does the shape of the red overlay resemble the typical triangular or wedge-like shape of the liver? \n"
"3. Is the red overlay primarily located in the upper right quadrant of the abdomen, just below the diaphragm? \n")
kidneys_questions=("Consider the following anatomical information: the kidneys are two bean-shaped organs located on either side of the spine. "
"Each kidney should appear as a distinct structure. "
"The kidneys are located around the T12 to L3 vertebrae, primarily in the upper abdomen."
"In assessing the annotation and finding potential errors on it, I want you to answer the following questions: \n"
"1. Is the red overlay divided into two distinct regions, one for each kidney? \n"
"2. Does the shape of the red overlay resemble the typical bean shape of each kidney? \n"
"3. Is the red overlay located in the correct anatomical region, on either side of the spine and close to the posterior wall? \n")
liver_no_question=(" Consider the following anatomical information: the liver is a large, triangular organ located in the upper right quadrant of the abdomen, "
"just below the diaphragm. It is a single structure. It spans across the midline, partially extending"
" into the left upper quadrant. "
"The liver position is primarily under the rib cage. \n")
liver_describe=(" Consider the following anatomical information: the liver is a large, triangular organ located in the upper right quadrant of the abdomen, "
"just below the diaphragm. It is a single structure. It spans across the midline, partially extending"
" into the left upper quadrant. "
"The liver position is primarily under the rib cage. \n"
"Throuhgly describe the overlay: what is its shape? where is it? Then you say if it corresponds to the liver or not. ")
KidneysDescriptionED=("Consider the following anatomical information: A person usually has two kidneys, check if the image display one, two or more red objects, this is a very important point. "
"Each kidney has a bean-shaped structure, with a slightly concave surface facing the spine, and a clearly convex surface facing outward. Check if the red objects resemble this shape and are complete. "
" The kidneys are located on either side of the spine, at the level of the lower ribs. Check if the red objects, if a pair, are on either side of the spine and at the level of the lower ribs. \n")
AdrenalGlandDescriptionED=("Consider the following anatomical information:\n"
"a) Number: a person usually has two adrenal glands, one on top of each kidney.\n"
"b) Location: the adrenal glands are located on the superior aspect of each kidney, in the retroperitoneal space.\n"
"c) Right Adrenal Gland Shape (right side of the body): triangular shape.\n"
"d) Left Adrenal Gland Shape (left side of the body): Generally crescent-shaped or semilunar. May appear as a curved line or elongated structure above the kidney.\n"
"e) Size: adrenal glands are relatively small compared to the kidneys.\n")
AortaDescriptionED = (
"Consider the following anatomical information:\n"
"a) Shape: The aorta should appear as a long vertical red line with a curve at the top, resembling a question mark (?). The aorta should either display this curve at the top, or extend as far as possible in the top of the image, in case the heart is not visible in the image. The aorta starts considerably higher than the diaphragm and the lung bases. Check if the the shape of the red overlay resembles this description.\n"
"b) Location: After the small curve at the top (aortic arch), the aorta should run parallel to the spine, which usually appears as a vertical line along the midline of the body. Check if the red overlay has the correct position and shape of the aorta relative to the spine.\n"
"c) Completeness: The aorta should be visible from the heart down to the pelvis (or down to the image bottom, if the pelvis is not visible). Check if the red overlay accurately shows the full length of the aorta from the heart to the pelvis."
)
DescendingAortaDescriptionED = (
"Closely follow the following instructions in evauating the image:\n"
"1- Do you see a red overlay in the image? \n"
"2- Does the red overlay resemble a long, vertical line approximatelly in the center of the body? It may be slightly curved if the spine is curved. The aortic arch is not present. \n" #
"3- Does the red line touch the top of the image? it MUST touch. This is very important, check carefully. \n"
"4- Does the red line extend as far as the end of the lumbar spine (if it is visible in the image)? \n"
)
#50 %
LiverDescriptionED=("When evaluating and comparing the overlays, consider the following anatomical information:\n"
"a) The liver is a large organ, with triangular or wedge-like shape.\n"
"b) The liver is located in the upper right quadrant of the abdomen (left of the figure, like an AP X-ray), just below the diaphragm. It spans across the midline, partially extending into the left upper quadrant of the abdomen. The liver is not near the pelvis.\n"
"c) The liver position is primarily under the rib cage. The overlay must show red in the ribs region. \n")
#"d) The liver is a single structure.\n")
SpleenDescriptionED=("When evaluating and comparing the overlays, consider the following anatomical information:\n"
"a) The spleen is an organ with an oval or bean-like shape, approximately the size of a fist.\n"
"b) The spleen is located in the upper LEFT quadrant of the abdomen. I.e., in the right side of the figure, which is oriented like an AP X-ray."
"c) The spleen is located beneath the diaphragm and behind the stomach. It lies at the level of the 9th to 11th ribs.\n"
"d) The spleen is situated near the LEFT kidney.")
StomachDescriptionED="""When evaluating and comparing the overlays, consider the following anatomical information:
a) Shape: The shape of the stomach red overlay should resemble the letter J, an inverted letter L, or a sac with a downwards curvature.
b) Shape 2: The stomach red overlay should not be a random shape. It should not have many random points, nor internal gaps.
c) Unity: The stomach red overlay should be a single connected structure. If it has multiple structures or small disconnected parts, the overlay has a big error.
d) Location: The stomach red overlay should be located mainly in the upper abdomen, starting just below the diaphragm. It lies mostly under the ribs."""
# 83% 3 rejections (10/12)
# Conservative: 100%, 10 rejections
PancreasDescriptionED="""When evaluating and comparing the overlays, consider the following anatomical information:
a) Shape: The pancreas is an elongated organ with a tadpole-like shape. The pancreas head is its thickest part and points to the left side of the image, which is the right side of the body because the image is oriented like an AP X-ray. The other side of the pancreas is thin.
b) Position: The pancreas is located in the upper abdomen, behind the stomach and near the bottom of the rib cage. The organ is mostly horizontal, but may be slightly curved and its head usually sits lower than its tail.
C) Smoothness: The pancreas is a single smooth shape and it does not have very sharp edges."""
GallbladderDescriptionED="""When evaluating and comparing the overlays, consider the following anatomical information:
a) The gallbladder is a small, pear-shaped organ.
b) The gallbladder is located in the upper right quadrant of the abdomen (left side of the figure, like an AP X-ray).
c) The gallbladder sits near the lower edge of the liver and may overlap with the liver in this frontal CT projection.
d) The gallbladder should be represented as a single red object."""
LiverDescriptionLocation=("The liver is positioned at the height of the lower ribs, spanning from about the 7th to the 11th rib on the right side. In relation to the spine, the liver typically starts around the level of the 7th or 8th thoracic vertebra (T7–T8) and extends down to around the 1st or 2nd lumbar vertebra (L1–L2). In terms of the pelvis, the liver is well above it, separated by several vertebrae and the abdominal cavity. Its lower edge does not reach as far down as the pelvis, staying contained in the upper abdomen.")
GallbladderDescriptionLocation=("It is positioned beneath the liver, close to the lower edge of the rib cage. "
"It is typically found at the level of the 9th or 10th rib."
"The gallbladder is usually present when the lower ribs are present.")
DescriptionsED={
"aorta":AortaDescriptionED,
"descending aorta":DescendingAortaDescriptionED,
"liver":LiverDescriptionED,
"kidneys":KidneysDescriptionED,
"adrenal_glands":AdrenalGlandDescriptionED,
"spleen":SpleenDescriptionED,
"stomach":StomachDescriptionED,
"pancreas":PancreasDescriptionED,
"gall_bladder":GallbladderDescriptionED}
StomachDescriptionEDShapeless="""When evaluating and comparing the overlays, consider only the overlay unity and location, do NOT consider its shape:
a) Unity: The stomach red overlay should be a single connected structure. If it has multiple structures or small disconnected parts, the overlay has a big error.
b) Location: The stomach red overlay should be located mainly in the upper abdomen, starting just below the diaphragm. It lies mostly under the ribs."""
PancreasDescriptionEDShapeless="""When evaluating and comparing the overlays, consider only the overlay unity and location, do NOT consider its shape:
a) Location: The pancreas is located in the upper abdomen, behind the stomach and near the bottom of the rib cage. The organ is mostly horizontal, but may be slightly curved and its head usually sits lower than its tail.
b) Unity: The pancreas red overlay should be a single connected structure. If it has multiple structures or small disconnected parts, the overlay has a big error."""
GallbladderDescriptionEDShapeless="""When evaluating and comparing the overlays, consider only the overlay unity and location, do NOT consider its shape:
a) Unity: The gallbladder red overlay should be a single connected structure. If it has multiple structures or small disconnected parts, the overlay has a big error.
b) Location: The gallbladder is located in the upper right quadrant of the abdomen (left side of the figure, like an AP X-ray). It sits near the lower edge of the liver and the rib cage."""
DescriptionsEDShapeless={
"aorta":AortaDescriptionED,
"descending aorta":DescendingAortaDescriptionED,
"liver":LiverDescriptionED,
"kidneys":KidneysDescriptionED,
"adrenal_glands":AdrenalGlandDescriptionED,
"spleen":SpleenDescriptionED,
"stomach":StomachDescriptionEDShapeless,
"pancreas":PancreasDescriptionEDShapeless,
"gall_bladder":GallbladderDescriptionEDShapeless}
organ_descriptions={'liver':liver}
def superpose_images(image1, image2):
# Load images using PIL
image1 = Image.open(image1)
image2 = Image.open(image2)
# Convert images to numpy arrays
image1_array = np.array(image1)
image2_array = np.array(image2)
# Check if images have 4 channels (RGBA)
if image1_array.shape[-1] == 4:
image1_array = image1_array[:, :, :3] # Drop alpha channel if present
if image2_array.shape[-1] == 4:
image2_array = image2_array[:, :, :3] # Drop alpha channel if present
# Ensure the images have 3 channels (RGB)
if image1_array.shape[-1] != 3 or image2_array.shape[-1] != 3:
raise ValueError("Both images must be RGB with 3 channels.")
# Get red, green, and blue channels from image 1 and image 2
red1 = image1_array[:, :, 0] # Red channel of image 1
green1 = image1_array[:, :, 1] # Green channel of image 1
blue1 = image1_array[:, :, 2] # Blue channel of image 1
red2 = image2_array[:, :, 0] # Red channel of image 2
green2 = image2_array[:, :, 1] # Green channel of image 2
blue2 = image2_array[:, :, 2] # Blue channel of image 2
# Annotation masks for image 1 and image 2
mask1 = (red1 != green1) & (red1 != blue1)
mask2 = (red2 != green2) & (red2 != blue2)
overlap = mask1 & mask2
#create grey image
image2_array[mask2,1] = image2_array[mask2,0]
image2_array[mask2,2] = image2_array[mask2,0]
grey=image2_array.copy()
#annotation 1 red
image2_array[mask1, 0] = grey[mask1, 0]
image2_array[mask1, 1] = 0
image2_array[mask1, 2] = 0
#annotation 2 yellow
image2_array[mask2, 0] = grey[mask2, 0]
image2_array[mask2, 1] = grey[mask2, 1]
image2_array[mask2, 2] = 0
# Annotation overlap: red + yellow = orange
image2_array[overlap, 0] = grey[overlap, 0]
image2_array[overlap, 1] = grey[overlap, 1] / 2
image2_array[overlap, 2] = 0
# Convert the modified array back to an image
result_image = Image.fromarray(image2_array)
return result_image
def SolidOverlay(image1):
if isinstance(image1, Image.Image):
image1_array = np.array(image1)
else:
image1_array=image1
# Check if images have 4 channels (RGBA)
if image1_array.shape[-1] == 4:
image1_array = image1_array[:, :, :3] # Drop alpha channel if present
# Get red, green, and blue channels from image 1 and image 2
red1 = image1_array[:, :, 0] # Red channel of image 1
green1 = image1_array[:, :, 1] # Green channel of image 1
blue1 = image1_array[:, :, 2] # Blue channel of image 1
# Annotation masks for image 1 and image 2
mask1 = (red1 != green1) & (red1 != blue1)
image1_array[mask1,0]=255
image1_array[mask1,1]=0
image1_array[mask1,2]=0
# Convert the modified array back to an image
result_image = Image.fromarray(image1_array)
return result_image
def resize_image(img, size):
# Get the original dimensions
width, height = img.size
# Determine the scaling factor based on the largest axis
if width > height:
new_width = size
new_height = int((size / width) * height)
else:
new_height = size
new_width = int((size / height) * width)
# Resize the image with the new dimensions
img = img.resize((new_width, new_height))
return img
def ZeroShot(img,processor,model,text=ZeroShotInstructions,organ='liver',size=None):
text=text%{'organ':organ}
print(text)
if isinstance(img, str):
img = Image.open(img)
if size is not None:
img=resize_image(img,size)
conversation = [
{
"role": "user",
"content": [
{"type": "image"},
{"type": "text", "text": text}
],
}
]
prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(text=prompt, images=[img],
return_tensors="pt").to(model.device).to(torch.float16)
generate_kwargs = {"max_new_tokens": 400, "do_sample": False}
output = model.generate(**inputs, **generate_kwargs)
generated_text = processor.batch_decode(output, skip_special_tokens=True)
return generated_text
def ZeroShot2Steps(img,processor,model,text=ZeroShotInstructions,
text2=SummarizeInstructions,
organ='liver',size=None):
text=text%{'organ':organ}
if isinstance(img, str):
img = Image.open(img)#.resize((224,224))
if size is not None:
img=resize_image(img,size)
generated_text=ZeroShot(img=img,text=text,processor=processor,model=model,size=size)
model_text=generated_text[0][generated_text[0].rfind('assistant\n')+len('assistant\n'):]
print('First answer:',model_text)
conversation = [
{
"role": "user",
"content": [
{"type": "text", "text": text},
{"type": "image"}, # Placeholder for the first image
],
},
{
"role": "assistant",
"content": [
{"type": "text", "text": model_text}
],
},
{
"role": "user",
"content": [
{"type": "text", "text": text2}
# No image for the second user input
],
},
]
prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(text=prompt, images=[img],
return_tensors="pt").to(model.device).to(torch.float16)
generate_kwargs = {"max_new_tokens": 20, "do_sample": False}
output = model.generate(**inputs, **generate_kwargs)
generated_text = processor.batch_decode(output, skip_special_tokens=True)
answer=generated_text[0][generated_text[0].rfind('assistant\n')+len('assistant\n'):]
print('Second answer:',answer)
if 'good annotation' in answer.lower() and ' not ' not in answer.lower():
return 1.0
elif 'bad annotation' in answer.lower() and ' not ' not in answer.lower():
return 0.0
else:
return 0.5
def OneShot(img,good_example,processor,model,text1=OneShotFirstPart,
text2=OneShotSecondPart,organ='liver',size=None):
text1=text1%{'organ':organ}
if isinstance(img, str):
img = Image.open(img)#.resize((224,224))
if isinstance(good_example, str):
good_example = Image.open(good_example)
if size is not None:
img=resize_image(img,size)
good_example=resize_image(good_example,size)
conversation = [
{
"role": "user",
"content": [
{"type": "text", "text": text1},
{"type": "image"}
],
},
{
"role": "assistant",
"content": [
{"type": "text", "text": 'Thank you for the example. The liver looks correct.'},
],
},
{
"role": "user",
"content": [
{"type": "text", "text": text2},
{"type": "image"}
],
},
]
print(conversation)
prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(text=prompt, images=[good_example,img],
return_tensors="pt").to(model.device).to(torch.float16)
generate_kwargs = {"max_new_tokens": 400, "do_sample": False}
output = model.generate(**inputs, **generate_kwargs)
generated_text = processor.batch_decode(output, skip_special_tokens=True)
return generated_text
def OneShot2Steps(img,good_example,processor,model,text1=OneShotFirstPart,
text2=OneShotSecondPart,organ='liver',size=None):
text1=text1%{'organ':organ}
if isinstance(img, str):
img = Image.open(img)#.resize((224,224))
if isinstance(good_example, str):
good_example = Image.open(good_example)
if size is not None:
img=resize_image(img,size)
good_example=resize_image(good_example,size)
generated_text=OneShot(img=img,good_example=good_example,text1=text1,processor=processor,
model=model,size=size,text2=text2)
model_text=generated_text[0][generated_text[0].rfind('assistant\n')+len('assistant\n'):]
print('First answer:',model_text)
conversation = [
{
"role": "user",
"content": [
{"type": "text", "text": text1},
{"type": "image"}
],
},
{
"role": "assistant",
"content": [
{"type": "text", "text": 'Thank you for the example. The liver looks correct.'},
],
},
{
"role": "user",
"content": [
{"type": "text", "text": text2},
{"type": "image"}
],
},
{
"role": "assistant",
"content": [
{"type": "text", "text": model_text}
],
},
{
"role": "user",
"content": [
{"type": "text", "text": SummarizeInstructions}
# No image for the second user input
],
},
]
prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(text=prompt, images=[good_example,img],
return_tensors="pt").to(model.device).to(torch.float16)
generate_kwargs = {"max_new_tokens": 20, "do_sample": False}
output = model.generate(**inputs, **generate_kwargs)
generated_text = processor.batch_decode(output, skip_special_tokens=True)
answer=generated_text[0][generated_text[0].rfind('assistant\n')+len('assistant\n'):]
print('Second answer:',answer)
if 'good annotation' in answer.lower() and ' not ' not in answer.lower():
return 1.0
elif 'bad annotation' in answer.lower() and ' not ' not in answer.lower():
return 0.0
else:
return 0.5
def FewShot(img,good_examples,bad_examples,processor,model,
text1=FewShotFirstPart+liver_no_question+' If the overlay has any clear error, consider it a bad annotation.',
text2=FewShotSecondPart+liver_no_question+' If the overlay has any clear error, consider it a bad annotation.',
organ='liver',organ_descriptions=organ_descriptions,
size=None):
if isinstance(img, str):
img = Image.open(img)#.resize((224,224))
if size is not None:
img=resize_image(img,size)
tmp=[]
for good_example in good_examples:
if isinstance(good_example, str):
good_example = Image.open(good_example)
if size is not None:
good_example=resize_image(good_example,size)
tmp.append(good_example)
good_examples=tmp
tmp=[]
for bad_example in bad_examples:
if isinstance(bad_example, str):
bad_example = Image.open(bad_example)
if size is not None:
bad_example=resize_image(bad_example,size)
tmp.append(bad_example)
bad_examples=tmp
del tmp
text1=text1%{'organ':organ}
conversation = [
{
"role": "user",
"content": [
{"type": "text", "text": text1},
{"type": "image"}
],
},
{
"role": "assistant",
"content": [
{"type": "text", "text": "Thank you for the example. The red overlay is indeed a 'good annotation' for {organ}."%{'organ':organ}},
],
}
]
last='good'
examples=[]
examples.append(good_examples[0])
good_i=1
bad_i=0
for i in range(len(good_examples)+len(bad_examples)-1):
if last=='good' and bad_i<len(bad_examples):
current='bad'
elif last=='bad' and good_i<len(good_examples):
current='good'
elif last=='good' and bad_i>=len(bad_examples):
current='good'
elif last=='bad' and good_i>=len(good_examples):
current='bad'
if current=='good':
examples.append(good_examples[good_i])
conversation.append({
"role": "user",
"content": [
{"type": "text", "text": f"This image is a 'good annotation' for {organ}."%{'organ':organ}},
{"type": "image"}
],
})
conversation.append(
{
"role": "assistant",
"content": [
{"type": "text", "text": f"Thank you for the example. The red overlay is indeed a 'good annotation' for {organ}."%{'organ':organ}},
],
})
good_i+=1
last='good'
else:
examples.append(bad_examples[bad_i])
conversation.append({
"role": "user",
"content": [
{"type": "text", "text": f"This image is a 'bad annotation' for {organ}."%{'organ':organ}},
{"type": "image"}
],
})
conversation.append(
{
"role": "assistant",
"content": [
{"type": "text", "text": f"Thank you for the example. The red overlay is indeed a 'bad annotation' for {organ}."%{'organ':organ}},
],
})
bad_i+=1
last='bad'
conversation.append({
"role": "user",
"content": [
{"type": "text", "text": text2},
{"type": "image"}
],
})
print(conversation)
prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(text=prompt, images=examples+[img],
return_tensors="pt").to(model.device).to(torch.float16)
generate_kwargs = {"max_new_tokens": 200, "do_sample": False}
output = model.generate(**inputs, **generate_kwargs)
generated_text = processor.batch_decode(output, skip_special_tokens=True)
return generated_text,conversation,examples+[img]
def FewShot2Steps(img,good_examples,bad_examples,processor,model,
text1=FewShotFirstPart+liver_no_question+' If the overlay has any clear error, consider it a bad annotation.',
text2=FewShotSecondPart+liver_no_question+' If the overlay has any clear error, consider it a bad annotation.',
organ='liver',
summarize_instructions=SummarizeInstructionsFewShot,
prt=False,size=None):
generated_text,conversation,images=FewShot(img=img,good_examples=good_examples,
bad_examples=bad_examples,text1=text1,text2=text2,
organ=organ,processor=processor,model=model,size=size)
if prt:
for image in images:
display(image)
model_text=generated_text[0][generated_text[0].rfind('assistant\n')+len('assistant\n'):]
print('First answer:',model_text)
conversation.append({
"role": "assistant",
"content": [
{"type": "text", "text": model_text}
],
})
conversation.append({ "role": "user",
"content": [
{"type": "text", "text": summarize_instructions}
],
})
prompt = processor.apply_chat_template(conversation, add_generation_prompt=True)
inputs = processor(text=prompt, images=images, return_tensors="pt").to(model.device).to(torch.float16)
generate_kwargs = {"max_new_tokens": 20, "do_sample": False}
output = model.generate(**inputs, **generate_kwargs)
generated_text = processor.batch_decode(output, skip_special_tokens=True)
answer=generated_text[0][generated_text[0].rfind('assistant\n')+len('assistant\n'):]
print('Second answer:',answer)
if 'good annotation' in answer.lower() and ' not ' not in answer.lower():
return 1.0
elif 'bad annotation' in answer.lower() and ' not ' not in answer.lower():
return 0.0
else:
return 0.5
def get_random_file_paths(folder_path, n, exclude_path,contains='overlay_axis_1'):
# Get all file paths in the specified folder
if isinstance(folder_path, list):
all_files = [file for file in folder_path
if (os.path.isfile(file) and (exclude_path not in file))]
else:
all_files = [os.path.join(folder_path, file) for file in os.listdir(folder_path)
if (os.path.isfile(os.path.join(folder_path, file)) \
and (exclude_path not in file))]
if isinstance(contains, str):
all_files = [file for file in all_files if contains in file]
elif isinstance(contains, list):
for contain in contains:
all_files = [file for file in all_files if contain in file]
# Get n random file paths from the list
random_files = random.sample(all_files, min(n, len(all_files)))
print('Example files:',random_files)
return random_files
#say people have annatomical variations and the liver must not look perfect? it you have too many FP only
def calculate_accuracy(answers_good, answers_bad):
"""
Calculates the accuracy of binary predictions given two vectors:
answers_good (should ideally be all 1s) and answers_bad (should ideally be all 0s).
Parameters:
answers_good (np.ndarray): A vector of binary predictions where the expected values are 1.
answers_bad (np.ndarray): A vector of binary predictions where the expected values are 0.
Returns:
float: The accuracy of the predictions.
"""
answers_good, answers_bad = np.array(answers_good), np.array(answers_bad)
# Combine predictions
predictions = np.concatenate((answers_good, answers_bad))
# Create ground truth vector
ground_truth = np.concatenate((np.ones_like(answers_good), np.zeros_like(answers_bad)))
# Calculate the number of correct predictions
correct_predictions = np.sum(predictions == ground_truth)
# Calculate accuracy
accuracy = correct_predictions / len(predictions)
return accuracy
def ZeroShotSystematicEval(good_annos,bad_annos,model,processor,
text=ZeroShotInstructions):
answers_good=[]
for target in good_annos:
if 'overlay_axis_1' not in target:
continue
print(target)
Image.open(target).show()
answers_good.append(ZeroShot2Steps(img=target,
model=model,processor=processor,
text=text))
print('Traget:',target,'Answer:',answers_good[-1])
answers_bad=[]
for target in bad_annos:
if 'overlay_axis_1' not in target:
continue
print(target)
Image.open(target).show()
answers_bad.append(ZeroShot2Steps(img=target,
model=model,processor=processor,
text=text))
print('Traget:',target,'Answer:',answers_bad[-1])
print('Correct good annotation: ',np.array(answers_good).sum(),'/',len(answers_good))
print('Correct bad annotation: ',(1-np.array(answers_bad)).sum(),'/',len(answers_bad))
print('Accuracy: ',calculate_accuracy(answers_good,answers_bad))