From 168f42cc867925099002ed89cc2992d2fd6fd8a1 Mon Sep 17 00:00:00 2001 From: davpapp Date: Fri, 23 Feb 2018 16:43:57 -0500 Subject: [PATCH] Object tracking beginnings. Still getting a bunch of errors with OpenCV imports. --- src/DetectedObject.java | 8 +++- src/ImageCollector.java | 1 - src/IronMiner.java | 65 +++++++++++++--------------- src/ObjectDetector.java | 33 ++++++++------ src/ObjectTracker.java | 17 ++++++++ target/classes/DetectedObject.class | Bin 2134 -> 2349 bytes target/classes/ImageCollector.class | Bin 3837 -> 3837 bytes target/classes/IronMiner.class | Bin 4200 -> 3549 bytes target/classes/ObjectDetector.class | Bin 6233 -> 5822 bytes target/classes/ObjectTracker.class | Bin 0 -> 313 bytes 10 files changed, 73 insertions(+), 51 deletions(-) create mode 100644 src/ObjectTracker.java create mode 100644 target/classes/ObjectTracker.class diff --git a/src/DetectedObject.java b/src/DetectedObject.java index a3eb6b3..f1b97aa 100644 --- a/src/DetectedObject.java +++ b/src/DetectedObject.java @@ -1,6 +1,8 @@ import java.awt.Point; import java.awt.Rectangle; +import org.opencv.core.Rect2d; + public class DetectedObject { @@ -35,10 +37,14 @@ public class DetectedObject { return detectionClass; } - public Rectangle getBoundingBox() { + public Rectangle getBoundingRectangle() { return boundingBox; } + private Rect2d getBoundingRect2d() { + return new Rect2d(boundingBox.x, boundingBox.y, boundingBox.x + boundingBox.width, boundingBox.y + boundingBox.height); + } + public Point getCenterForClicking() { return new Point(boundingBox.x + boundingBox.width / 2 + Constants.GAME_WINDOW_OFFSET_X, boundingBox.y + boundingBox.height / 2 + Constants.GAME_WINDOW_OFFSET_Y); } diff --git a/src/ImageCollector.java b/src/ImageCollector.java index 9339eec..a183d55 100644 --- a/src/ImageCollector.java +++ b/src/ImageCollector.java @@ -78,7 +78,6 @@ public class ImageCollector { return screenshotOutputDirectory + itemName + "_" + counter + ".jpg"; } - private void generateInventoryImages() throws AWTException, IOException { Inventory inventory = new Inventory(); inventory.updateAndWriteAllInventoryImages(); diff --git a/src/IronMiner.java b/src/IronMiner.java index 13eaf54..433b838 100644 --- a/src/IronMiner.java +++ b/src/IronMiner.java @@ -10,10 +10,12 @@ import java.util.ArrayList; import javax.imageio.ImageIO; import org.opencv.core.Rect2d; +import org.opencv.tracking.Tracker; +import org.opencv.tracking.TrackerKCF; public class IronMiner { - public static final int IRON_ORE_MINING_TIME_MILLISECONDS = 2738; + public static final int IRON_ORE_MINING_TIME_MILLISECONDS = 1320; public static final int MAXIMUM_DISTANCE_TO_WALK_TO_IRON_ORE = 400; public static final Point GAME_WINDOW_CENTER = new Point(Constants.GAME_WINDOW_WIDTH / 2, Constants.GAME_WINDOW_HEIGHT / 2); @@ -37,15 +39,31 @@ public class IronMiner { public void run() throws Exception { while (true) { - objectDetector.update(); - ArrayList ironOres = objectDetector.getRecognizedObjectsOfClassFromImage("ironOre"); - ArrayList ores = objectDetector.getRecognizedObjectsOfClassFromImage("ore"); - System.out.println(ironOres.size() + " ironOres, " + ores.size() + " ores."); + BufferedImage screenCapture = objectDetector.captureScreenshotGameWindow(); + ArrayList detectedObjects = objectDetector.getObjectsInImage(screenCapture); + ArrayList ironOres = objectDetector.getObjectsOfClassInList(detectedObjects, "ironOre"); + + DetectedObject closestIronOre = getClosestObjectToCharacter(ironOres); + if (closestIronOre != null) { + Tracker objectTracker = TrackerKCF.create(); + objectTracker.init(screenCapture, closestIronOre.getBoundingRect2d()); + cursor.moveAndLeftClickAtCoordinatesWithRandomness(closestIronOre.getCenterForClicking(), 10, 10); + + long mineStartTime = System.currentTimeMillis(); + int maxTimeToMine = randomizer.nextGaussianWithinRange(3500, 5000); + + + while ((System.currentTimeMillis() - mineStartTime) < maxTimeToMine) { + screenCapture = objectDetector.captureScreenshotGameWindow(); + objectTracker.update(screenCapture, boundingBox); + Rectangle newBounds = new Rectangle(); + if (!objectDetector.isObjectPresentInBoundingBoxInImage(screenCapture, newBounds, "ironOre")) { + System.out.println("Lost track! Finding new ore."); + break; + } + } + } - /*for (DetectedObject ironOre : ironOres) { - ironOre.display(); - }*/ - mineClosestIronOre(ironOres, ores); dropInventoryIfFull(); } } @@ -58,36 +76,13 @@ public class IronMiner { } - private void mineClosestIronOre(ArrayList ironOres, ArrayList ores) throws Exception { + private void mineClosestIronOre(ArrayList ironOres) throws Exception { DetectedObject closestIronOre = getClosestObjectToCharacter(ironOres); - if (closestIronOre != null) { + if (closestIronOre != null) { cursor.moveAndLeftClickAtCoordinatesWithRandomness(closestIronOre.getCenterForClicking(), 10, 10); - Thread.sleep(84, 219); - DetectedObject closestOre = getClosestObjectToCharacter(ores); - if (closestOre != null) { - cursor.moveCursorToCoordinatesWithRandomness(closestOre.getCenterForClicking(), 10, 10); - } - Thread.sleep(randomizer.nextGaussianWithinRange(IRON_ORE_MINING_TIME_MILLISECONDS - 250, IRON_ORE_MINING_TIME_MILLISECONDS + -50)); + Thread.sleep(randomizer.nextGaussianWithinRange(IRON_ORE_MINING_TIME_MILLISECONDS - 350, IRON_ORE_MINING_TIME_MILLISECONDS + 1850)); } - //Thread.sleep(randomizer.nextGaussianWithinRange(150, 350)); - - //cursor.moveCursorToCoordinates(goalPoint); } - /*private void mineClosestIronOre(String filename) throws Exception { - Point ironOreLocation = getClosestIronOre(filename); - if (ironOreLocation != null) { - System.out.println("Mineable iron at (" + (ironOreLocation.x + 103) + "," + (ironOreLocation.y + 85) + ")"); - Point actualIronOreLocation = new Point(ironOreLocation.x + 103, ironOreLocation.y + 85); - Rect2d trackerBoundingBox = new Rec2d(); - //Rectangle trackerBoundingBox = new Rectangle(ironOreLocation.x - 10, ironOreLocation.x + 10, ironOreLocation.y - 10, ironOreLocation.y + 10); - //tracker.init(image, trackerBoundingBox); - cursor.moveAndLeftClickAtCoordinatesWithRandomness(actualIronOreLocation, 12, 12); - Thread.sleep(randomizer.nextGaussianWithinRange(IRON_ORE_MINING_TIME_MILLISECONDS - 350, IRON_ORE_MINING_TIME_MILLISECONDS + 150)); - } - - } - - }*/ private DetectedObject getClosestObjectToCharacter(ArrayList detectedObjects) { int closestDistanceToCharacter = Integer.MAX_VALUE; diff --git a/src/ObjectDetector.java b/src/ObjectDetector.java index b2ab4bc..d40e0d0 100644 --- a/src/ObjectDetector.java +++ b/src/ObjectDetector.java @@ -31,6 +31,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import javax.imageio.ImageIO; + import org.tensorflow.SavedModelBundle; import org.tensorflow.Tensor; import org.tensorflow.types.UInt8; @@ -42,28 +43,26 @@ import org.tensorflow.types.UInt8; public class ObjectDetector { SavedModelBundle model; - ArrayList detectedObjects; Robot robot; public ObjectDetector() throws AWTException { this.model = SavedModelBundle.load("/home/dpapp/tensorflow-1.5.0/models/raccoon_dataset/results/checkpoint_22948/saved_model/", "serve"); - this.detectedObjects = new ArrayList(); this.robot = new Robot(); } public void update() throws Exception { // TODO: eliminate IO and pass BufferedImage directly. - String fileName = "/home/dpapp/Desktop/RunescapeAI/temp/screenshot.jpg"; + /*String fileName = "/home/dpapp/Desktop/RunescapeAI/temp/screenshot.jpg"; BufferedImage image = captureScreenshotGameWindow(); ImageIO.write(image, "jpg", new File(fileName)); - this.detectedObjects = getRecognizedObjectsFromImage(fileName); + this.detectedObjects = getRecognizedObjectsFromImage(fileName);*/ } - private ArrayList getRecognizedObjectsFromImage(String fileName) throws Exception { + public ArrayList getObjectsInImage(BufferedImage image) throws Exception { List> outputs = null; ArrayList detectedObjectsInImage = new ArrayList(); - try (Tensor input = makeImageTensor(fileName)) { + try (Tensor input = makeImageTensor(image)) { outputs = model .session() @@ -92,10 +91,16 @@ public class ObjectDetector { } return detectedObjectsInImage; } + + public boolean isObjectPresentInBoundingBoxInImage(BufferedImage image, Rectangle boundingBox, String objectClass) throws Exception { + BufferedImage subImage = image.getSubimage(boundingBox.x, boundingBox.y, boundingBox.width, boundingBox.height); + ArrayList detectedObjectsInSubImage = getObjectsInImage(subImage); + return (getObjectsOfClassInList(detectedObjectsInSubImage, objectClass).size() != 0); + } - public ArrayList getRecognizedObjectsOfClassFromImage(String objectClass) { + public ArrayList getObjectsOfClassInList(ArrayList detectedObjects, String objectClass) { ArrayList detectedObjectsOfType = new ArrayList(); - for (DetectedObject detectedObject : this.detectedObjects) { + for (DetectedObject detectedObject : detectedObjects) { if (detectedObject.getDetectionClass().equals(objectClass)) { detectedObjectsOfType.add(detectedObject); } @@ -103,8 +108,8 @@ public class ObjectDetector { return detectedObjectsOfType; } - private static Tensor makeImageTensor(String filename) throws IOException { - BufferedImage img = ImageIO.read(new File(filename)); + private static Tensor makeImageTensor(BufferedImage image) throws IOException { + BufferedImage img = ImageIO.read(image); if (img.getType() != BufferedImage.TYPE_3BYTE_BGR) { throw new IOException( String.format( @@ -128,8 +133,8 @@ public class ObjectDetector { } } - private BufferedImage captureScreenshotGameWindow() throws IOException { - Rectangle area = new Rectangle(103, 85, 510, 330); - return robot.createScreenCapture(area); - } + public BufferedImage captureScreenshotGameWindow() throws IOException, AWTException { + Rectangle area = new Rectangle(Constants.GAME_WINDOW_OFFSET_X, Constants.GAME_WINDOW_OFFSET_Y, Constants.GAME_WINDOW_WIDTH, Constants.GAME_WINDOW_HEIGHT); + return robot.createScreenCapture(area); + } } \ No newline at end of file diff --git a/src/ObjectTracker.java b/src/ObjectTracker.java new file mode 100644 index 0000000..f70e79a --- /dev/null +++ b/src/ObjectTracker.java @@ -0,0 +1,17 @@ +import java.awt.Rectangle; + +import org.opencv.tracking.Tracker; +import org.opencv.tracking.TrackerKCF; + +public class ObjectTracker { + + Rectangle boundingBox; + public ObjectTracker() { + /*this.boundingBox = boundingBox; + Tracker tracker = TrackerKCF.create(); + tracker.init(image, boundingBox);*/ + } + + + +} diff --git a/target/classes/DetectedObject.class b/target/classes/DetectedObject.class index 3fffeed813edfc4ba59c4c87138c40b3a5a9b5a7..0f50857982ab31a714d2eb698f52fbf1e6717532 100644 GIT binary patch delta 927 zcmZuw+fEZv6kUhTbcS(2T4-xPv~AvFhjL^n80zYzb{45&YF!vCS5QzS0G{B;i%C4^DbT(-Y_a)5JVVleL01xS`O%omwcxboa zQ55t-SFwO3-Lv%%#gXP?771MbssTCXNNCC|Fn_{<85v6is3L1Ti~_mrv8bW`&yAXrrq!<|N^be!K==WQDVr~OgdCA43ZvBg~I;^z?= z&siSeZ7)?6F+?DnMpcA@*LWjghoF_}wpLNaTP)H)LX61tmN?>QhJcf3LJOR5paV|& zQT*U=L57>vJgjmG=g~ng*>^+@ZSc~c_82)wZ%Z!flYGShH1>I@OfVqAi%u3BFpSHr z(!}xw@MVC(LyXK_D8W_2*lsmWSaGZhqlu$>6<<&69^mGe%Dl7eZLiD;*6|I(TdWWg zZk0tiZ#DKDV)BU4A`o+lmQ2DTw@7$xI&pxz$BBCDho)~V4zMz Lq^99rrSttiFbKWb delta 708 zcmYjPU2hUW6g^WG7S;iQN{zJ^S`!N@!b_jp#DbNIwu;3TTfbOgrBq-mECu|+_~!Hc z0)K#sHAsz3^xYretN+6X^$sM&Y%=$rd*;qLcXmGbe|ohaf4+VLFpoC}T4yZmDH&GM zY}Sh9S}AF^879J!)m?pGkLfKdwr&(Gy;iCi3nFQ|)J-yYMqI`$*DM$2u*`84Q3lqQ zXoVw&sLawF*9Y~EM5`QeL^ULElj9g}G1$ZDbYzo3EtVU56`gji~-M z)3A&x#|Ac;_-+fbyW*E^+Of@X5BG&lnRn*!P#*Y5EGd(dIs*^2%GOS(Q84R9NJ9<< zjv@?UDGTl#N)qfaOodb8uQD~eOFlxn6$)!$sBm1yl8Zbp%SKb~-4nl+nRK1TyC{jY z6HUq^l5cUevB==G%&b+X3)Ju&FIBu?7#~bfHe(s8Tf`bV4e^Rx7VR0PiqH0hk3)f* zJ`emjj??f8)$z_Z4ku2)ivU$7a1N)$d&gV*G}VG4tY+9*(NPz>a|qE-jBgK2Q^}k!T`&V{DBp+uG0|!Ge0}n&U`&V{DBp+um0|!G00}n&!hA0LBhM396`OE;UX9$!4 diff --git a/target/classes/IronMiner.class b/target/classes/IronMiner.class index 3ddfe4610901e393666f018ea1ce975ec59d3447..b5189dd7dc95d903771cde2d87dfbf3f1182a835 100644 GIT binary patch delta 1558 zcmY*YTT>fl7=9L#U9#O48lYoqw_e1?@ByC?y<9TWL4Rm#}oR8#kK_ zcuK2QJb~8F6ZKTR?u1v%}lpGXxo-uhL|A8gQm@LmQi3y zHDP74x}iC`WvW@*iW@AGYggsKz!+0A%o($is&48|xJPqZ)P9z5?ny={qdHV~CbLW( zu(iY~W~+KmH7!TgvROkypc&rLBTo>ZVA&ZotWJJ-h?GO9<*4!COt1T(tHx(qAFa!%B? z%tJOM?+EiVrPV=qXCS7hP0h)>=_WT_#s3y7D zr^BwnRy3j>ZPGwSRR0CR?nr~pN*wgYO8r-cdi!dtW3V9LsFnvd>P^iMh zA|5?^6HmHad>)rgG!DA|A`U}FKe-Y^GX_0A>JsoWA6kKDaFz&&MCDmb5ryYycF6~~ z_B;dzmv$M1f6y!;LoIYk>Al4G1(&f9Bv^^MdV(+R6?&T&@lv~g4v~=mE4(V;dt};u zP1jH#@{xi?ytP}b7V9n|P%Z8a`Fh>1cX+sbTk#0Nj1c={#Q8YF^q;#Mqu7BH*h#q# z#L3+Rac7>E4q{C!SG4rteNRg%`G0|GC4_VUACRh2!mGhWihP8s;6tj4Ym~Z^q_pTM zjiB@vHb9ghNSLN4L((|Vcnh20TazW-?v19mduYRi?H*dFWdR>G)03Q{XpW+}#ipVdrzHV-2x(~y?6@p0yX)*Mi9J>; z)5keV)u@~i>4M4SD{)$4Kg|K(Xdz66f5dD2!1sbtWp(rUN4~m z*Kru@Q?weTIphoamnq(=rdFj{i&QNt4akv5nHp;Iw8=h&gVQm)Rc@Dy0&>t-RI91} zpwI2pXIRo>ZY$<^0Sh>!o#opR(Udj`H=>bwMKul?iBSGf(Hx4SQ8Yzv za|>wVF!HR6Mv|FPf55A#BD`26fPf4)JffOEP&7kTMp!})mN1X=&z<#FL^uq|@vAXI)kr}O zyAzerj+;n$IUH7kUJiv9x_n-|exhy`qJxE4#v%Jczy=(xxP_ABknR!hcy{YH2{A0^ zVAVp20&z%pT#(r9xKqF#16rP22MH?~VW$_Rsg%3F!$_D6=45qKRTxCwE?hl5rB;)h7gxyPI=Lx8T{s2RY^a4H7ouF@3jrT1s-Q-xrj%s7i&dCNvI8Q^-Fl z;VC?=YnJltPHYkI42RTNiblOW(9;s0MVEfeGJ$_y!VB1{r}1-R78)J)Qzv3Ob_jU+ zoOY>ep~mz|=*CVCmPkNR!t{2Odl>OT?3NJ6Ru1FZLhVXvRE>nxx=?8-qD47 zwU!z==nb_6l}Lmy=?ojW_>wKg8zQV_cvZq{*r%`H#}3_&{VdlTq?n`JS+D=i=cS6x zflx$=Xf(7Ot|YoneYG&s>cx8!-p4_GzmS)67#|AwfP?MKWOy`1l}SbkAK_!2x90Mn zN;rbg^zqhG>tTE$;Y+O7JFF`weqc9Ysk%@~jj^%E;?g}$Rmj;&vEU-^myWB ztBqssY_?&WM^;5QuHOrJUVa?Sz3}%SWK-kN3g|PArM8>Nj^oxiR=DyDdT^Jk2lsUK z?WCAyjHPun1X>-mIx!3`WFa4=gu{c&P*IIb2^I*L0Rq=zHhm4Fe}VU5E%k2}_1FDm zneYJZkDzzrY~A45I@)`X&XQ<{%r*K&Lvp9+1im`Q)+Bl+Bz7L%VYo6}Ho^x? zCR|e}=oJK^SYI+^c&tBI4h5sZqPThFW~S(sc$_Q~o*-1fU$D?GP~*z)!KTW>11M){ z3gg(ECZ>sTJlBwpZNqT97caR;mrccHyZHdpnUmc-=(VeXHazL*NynZz_O9%W;dO>F zpGU7QKNa1P>!$Y%tVo{!7+n* zCPh4MFn2)0*L2Do8c3{tD8^ADP8zT#a3ZM>so({00=)Ee63N5BxISt`^I5y30GnT0 zSz|Pis;rsq1-oz}pC0cjvQCn9(z~h`KdjwGhbCh*cq3`>tO>9iB=mhLy!{u#$1vIP ND_JJ8e={!r{y(kc^!xw- diff --git a/target/classes/ObjectDetector.class b/target/classes/ObjectDetector.class index 9ec4b0e9c3e0016b5bbdafaea068555c1cd0cf3b..4075ebbe4470d3e53a1675d4c7a9f9be59ac01f2 100644 GIT binary patch literal 5822 zcmbVQ3w%@69sW+6|C~Je#)A(7Sd4#ZC=(dBzGs^mbQ5Qt3J9E~!wa8=5cjYUHWlXpreVAkd6Nzeeu2~4<4JQ}W}wHkJB^-%DNxgH zMUBKJ!;Z^#uG8c6$J27#N+BV)Et6sdO$}jz3B%wh+M((`({ZPz+mdYqMjw?d&bccA z5SLw%mP~K2X`3-6&{Xfbo^j%d$O_vwcD2XT5+oHJ@xG+tWNfan`u|y@x!r}c8FNi) zTHd5#Uf%RleE~!;KEPlt)UgPQ8LR#K!H24Ny6kD9ZRuHKueqH zG;uC4rS8Q2b;mfN3Fm5P7MMI@#ST|O$8wy~8Aa)-S6?qsJ-m7}VWb&*KQ89h4tMuhL$1ya9i7-nLSjsr7YwMv zw*ra6V*p*)tl<)}(};Mv{_40C0ooi*SZOlDWq6y0x8|{?GQG3kwgzRM>DYp;0#!yV z){#l2=yh7g_KadT3&KtHn<~+Rn1*OR!wW{~Fwsk!nRGO(t}SPD5n~hZ4B%X9Jkz$5=>gv1KXdoKPySrPZ zgrg&aozxJuQoA}WfwPM>Yp&Fj3BW~*EveY(SAaTAxeGEOSvC_)?o3FfDZq&&e4 zbKayP_!M)RH=g8T0;i7=S;-J84>|?>In46ar*O)bz<@E7<5vN}1oFA!0%hH;oSd6N z|I`f1#A#(I%kN6%nZBXON(u7j(XDC@FDG>h%qkKuI5i5%lJdt`Xk@oTThft@ZAoXT z(hwdg;O17dt61S$0ayuFbvQXwEB0%xin8e=mx+{juOPrLEEh-qggowj6) z#RCfqdy6$xC|Rc}H?BSEM8n+S0f`5mA{%oxz5db)+>TFa_#_#nVA+QJ3>|mi2JX<1 zlsc&6Gpe;qwmzrh^Rnd~jK`dQ+4BV*cgY@KzZviAX9nMbyLH@yF9}pK{W~%}IY7^> zYvaFMT8$A2Ca}PLI%cCmzGZbB!eN4PxOlJcRSeLUl&}z3p39=rnmL-wqd|n~B6v{8 z5qz16osPeqbisok(OcOw9DIR^qlJ}1mO7d$gXEEAn9Ool!hns_)zi9kLvg) zzQuqg%^_~V#QX}y>@GR*J35}mcUkPXdoDw=n&yI4Iw=>f04CsjI-Zpz=`(j^j6_=6 z@dF({#E-}!{YHA7Qpr6cQ|78=f#b-jBSO5sx088Hf-dcsM@ZM8{9IO}WBHO8w<2xp zi%yOkb9*xuWXGsQCs0-?rD*thN%>o4+m`LeF9r5&Op*bt1kd}Ss5Ov^Ck%N338i>z zNtgqTp+H4vzZn`Zoqj78vP~ma*BVcl^`Uq&uj>jMG2fM3h{^_vk@D$gFg7->Cy_5!=FhfGk{-6F3_9(je4 zY~R8hat2s{1>b6;MoJ!(JEFG9(`vuvtTqPBu6QzL4GM(v&>1VX0TRq#q$z);N|!%a zN~hs(I{uDVxMN(F=YDtfm98p*MMXPSAhrByek@h=z~Ex6WF1zRpp5Q%^!qX69!iyACZ9E&56t6pJ|<%UwJyY) z*`vEF;cU!eOQ5bqU%k>dTa9pZcsbXArlKWy9?C628m2>elj|{u)5|bdE>->tUE{ZS zR;)dO2(6sIPBeN%qj&aUENJwF@59tAmSoXb^Om45iei#Qa8&Z2DC*=y{~k8}vMmK+xOhIf&|@C+NL{Eq-Ou&0$wnL-_+R%J3+ zH~Jbpr5##>x3oj8Kj4X6v!o*E^GvFE3_HrQNI!}lE%nR4q|RFFsW+Z^%Jq{rdx5=oFmx1S@i@f*&Wm;RV_G#%d^pmr3Cf|?p@IH~s(^eaI*i!tlSh48|Y&iWsMJLhir2(3n} z=6;XICjJNR60^lpejgC4MJK0MFxkzG>9@QM{jTe(Fx+1Gs?+`xLg~gSe5N9HV_7;#(!HS%we8X1cvX4?aS< zD$bNX`*K=uDrmjApmkqC>;8h){ROT2sr6%A<2D43GeDXKq2U${JsS99&t>YbOZHv% zIx1WERaNyG%8v6oF>X{*4PE@B{H^M52PtM2#V4ZTB;xt?%i5KK*HJP2R?x}NejJ~m zgTtgCc`TdbyU6T@`*0gSpXTSY{M?zv7aKj{peKua8@=J6cTN`fXYs&3cs+NP-^r;i zZpJC%+{!&Na29Wsa&Kis!BdONM)J+wTlw}79;OlU?GdKG+}j|&!kzbUb~V1r{r0L| zk)-JM+{_*s<4TDCV2%oJgo8X^av_)4zNR?x7~4{DbDq{_XYut$??KE6l1#oKsBbrO zmmYn)A&Y1BAuvZa_H@bN?+ffd{u~o1@7NTzRxdZWk4eyvdc?`yeFS|%Ik|{tQCGpq zX8f4O%bL)vTr1GTq2lhx;`6Kba;T01(@JIZKozgF+A`{!K zu^p-f)E>d@o5%N!-&if)s1|Du;m_?XQZu-_FU#$6U6dsn40a8fLSoPja&2yVYN-1k ZL>~DC|5S5c#cTYQca@d=ZR~Ya{}=E$37)Zh^qyWQAng!+tlPsmvn@W!l&*(Wfg z#Y*;sY%`v+lHGmQu27q?(~PW_i;d}cq|dAuI4hzKGb7ICl)!`*bz<6%_Jvj?lg93r zXv!vKVOz8(ZrJIhDKKX=nuZofumkGGb zE4B#uSNDg_gdMfwDGlQ&hY~|>Sh2M|wVGm-jT4PaeG@$P3@wkp_DY! zHnl6HVe&8=x7o>PyvK(rR4rvDcT$q6n5N-uf$8sHZ5`7Qr2KuB5fNBh?pQ>5&N;Qd z^VF$|cl2Q)1kTanMS+G{0^>*Vo*y$&D(U9v(BRWhK`iCWtNh_dxkFN|qX;^I(GrcD zt?5{onQS+@`snnM7AtJ@Z84Hj`JI{c*u7DLW^BuFXiE2{6STCcVPRG%rE1QTKEfIc z^BuR!Ozp6(L}*hwZl=OU!d%fzXT}ntR5)o8#l4n2Z+oK0PeqpKSc+PKGLlJ1w&f_+ z7%_8eG#;^b2?Wb4hF}+s89ipGG2Pv5Ce28*{HphXg1UL8hVun9JoOb9OU2jfXvR8$vwKW?lNq*p;?X_XfJ-$ct(amiP%)aw&Vi>ywy&G|w$g6W z$I1rzpri&$T!CrM;*bKvjVVz7zcL*qRY>LDrDGG?h`pGx!&Hj2tK=n6HR{PD0?r|> zml3&H#};fA&{Ae9#grm2g;*Ft-{!1wLkBuFTr4ngbP64p;8G$wnU2TJBopgt&zeQ- zl7zt8Mm#CIZF3T^9|po2y6C#mnPj%=h=7jpbekr##JW-R9;rq~?bE3@BvKFK)JgZW z$hK72Vg{6AMRjb)4*I#gdN9${g;qQViBTxJtv7!!k{KZ_?T&QmHF&fN^{kpV&oj6)7YouW_*wer+S$g36S!N z&UG4Yr8u3PO_IZhb$kRLC5Nz;*xgRMpPSFY$MA?#F(7TqeW=46E{{rp_jr0%ziG9iPBG6ocj+kt(x18CjZ@wi9Vvl?o+6&Zx}5 z6CK>-@>p{`lf}HzI5!DQIt}J!fGZfc3-~gSb(3ZG1@S?R1KBORO@rzX_iM$+vJc6ZElX+OVz&0kpvD3 z8uC1+P^E~A+iWAeW4)13^;pAw0<%Ud4I8=@#BvovdhQ_|5925;$NH2s*icgUVS(73 znXfOxF+8T>QAX;J^RlZt9>-USpt#vjSCwW@$d3w%ov-Wo2A*J&pm+|lm=KguBR6Hb zAwMSJNgdym;3_b$NE>}ADaTVfp2oN7yj~;Ks#vmrB*Ka;)f{I|oG=1X8@k(RW`cV7 zyzE>YVY1+31V5KeBeIrn$Tchi&G`Y9k@7S#dkQv&ds+4l-`DX2{E&uAnzA9BmpMFF z@6TLq{dfUC(ePt|smdj%smeNjiWg}t8r?Afjn(YK&jfl_t3+f*f(z$%?Y7NeSuv{wtTOGf{%Z_TR*wPCG zvd|l{#U>*~470-j9)HyEhfJ#$8cCqzPxvz{goGxKax`b9KJ~z%Wdh|dBpG4+q4h@E z)Q?y3nw0ZzPQq@KUC!$|{(*l|@UEU@O|pj_fw#P~v0{stH$C~!Ew%f%j<=-a3MtZB zGuqQjJHCLob)1y5KF)59M(kcq2(~sO3S=JC{K74~n($;19Vfb^V2*~mC=ePY+m$pD z(tMP{G5ByYnlCL1b>SC9bRj!!w)I2(tcr6Lb@iR=M%{jz5Mkn1oo8Hip6+Oj{fQ@{ zJn8CeWb$rY(Y|u+wzlSrS2HoQ=k={zyP~yqbxWH}-~4vlF^Vl3H8Clhn51Lb7TdfI zvxOHEMSvAcOrf#MoB2;xta33`7iWuUG>M~%b9Y$jTqFb*WuQ6isBGOF0?x4Lpei1N zc3~zPirSnUT=lnE>15cH7Yp)r(Cy?MD>+X>h}~kd>>QOLO;)P{r<24CBE~II!T-GD zNr1>Z9A^tTa>36hFKlX-2*w!Boy9K?DaN9NqaN-Oe9x^sg7K9l6FkQ-sl#0oXd6V? zVa%vJikTfpF#9mh<*=N?%EPGoGB>y|m(Pi03(V(%3plQ!KnqcgMVQA~-8mQ`u7l$R z!`Rm-_7?X`;b)XAYM}&+*H@R+9YsS&)e$Tk#EPwzl^m@h_mM1@a$7Amtz!bIN09e1 z$uj61mSO3}G+c-lvi2c>^=PHgE_p*DRn`hM8B1-D;<#U@^~O-v=f(Pr;;o|2BkH_W zgVst*b#JklP#o|-de6yc1HR*!67VU7OQ6wuUK^%=D%p-&OYU9?$S#)KE1ao7K0+_?{+fDo<28scOuFMwP4zF$;{ zrFk9+YM>b6hJ!a+4UfEV#ujx=(;9iQXK zN8TxX9$z5ZpT_0*BJQImFEJ+W=ctI%)Z$CHl_C2&HF$t@#gGxVjORLBzmnrBe_eC9 zKA7Wrh$DyVp&ZvkPUF8&u>CviQ- zj7q8@%|Fr)sJ~m7wdRn#6oqFX&z|pUQ4HQh;m|0@lL_JoUFXVA1$dI*GPgBk62W=~ z|G5Jgl%YF4z>s`IFh=*c&KBuYVa*n=}NnDHY40pM>YaG6dXK8%t7@1pr%(KtXjV@*8 zNz!(u@MeuMla4qBl#zfxo8)D@6B#wXCo>+6=;cAccA0gVfthumZ=pUv^4*Qnqd3t~ z@{;=)+=KY}5&ZfwF8pQyuRM-d2k_VR)r0uEzp)f=Eb(fO6m_`$8#_Ftc=Lvh9bUfw z!*>B!H`Zz6*+6L3gTgf^e9|ax6<0N?GVS2AkCMkQ85T2koJAyt*|>tG#KsavQ7v{7 ziMtpb{mNJpm6TZMG?6sLWulmo#-ApzM2rz<(IB;0AjXPunA{e`QOC diff --git a/target/classes/ObjectTracker.class b/target/classes/ObjectTracker.class new file mode 100644 index 0000000000000000000000000000000000000000..7f35f08a86a100f9699dd280588b918a7680ddab GIT binary patch literal 313 zcmY*V%TB^T6g{_8iUmXoKj2PX*oASWaRG^|MiV9Mr)2_zbtV~G^|x4=xZnf$DC22i zLnd<{=bZbP+5Y`*0Ni32ArSPRvz5-vsaN@%_7OtC=}LX7q)>K|>_|a3bCsPNyBND~ z!6Z?p%SW97{?*#38V@>%WXkA>+1mVQZb&M9eJw)gT2C1?7sd~?}pQ^0Tf??`% zRm_w(b-o9~a%nzl^}%NBAHTZF=lan!r3dVFy%5g9*?gPu9--YtQqLxG%L5B63;mH+?% literal 0 HcmV?d00001