image.magic-wand.test.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. const { doDrawingAction, hasKonvaPixelColorAtPoint, setKonvaLayersOpacity, serialize } = require("./helpers");
  2. const assert = require("assert");
  3. Feature("Test Image Magic Wand");
  4. const CLOUDSHADOW = {
  5. color: "#1EAE3B",
  6. rgbArray: [30, 174, 59],
  7. };
  8. const HAZE = {
  9. color: "#68eee5",
  10. rgbArray: [104, 238, 229],
  11. };
  12. const CLOUD = {
  13. color: "#1b32de",
  14. rgbArray: [27, 50, 222],
  15. };
  16. const config = `
  17. <View>
  18. <Labels name="labels_1" toName="image_1">
  19. <Label hotkey="1" value="Snow" background="#F61E33" />
  20. <Label hotkey="2" value="Cloud Shadow" background="${CLOUDSHADOW.color}" />
  21. <Label hotkey="3" value="Haze" background="${HAZE.color}" />
  22. <Label hotkey="4" value="Cloud" background="${CLOUD.color}" />
  23. <Label hotkey="5" value="Exclude" background="#2C2C2B" />
  24. </Labels>
  25. <MagicWand name="magicwand_1" toName="image_1" />
  26. <View style="width: 100%; margin-bottom: 20%; margin-side: 10%;">
  27. <Image name="image_1" value="$image" zoomControl="true" zoom="true" crossOrigin="anonymous" />
  28. </View>
  29. </View>`;
  30. const annotationEmpty = {
  31. id: "1000",
  32. result: [],
  33. };
  34. const data = {
  35. image: [
  36. "http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_scale_1_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg",
  37. "http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_scale_2_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg",
  38. "http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_scale_3_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg",
  39. "http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_false_color_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg",
  40. ],
  41. thumb:
  42. "http://htx-pub.s3.amazonaws.com/samples/magicwand/magic_wand_thumbnail_20200902_015806_26_2235_1B_AnalyticMS_00750_00750.jpg",
  43. };
  44. async function assertMagicWandPixel(I, x, y, assertValue, rgbArray, msg) {
  45. const hasPixel = await I.executeScript(hasKonvaPixelColorAtPoint, [x, y, rgbArray, 1]);
  46. assert.equal(hasPixel, assertValue, msg);
  47. }
  48. Scenario(
  49. "Make sure the magic wand works in a variety of scenarios",
  50. async ({ I, LabelStudio, AtImageView, AtOutliner, AtPanels }) => {
  51. const AtDetailsPanel = AtPanels.usePanel(AtPanels.PANEL.DETAILS);
  52. const params = {
  53. config,
  54. data,
  55. annotations: [annotationEmpty],
  56. };
  57. I.amOnPage("/");
  58. LabelStudio.init(params);
  59. AtDetailsPanel.collapsePanel();
  60. LabelStudio.waitForObjectsReady();
  61. await AtImageView.lookForStage();
  62. I.say("Making sure magic wand button is present");
  63. I.seeElement('.lsf-toolbar__group button[aria-label="magicwand"]');
  64. I.say("Making sure Eraser button is present");
  65. I.seeElement('.lsf-toolbar__group button[aria-label="eraser"]');
  66. I.say("Select magic wand & cloud class");
  67. I.pressKey("W");
  68. I.pressKey("4");
  69. AtOutliner.seeRegions(0);
  70. I.say("Magic wanding clouds with cloud class in upper left of image");
  71. await doDrawingAction(I, { msg: "Fill in clouds upper left", fromX: 454, fromY: 184, toX: 650, toY: 644 });
  72. I.waitTicks(2);
  73. await doDrawingAction(I, { msg: "Fill in clouds lower left", fromX: 454, fromY: 834, toX: 650, toY: 644 });
  74. I.waitTicks(2);
  75. I.say("Ensuring repeated magic wands back to back with same class collapsed into single region");
  76. AtOutliner.seeRegions(1);
  77. AtOutliner.see("Cloud");
  78. // Force all the magic wand regions to be a consistent color with no opacity to make testing
  79. // magic wand pixel colors more robust.
  80. I.say("Ensuring cloud magic wand pixels are correctly filled color");
  81. await I.executeScript(setKonvaLayersOpacity, [1.0]);
  82. await assertMagicWandPixel(
  83. I,
  84. 0,
  85. 0,
  86. false,
  87. CLOUD.rgbArray,
  88. "Far upper left corner should not have magic wand cloud class",
  89. );
  90. await assertMagicWandPixel(I, 260, 50, true, CLOUD.rgbArray, "Upper left should have magic wand cloud class");
  91. await assertMagicWandPixel(I, 300, 620, true, CLOUD.rgbArray, "Lower left should have magic wand cloud class");
  92. await assertMagicWandPixel(
  93. I,
  94. 675,
  95. 650,
  96. false,
  97. CLOUD.rgbArray,
  98. "Far lower right corner should not have magic wand cloud class",
  99. );
  100. // Make sure the region made from this is correct.
  101. I.say("Ensuring magic wand brushregion was created correctly");
  102. const result = await I.executeScript(serialize);
  103. const entry = result[1];
  104. assert.equal(entry.from_name, "labels_1");
  105. assert.equal(entry.type, "labels");
  106. const labels = entry.value.labels;
  107. assert.equal(labels.length, 1);
  108. assert.equal(labels[0], "Cloud");
  109. assert.equal(entry.value.rle.length > 0, true);
  110. // Undo the bottom left area we just added, make sure its gone but our region list is still
  111. // 1, then redo it and ensure its back and our region list is still 1 again.
  112. I.say("Undoing last cloud magic wand and ensuring it worked correctly");
  113. I.click('button[aria-label="Undo"]');
  114. I.waitTicks(2);
  115. await assertMagicWandPixel(
  116. I,
  117. 300,
  118. 620,
  119. false,
  120. CLOUD.rgbArray,
  121. "Undone lower left should not have magic wand cloud class anymore",
  122. );
  123. await assertMagicWandPixel(I, 260, 50, true, CLOUD.rgbArray, "Upper left should still have magic wand cloud class");
  124. AtOutliner.seeRegions(1);
  125. I.say("Redoing last cloud magic wand and ensuring it worked correctly");
  126. I.click('button[aria-label="Redo"]');
  127. I.waitTicks(2);
  128. await assertMagicWandPixel(
  129. I,
  130. 300,
  131. 620,
  132. true,
  133. CLOUD.rgbArray,
  134. "Redone lower left should have magic wand cloud class again",
  135. );
  136. await assertMagicWandPixel(I, 260, 50, true, CLOUD.rgbArray, "Upper left should still have magic wand cloud class");
  137. AtOutliner.seeRegions(1);
  138. I.say("Unselecting last magic wand region");
  139. I.pressKey("Escape");
  140. // @todo currently gallery doesn't work well with CORS, so this is not covered by test
  141. // Change to the false color view, zoom in, and magic wand with a new class.
  142. // I.say('Changing to false-color view');
  143. // I.click('[class*="gallery--"] img[src*="false_color"]');
  144. I.say("Zooming in");
  145. I.click('button[aria-label="zoom-in"]');
  146. I.click('button[aria-label="zoom-in"]');
  147. I.click('button[aria-label="zoom-in"]');
  148. I.click('button[aria-label="zoom-in"]');
  149. I.click('button[aria-label="zoom-in"]');
  150. I.say("Selecting cloud shadow class");
  151. I.pressKey("2");
  152. I.say("Magic wanding cloud shadows with cloud shadow class in center of zoomed image");
  153. await doDrawingAction(I, { msg: "Cloud shadow in middle of image", fromX: 600, fromY: 500, toX: 500, toY: 500 });
  154. I.waitTicks(2);
  155. I.say("Ensuring new cloud shadow magic wand region gets added to sidebar");
  156. AtOutliner.seeRegions(2);
  157. AtOutliner.see("Cloud Shadow");
  158. I.say("Ensuring cloud shadow magic wand pixels are correctly filled color");
  159. await I.executeScript(setKonvaLayersOpacity, [1.0]);
  160. await assertMagicWandPixel(
  161. I,
  162. 0,
  163. 0,
  164. false,
  165. CLOUDSHADOW.rgbArray,
  166. "Zoomed upper left corner should not have cloud shadow",
  167. );
  168. await assertMagicWandPixel(
  169. I,
  170. 350,
  171. 360,
  172. true,
  173. CLOUDSHADOW.rgbArray,
  174. "Center area should have magic wand cloud shadow class",
  175. );
  176. // Make sure if you have a region selected then change the class the region class changes.
  177. I.say("Changing class of existing selected region to Haze should change it to new class");
  178. I.pressKey("3");
  179. AtOutliner.seeRegions(2);
  180. AtOutliner.dontSee("Cloud Shadow");
  181. AtOutliner.see("Haze");
  182. I.waitTicks(2);
  183. await I.executeScript(setKonvaLayersOpacity, [1.0]);
  184. await assertMagicWandPixel(I, 350, 360, true, HAZE.rgbArray, "Center area should have magic wand haze class");
  185. },
  186. );