2
2
3
3
import java .awt .Rectangle ;
4
4
import java .awt .image .BufferedImage ;
5
+ import java .util .ArrayList ;
5
6
import java .util .List ;
6
7
import org .slf4j .Logger ;
7
8
import org .slf4j .LoggerFactory ;
10
11
import tools .sctrade .companion .domain .ocr .LocatedFragment ;
11
12
import tools .sctrade .companion .domain .ocr .OcrResult ;
12
13
import tools .sctrade .companion .domain .ocr .OcrUtil ;
14
+ import tools .sctrade .companion .exceptions .UnreadableTransactionTypeException ;
13
15
import tools .sctrade .companion .utils .ImageUtil ;
14
16
15
17
public class TransactionTypeExtractor {
16
18
private final Logger logger = LoggerFactory .getLogger (TransactionTypeExtractor .class );
17
19
private static final String SHOP_INVENTORY = "shop inventory" ;
20
+ private static final String BUY = "buy" ;
21
+ private static final Object SELL = "sell" ;
18
22
19
23
private ImageWriter imageWriter ;
20
24
@@ -23,12 +27,16 @@ public TransactionTypeExtractor(ImageWriter imageWriter) {
23
27
}
24
28
25
29
public TransactionType extract (BufferedImage screenCapture , OcrResult result ) {
26
- screenCapture = cropAroundButtons (screenCapture , result );
30
+ screenCapture = ImageUtil .crop (screenCapture , new Rectangle ((screenCapture .getWidth () / 2 ), 0 ,
31
+ (screenCapture .getWidth () - (screenCapture .getWidth () / 2 )), screenCapture .getHeight ()));
32
+ screenCapture = ImageUtil .makeGreyscaleCopy (screenCapture );
33
+ var buttonAreaBoundingBox = findButtonAreaBoundingBox (screenCapture , result );
34
+ screenCapture = ImageUtil .crop (screenCapture , buttonAreaBoundingBox );
27
35
var boundingBoxes = getButtonBoundingBoxes (screenCapture );
28
36
29
37
boundingBoxes
30
38
.sort ((n , m ) -> Double .compare (m .getWidth () * m .getHeight (), n .getWidth () * n .getHeight ()));
31
- var boundingBoxesOrderedByX = boundingBoxes .subList (0 , 2 );
39
+ var boundingBoxesOrderedByX = new ArrayList <>( boundingBoxes .subList (0 , 2 ) );
32
40
boundingBoxesOrderedByX .sort ((n , m ) -> Double .compare (n .getMinX (), m .getMinX ()));
33
41
34
42
Rectangle buyRectangle = boundingBoxesOrderedByX .get (0 );
@@ -39,29 +47,33 @@ public TransactionType extract(BufferedImage screenCapture, OcrResult result) {
39
47
var sellImage = ImageUtil .crop (screenCapture , sellRectangle );
40
48
imageWriter .write (sellImage , ImageType .SELL_BUTTON );
41
49
42
- double buyButtonAreaOverSellButtonArea = (buyRectangle .getWidth () * buyRectangle .getHeight ())
43
- / (sellRectangle .getWidth () * sellRectangle .getHeight ());
44
-
45
- if (Math .abs (1 - buyButtonAreaOverSellButtonArea ) >= 0.2 ) {
46
- logger .warn ("Detected buttons are not the same size: one button may have not been captured" );
47
- return (buyButtonAreaOverSellButtonArea > 1 ) ? TransactionType .SELLS : TransactionType .BUYS ;
48
- } else {
50
+ if (buttonsAreAligned (buyRectangle , sellRectangle )) {
49
51
return extractByLuminance (buyImage , sellImage );
52
+ } else {
53
+ logger .warn ("Detected buttons are not aligned: one button may have not been captured" );
54
+ var buttonBoundingBox = getNormalizedButtonBoundingBox (buttonAreaBoundingBox , boundingBoxes );
55
+
56
+ return extractByStringPosition (result , buttonBoundingBox );
50
57
}
51
58
}
52
59
53
- private BufferedImage cropAroundButtons (BufferedImage screenCapture , OcrResult result ) {
60
+ private Rectangle findButtonAreaBoundingBox (BufferedImage screenCapture , OcrResult result ) {
54
61
Rectangle shopInventoryRectangle = getShopInventoryRectangle (result );
55
- screenCapture = ImageUtil .crop (screenCapture , new Rectangle ((screenCapture .getWidth () / 2 ), 0 ,
56
- (screenCapture .getWidth () - (screenCapture .getWidth () / 2 )), screenCapture .getHeight ()));
57
- Rectangle buttonsAreaRectangle = new Rectangle ((int ) shopInventoryRectangle .getMinX (),
62
+ Rectangle buttonAreaBoundingBox = new Rectangle ((int ) shopInventoryRectangle .getMinX (),
58
63
(int ) (shopInventoryRectangle .getMinY () - shopInventoryRectangle .getHeight ()),
59
64
(int ) (shopInventoryRectangle .getWidth () * 3 ),
60
65
(int ) (shopInventoryRectangle .getHeight () * 5 ));
61
- screenCapture = ImageUtil .crop (screenCapture , buttonsAreaRectangle );
62
- screenCapture = ImageUtil .makeGreyscaleCopy (screenCapture );
63
66
64
- return screenCapture ;
67
+ return buttonAreaBoundingBox ;
68
+ }
69
+
70
+ private Rectangle getShopInventoryRectangle (OcrResult result ) {
71
+ var fragments = result .getColumns ().parallelStream ()
72
+ .flatMap (n -> n .getFragments ().parallelStream ()).toList ();
73
+ LocatedFragment shopInventoryFragment =
74
+ OcrUtil .findFragmentClosestTo (fragments , SHOP_INVENTORY );
75
+
76
+ return shopInventoryFragment .getBoundingBox ();
65
77
}
66
78
67
79
private List <Rectangle > getButtonBoundingBoxes (BufferedImage screenCapture ) {
@@ -73,6 +85,63 @@ private List<Rectangle> getButtonBoundingBoxes(BufferedImage screenCapture) {
73
85
return boundingBoxes ;
74
86
}
75
87
88
+ private boolean buttonsAreAligned (Rectangle buyRectangle , Rectangle sellRectangle ) {
89
+ var yOverlap1 = sellRectangle .getMaxY () - buyRectangle .getMinY ();
90
+ var yOverlap2 = buyRectangle .getMaxY () - sellRectangle .getMinY ();
91
+ var yOverlap = Math .min (yOverlap1 , yOverlap2 );
92
+ var yHeight = Math .min (buyRectangle .getHeight (), sellRectangle .getHeight ());
93
+
94
+ return (yOverlap / yHeight ) > 0.66 ;
95
+ }
96
+
97
+ private Rectangle getNormalizedButtonBoundingBox (Rectangle buttonAreaBoundingBox ,
98
+ List <Rectangle > boundingBoxes ) {
99
+ var buttonBoundingBox = boundingBoxes .iterator ().next ();
100
+ buttonBoundingBox .setLocation (buttonAreaBoundingBox .x + buttonBoundingBox .x ,
101
+ buttonAreaBoundingBox .y + buttonBoundingBox .y );
102
+
103
+ return buttonBoundingBox ;
104
+ }
105
+
106
+ private TransactionType extractByStringPosition (OcrResult result , Rectangle buttonBoundingBox ) {
107
+ var buttonFragments =
108
+ result .getColumns ().parallelStream ().flatMap (n -> n .getFragments ().stream ())
109
+ .filter (n -> n .getBoundingBox ().intersectsLine (Double .MIN_VALUE ,
110
+ buttonBoundingBox .getCenterY (), Double .MAX_VALUE , buttonBoundingBox .getCenterY ()))
111
+ .toList ();
112
+
113
+ var buyFragment =
114
+ buttonFragments .parallelStream ().filter (n -> n .getText ().equals (BUY )).findFirst ();
115
+
116
+ if (buyFragment .isPresent ()) {
117
+ if (buttonBoundingBox .contains (buyFragment .get ().getBoundingBox ())) {
118
+ return TransactionType .SELLS ;
119
+ }
120
+
121
+ if (buyFragment .get ().getBoundingBox ().getMaxX () < buttonBoundingBox .getMinX ()) {
122
+ // buttonBoundingBox is the SELL button
123
+ return TransactionType .BUYS ;
124
+ }
125
+ }
126
+
127
+ var sellFragment =
128
+ buttonFragments .parallelStream ().filter (n -> n .getText ().equals (SELL )).findFirst ();
129
+
130
+ if (sellFragment .isPresent ()) {
131
+ if (buttonBoundingBox .contains (sellFragment .get ().getBoundingBox ())) {
132
+ return TransactionType .BUYS ;
133
+ }
134
+
135
+ if (buttonBoundingBox .getMaxX () < sellFragment .get ().getBoundingBox ().getMinX ()) {
136
+ // buttonBoundingBox is the BUY button
137
+ return TransactionType .SELLS ;
138
+ }
139
+ }
140
+
141
+ logger .warn ("Could not detect transaction type" );
142
+ throw new UnreadableTransactionTypeException ();
143
+ }
144
+
76
145
private TransactionType extractByLuminance (BufferedImage buyImage , BufferedImage sellImage ) {
77
146
var buyRectangleColor = ImageUtil .calculateDominantColor (buyImage );
78
147
var buyRectangleLuminance = buyRectangleColor .getRed ();
@@ -83,13 +152,4 @@ private TransactionType extractByLuminance(BufferedImage buyImage, BufferedImage
83
152
return (buyRectangleLuminance > sellRectangleLuminance ) ? TransactionType .SELLS
84
153
: TransactionType .BUYS ;
85
154
}
86
-
87
- private Rectangle getShopInventoryRectangle (OcrResult result ) {
88
- var fragments = result .getColumns ().parallelStream ()
89
- .flatMap (n -> n .getFragments ().parallelStream ()).toList ();
90
- LocatedFragment shopInventoryFragment =
91
- OcrUtil .findFragmentClosestTo (fragments , SHOP_INVENTORY );
92
-
93
- return shopInventoryFragment .getBoundingBox ();
94
- }
95
155
}
0 commit comments