From 7fd730a31b3ee144aa76e419bf58592b2585baf5 Mon Sep 17 00:00:00 2001 From: Victor Muryn Date: Thu, 10 Jul 2025 17:17:07 +0300 Subject: [PATCH 1/3] added all files --- LICENSE | 27 + README.md | 134 +- hierarchy_dl/application.py | 82 + hierarchy_dl/blip.py | 25 + hierarchy_dl/hierarchy.py | 120 + hierarchy_dl/requirements.txt | 79 + hierarchy_dl/system_buttons.png | Bin 0 -> 7427 bytes hierarchy_dl/utils.py | 353 ++ hierarchy_heuristics/box.py | 233 ++ hierarchy_heuristics/group.ipynb.py | 186 ++ hierarchy_heuristics/main.py | 45 + hierarchy_heuristics/metrics.py | 182 + hierarchy_heuristics/requirements.txt | 9 + hierarchy_heuristics/simplify_tree.py | 327 ++ hierarchy_heuristics/utils.py | 785 +++++ .../example_data/custom_acc.json | 2915 +++++++++++++++++ .../example_data/screenshot.png | Bin 0 -> 278138 bytes .../example_data/system_acc.json | 1037 ++++++ output_visualisation/visualiser_app.py | 756 +++++ requirements.txt | 218 ++ screen_reader/screen_reader.py | 384 +++ screen_reader/screenshot.py | 154 + screen_reader/utils.py | 207 ++ 23 files changed, 8257 insertions(+), 1 deletion(-) create mode 100644 LICENSE create mode 100644 hierarchy_dl/application.py create mode 100644 hierarchy_dl/blip.py create mode 100644 hierarchy_dl/hierarchy.py create mode 100644 hierarchy_dl/requirements.txt create mode 100644 hierarchy_dl/system_buttons.png create mode 100644 hierarchy_dl/utils.py create mode 100644 hierarchy_heuristics/box.py create mode 100644 hierarchy_heuristics/group.ipynb.py create mode 100644 hierarchy_heuristics/main.py create mode 100644 hierarchy_heuristics/metrics.py create mode 100644 hierarchy_heuristics/requirements.txt create mode 100644 hierarchy_heuristics/simplify_tree.py create mode 100644 hierarchy_heuristics/utils.py create mode 100644 output_visualisation/example_data/custom_acc.json create mode 100644 output_visualisation/example_data/screenshot.png create mode 100644 output_visualisation/example_data/system_acc.json create mode 100644 output_visualisation/visualiser_app.py create mode 100644 requirements.txt create mode 100644 screen_reader/screen_reader.py create mode 100644 screen_reader/screenshot.py create mode 100644 screen_reader/utils.py diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5194b35 --- /dev/null +++ b/LICENSE @@ -0,0 +1,27 @@ +MIT License +Copyright (c) 2025, Anonymous +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the “Software”), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--- +This project includes and builds upon the BLIP model developed by Salesforce.com, Inc., which is licensed under the BSD 3-Clause License: +BSD 3-Clause License +Copyright (c) 2022, Salesforce.com, Inc. +All rights reserved. +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +1. Redistributions of source code must retain the above copyright notice, this list of conditions, and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions, and the following disclaimer in the documentation and/or other materials provided with the distribution. +3. Neither the name of Salesforce.com nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/README.md b/README.md index a14c62b..d1fcde3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,134 @@ +[![MacPaw Research](https://pbs.twimg.com/profile_banners/3993798502/1720615716/1500x500)](https://research.macpaw.com) + # Screen2AX -Repository for Screen2AX paper + +A research-driven project for generating accessibility of macOS applications using computer vision and deep learning. Read more about the project in our [paper](). + +--- + +## 📁 Datasets + +- [Screen2AX-Tree](https://huggingface.co/datasets/macpaw-research/Screen2AX-Tree) +- [Screen2AX-Element](https://huggingface.co/datasets/macpaw-research/Screen2AX-Element) +- [Screen2AX-Group](https://huggingface.co/datasets/macpaw-research/Screen2AX-Group) +- [Screen2AX-Task](https://huggingface.co/datasets/macpaw-research/Screen2AX-Task) + +## 🤖 Models + +- [YOLOv11l — UI Elements Detection](https://huggingface.co/macpaw-research/yolov11l-ui-elements-detection) +- [BLIP — UI Elements Captioning](https://huggingface.co/macpaw-research/blip-icon-captioning) +- [YOLOv11l — UI Groups Detection](https://huggingface.co/macpaw-research/yolov11l-ui-groups-detection) + +--- + +## 🛠 Requirements + +- macOS +- Python (recommended ≥ 3.11) +- Conda +- Pip + +--- + +## ⚙️ Installation + +Create and activate the project environment: + +```bash +conda create -n screen2ax python=3.11 +conda activate screen2ax +pip install -r requirements.txt +``` + +## 🚀 Usage + +> ⚠️ The first run may take longer due to model downloads and initial setup. + +### Accessibility generation +Run the accessibility generation script: + +```bash +python -m hierarchy_dl.hierarchy --help +``` +#### Available Options + +``` +usage: hierarchy.py [-h] [--image IMAGE] [--save] [--filename FILENAME] [--save_dir SAVE_DIR] [--flat] + +options: + -h, --help show this help message and exit + --image IMAGE Path to the image + --save Save the result + --filename FILENAME Filename to save the result + --save_dir SAVE_DIR Directory to save the result. Default is './results/' + --flat Generate flat hierarchy (no groups) +``` + +##### Example +Run the accessibility generation script on a screenshot of the Spotify app: + +```bash +python -m hierarchy_dl.hierarchy --image ./screenshots/spotify.png --save --filename spotify.json +``` + +This will generate a JSON file with the accessibility of the app in the results folder. + +### Screen Reader +Run the screen reader: + +```bash +python -m screen_reader.screen_reader --help +``` + +#### Available Options + +``` +usage: screen_reader.py [-h] [-b BUNDLE_ID] [-n NAME] [-dw] [-dh] [-r RATE] [-v VOICE] [-sa] [-sk SKIP_GROUPS] + +options: + -h, --help show this help message and exit + -b, --bundle_id BUNDLE_ID Bundle ID of the target application + -n, --name NAME Name of the target application (alternative to bundle_id) + -dw, --deactivate_welcome Skip the "Welcome to the ScreenReader." message + -dh, --deactivate_help Skip reading the help message on startup + -r, --rate RATE Set speech rate for macOS `say` command (default: 190) + -v, --voice VOICE Set voice for macOS `say` command (see `say -v "?" | grep en`) + -sa, --system_accessibility Use macOS system accessibility data instead of vision-generated + -sk, --skip-groups N Skip groups with fewer than N children (default: 5) +``` + +##### Example + +Run the screen reader for the Spotify app: +```bash +python -m screen_reader.screen_reader --name Spotify +``` + +## 📜 License +### 🔍 YOLO Models +The YOLO models used for UI elements and UI groups detection are licensed under the GNU Affero General Public License (AGPL). This is inherited from the original YOLO model licensing. + +### 🧠 BLIP Model +The BLIP model for captioning UI elements is provided under the MIT License. + +### 📂 Datasets +All datasets (Screen2AX-Tree, Screen2AX-Element, Screen2AX-Group, Screen2AX-Task) are released under the Apache 2.0 license. + +### 💻 Codebase +All source code in this repository is licensed under the MIT License. See the [LICENSE](LICENSE) file for full terms and conditions. + +## 📚 Citation +If you use this code in your research, please cite our paper: + +```bibtex +... +``` + +## 🙌 Acknowledgements +We would like to express our deepest gratitude to the Armed Forces of Ukraine. Your courage and unwavering defense of our country make it possible for us to live, work, and create in freedom. This work would not be possible without your sacrifice. Thank you. + +## MacPaw Research + +Visit our site to learn more 😉 + +https://research.macpaw.com diff --git a/hierarchy_dl/application.py b/hierarchy_dl/application.py new file mode 100644 index 0000000..9aadc4e --- /dev/null +++ b/hierarchy_dl/application.py @@ -0,0 +1,82 @@ +import time +import threading + +import tkinter as tk + +from hierarchy import generate_hierarchy +from screen_reader.screenshot import screenshot_app, open_app_in_foreground + + +run = True +thread = None + + +def start_action(): + bundle_id = entry.get() + open_app_in_foreground(bundle_id, wait_time=2) + + global run + run = True + + i = 0 + while run: + try: + start = time.time() + open_app_in_foreground(bundle_id, wait_time=0.25) + screen_path = screenshot_app(bundle_id, f"./screenshots/")[0] + + tree = generate_hierarchy(screen_path, save=True, save_dir=f"./result/{bundle_id}/") + + end = time.time() + + i += 1 + print(f"Frame #{i}, time taken: {end - start}") + + except Exception as e: + print(f"Error: {e}") + break + + +def stop_action(): + global run, thread + run = False + print(f"Stopping process") + + if thread: + thread.join() + + print(f"Thread has stopped") + + +def start_thread(): + global thread + thread = threading.Thread(target=start_action, daemon=True) + thread.start() + + +if __name__ == "__main__": + # Create main window + root = tk.Tk() + root.title("Bundle ID Manager") + root.geometry("300x200") + + # Create input field + label = tk.Label(root, text="bundle_id:") + label.pack(pady=5) + + entry = tk.Entry(root) + entry.pack(pady=5) + + # Copyable text with suggestion + suggestion = tk.Label(root, text="osascript -e 'id of app \"Spotify\"' \n e.g. com.spotify.client") + suggestion.pack(pady=5) + + # Create buttons + start_button = tk.Button(root, text="Start", command=start_thread) + start_button.pack(pady=5) + + stop_button = tk.Button(root, text="Stop", command=stop_action) + stop_button.pack(pady=5) + + # Run application + root.mainloop() \ No newline at end of file diff --git a/hierarchy_dl/blip.py b/hierarchy_dl/blip.py new file mode 100644 index 0000000..5f5fb5f --- /dev/null +++ b/hierarchy_dl/blip.py @@ -0,0 +1,25 @@ +import torch +from PIL import Image +from transformers import BlipProcessor, BlipForConditionalGeneration + +if torch.cuda.is_available(): + device = torch.device("cuda") +elif torch.backends.mps.is_available(): + device = torch.device("mps") +else: + device = torch.device("cpu") + +print(f"Using device: {device}") + +cache_dir = "./.models" + +model_path = "macpaw-research/blip-icon-captioning" +processor = BlipProcessor.from_pretrained(model_path, cache_dir=cache_dir) +model = BlipForConditionalGeneration.from_pretrained(model_path, cache_dir=cache_dir).to(device) +model.eval() + +@torch.no_grad() +def generate_captions(images: list[Image.Image]) -> list[str]: + inputs = processor(images, return_tensors="pt").to(device) + outputs = model.generate(**inputs, max_new_tokens=25) + return processor.batch_decode(outputs, skip_special_tokens=True) diff --git a/hierarchy_dl/hierarchy.py b/hierarchy_dl/hierarchy.py new file mode 100644 index 0000000..19797e7 --- /dev/null +++ b/hierarchy_dl/hierarchy.py @@ -0,0 +1,120 @@ +import os +import json +import time +from os import path +from typing import Optional + +import numpy as np +from PIL import Image +from ocrmac import ocrmac +from ultralytics import YOLO + +from hierarchy_dl.utils import * + +from huggingface_hub import hf_hub_download +cache_dir = "./.models" + +ui_elements_model_path = hf_hub_download( + repo_id="macpaw-research/yolov11l-ui-elements-detection", + filename="ui-elements-detection.pt", + cache_dir=cache_dir +) + +ui_groups_model_path = hf_hub_download( + repo_id="macpaw-research/yolov11l-ui-groups-detection", + filename="ui-groups-detection.pt", + cache_dir=cache_dir +) + +ui_elements_model = YOLO(ui_elements_model_path) +ui_groups_model = YOLO(ui_groups_model_path) + + +def generate_hierarchy( + img: str | Image.Image | np.ndarray, + save_dir: str = "./results/", + save: bool = False, + filename: Optional[str] = None, + flat: bool = False +) -> UIElement: + """ + Generate UI hierarchy from an image + """ + # load image + if isinstance(img, str): + img_pil = Image.open(img) + + if isinstance(img, np.ndarray): + img_pil = Image.fromarray(img) + + if isinstance(img, Image.Image): + img_pil = img + + width, height = img_pil.size + + # detect ui elements + ui_elements = ui_elements_model(img_pil, verbose=False)[0].boxes + ui_elements = [UIElement(box, cls) for box, cls in zip(ui_elements.xyxy, ui_elements.cls)] + + # detect ui groups + ui_groups = ui_groups_model(img_pil, conf=0.5, verbose=False)[0].boxes + ui_groups = [UIElement(box, "Group") for box in ui_groups.xyxy] + + # ocr + annotations = ocrmac.OCR(img_pil, language_preference=['en-US']).recognize(px=True) + annotations = [UIElement(box, "Text", value=val) for val, _, box in annotations] + + # merge texts and elements + annotations = group_texts(annotations) + ui_elements = merge_text_and_elements(ui_elements, annotations, iou_threshold=0.2) + + # icons + ui_elements = caption_buttons(ui_elements, img_pil, batch_size=16) + + if not flat: + # build tree + tree = build_tree(ui_groups, ui_elements, (width, height), iou_threshold=0.0) + clean_tree(tree) + + if len(tree.children) == 1: + tree = tree.children[0] + else: + ui_elements.sort(key=lambda x: x.box[0] ** 2 + x.box[1] ** 2) + tree = UIElement( + box=[0, 0, width, height], + cls="Group", + value="Screen" + ) + tree.children = ui_elements + + if save or filename: + os.makedirs(save_dir, exist_ok=True) + + filename = f"{path.basename(img)}.json" if isinstance(img, str) and not filename else filename + filename = filename or f"{time.time()}.json" + + full_path = path.join(save_dir, filename) + + with open(full_path, "w", encoding='utf-8') as f: + json.dump(tree.to_dict(), f, indent=4) + + return tree + +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser() + parser.add_argument("--image", type=str, default="./screen.png", help="Path to the image") + parser.add_argument("--save", action="store_true", help="Save the result") + parser.add_argument("--filename", type=str, default=None, help="Filename to save the result") + parser.add_argument("--save_dir", type=str, default="./results/", help="Directory to save the result. Default is './results/'") + parser.add_argument("--flat", action="store_true", help="Generate flat hierarchy (no groups)") + args = parser.parse_args() + + image = args.image + save_dir = args.save_dir + save = args.save + filename = args.filename + flat = args.flat + + tree = generate_hierarchy(image, save_dir, save, filename, flat) \ No newline at end of file diff --git a/hierarchy_dl/requirements.txt b/hierarchy_dl/requirements.txt new file mode 100644 index 0000000..2ff8411 --- /dev/null +++ b/hierarchy_dl/requirements.txt @@ -0,0 +1,79 @@ +aiohappyeyeballs==2.4.4 +aiohttp==3.11.10 +aiosignal==1.2.0 +attrs==24.3.0 +Bottleneck==1.4.2 +Brotli==1.0.9 +certifi==2025.1.31 +charset-normalizer==3.3.2 +click==8.1.8 +contourpy==1.3.1 +cycler==0.12.1 +datasets==2.12.0 +Deprecated==1.2.13 +dill==0.3.6 +filelock==3.13.1 +fonttools==4.56.0 +frozenlist==1.5.0 +fsspec==2024.12.0 +gmpy2==2.2.1 +huggingface_hub==0.24.6 +idna==3.7 +ImageHash==4.3.2 +importlib-metadata==7.0.1 +Jinja2==3.1.5 +kiwisolver==1.4.8 +MarkupSafe==3.0.2 +matplotlib==3.10.1 +mpmath==1.3.0 +multidict==6.1.0 +multiprocess==0.70.14 +networkx==3.4.2 +numexpr==2.10.1 +numpy==1.26.4 +ocrmac==1.0.0 +opencv-python==4.11.0.86 +opentelemetry-api==1.26.0 +packaging==24.2 +pandas==2.2.3 +pillow==11.1.0 +pip==25.0 +propcache==0.2.0 +psutil==7.0.0 +py-cpuinfo==9.0.0 +pyarrow==19.0.0 +pyobjc-core==11.0 +pyobjc-framework-Cocoa==11.0 +pyobjc-framework-CoreML==11.0 +pyobjc-framework-Quartz==11.0 +pyobjc-framework-Vision==11.0 +pyparsing==3.2.1 +PySocks==1.7.1 +python-dateutil==2.9.0.post0 +pytz==2024.1 +PyWavelets==1.8.0 +PyYAML==6.0.2 +regex==2024.11.6 +requests==2.32.3 +responses==0.13.3 +safetensors==0.4.5 +scipy==1.15.2 +seaborn==0.13.2 +setuptools==75.8.0 +six==1.16.0 +sympy==1.13.1 +tokenizers==0.20.1 +torch==2.6.0 +torchvision==0.21.0 +tqdm==4.67.1 +transformers==4.45.2 +typing_extensions==4.12.2 +tzdata==2023.3 +ultralytics==8.3.82 +ultralytics-thop==2.0.14 +urllib3==2.3.0 +wheel==0.45.1 +wrapt==1.17.0 +xxhash==3.5.0 +yarl==1.18.0 +zipp==3.21.0 diff --git a/hierarchy_dl/system_buttons.png b/hierarchy_dl/system_buttons.png new file mode 100644 index 0000000000000000000000000000000000000000..7ca0709f35fe7640ace1a49fd5efbe1504405401 GIT binary patch literal 7427 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≪A9dD34H&ExL2@n8fE2q!~F=aO}iT`|?;hTDxJa(*Ct zYL6O^Ez(Q&xM(qlASh7f1otpTBJ49GLoX{iULK~z(3Z^xIe&!3)c{sKeC4-h3R1x+ zdRMe%*4-e#prP+&8E77nE>bEgpNFWLbZU#0-AI_b1}Ns$$=iNLnI}|b-_@pfvHK=E zI+`1l@R1t#B^smRKXb7iJP0aQbxFBzSIYwzuTag%+de}ZA)V-L&e;#_DXW6C{@l71 z=0L$(jpK09CwFHBO1e?K6ry#r=Vgr4;}?CU{_vR`7o16gXtVulOnwMAoymDfV$OnyFqF% zRK2209h7~Hnm8?*B9I|YZcbnzxHO|WX0ZXrr&E0E<0B5XL+GWT*6F%S;+dXQ4r)9V zkVmX2|H^V4wDO)5J!jpwE)Rfs&zSMw`6pE6~m?h&2`W4pGL#TaK=` z;)Pr@DMF86)3$th)0w*W-}v)llnIH^;hD^;Xs76250bJbgveyj^YBWtox-|$CNJAw ztxZbznUXiWSplEFigiVD#eNxVY8+-|8aBcvWBp4l&0T$_sIHmMul)+e*)mNi9|?-SM;VcJ5_px*N1I%yUc~0?hLjB*a&i^-Nh=RU&C+TO6SmQry1zN<@%D~Oefz76;v60XAX literal 0 HcmV?d00001 diff --git a/hierarchy_dl/utils.py b/hierarchy_dl/utils.py new file mode 100644 index 0000000..504ad36 --- /dev/null +++ b/hierarchy_dl/utils.py @@ -0,0 +1,353 @@ +from PIL import Image +from ocrmac import ocrmac +from imagehash import average_hash, ImageHash +import cv2 +import numpy as np + +from .blip import generate_captions + + +icons_cache = {} + + +def iou(box1: list[int], box2: list[int]) -> float: + """ + Calculate IoU between two boxes + """ + # Extract the coordinates of both boxes + x1, y1, x2, y2 = box1 + x1_other, y1_other, x2_other, y2_other = box2 + + # Calculate the coordinates of the intersection rectangle + inter_x1 = max(x1, x1_other) + inter_y1 = max(y1, y1_other) + inter_x2 = min(x2, x2_other) + inter_y2 = min(y2, y2_other) + + # Check if there is an intersection + inter_width = max(0, inter_x2 - inter_x1) + inter_height = max(0, inter_y2 - inter_y1) + + # Area of intersection + intersection_area = inter_width * inter_height + + # Area of union + union_area = abs((x2 - x1) * (y2 - y1)) + abs((x2_other - x1_other) * (y2_other - y1_other)) - intersection_area + + if union_area == 0: + return 0 + + # IoU calculation + return intersection_area / union_area + + +class UIElement: + names = [ + 'AXButton', 'AXDisclosureTriangle', 'AXImage', + 'AXLink', 'AXTextArea', 'Text', + 'Group' + ] + + def __init__(self, box: list[int], cls: int | str, value: str = None): + self.value = value or None + self.box = list(map(int, box)) # [x1, y1, x2, y2] + self.cls = self.names.index(cls) if isinstance(cls, str) else int(cls) + self.children: list[UIElement] = [] + + + @property + def area(self) -> int: + return abs((self.box[2] - self.box[0]) * (self.box[3] - self.box[1])) + + def iou(self, other: "UIElement") -> float: + # Extract the coordinates of both boxes + x1, y1, x2, y2 = self.box + x1_other, y1_other, x2_other, y2_other = other.box + + # Calculate the coordinates of the intersection rectangle + inter_x1 = max(x1, x1_other) + inter_y1 = max(y1, y1_other) + inter_x2 = min(x2, x2_other) + inter_y2 = min(y2, y2_other) + + # Check if there is an intersection + inter_width = max(0, inter_x2 - inter_x1) + inter_height = max(0, inter_y2 - inter_y1) + + # Area of intersection + intersection_area = inter_width * inter_height + + # Area of union + union_area = self.area + other.area - intersection_area + + if union_area == 0: + return 0 + + # IoU calculation + return intersection_area / union_area + + + def merge(self, other: "UIElement"): + self.box[0] = min(self.box[0], other.box[0]) + self.box[1] = min(self.box[1], other.box[1]) + self.box[2] = max(self.box[2], other.box[2]) + self.box[3] = max(self.box[3], other.box[3]) + + + def __dict__(self): + return { + "cls": self.names[self.cls], + "value": self.value, + "box": self.box, + "children": [child.__dict__() for child in self.children] + } + + + def to_dict(self): + return self.__dict__() + + + def __repr__(self): + return str(self.__dict__()) + + +def group_texts(annotations: list[UIElement], max_height_frac: int = 2) -> list[UIElement]: + """ + Group rows of texts in a single annotation (paragraph) + """ + # Sort annotations by their vertical position (top Y-coordinate) + annotations.sort(key=lambda x: x.box[1]) + + i = 0 + while i < len(annotations): + j = i + 1 + ref_height = abs(annotations[i].box[3] - annotations[i].box[1]) + + while j < len(annotations): + box1, box2 = annotations[i].box, annotations[j].box + height2 = abs(box2[3] - box2[1]) + + # Check if annotations are close on the Y-axis + if abs(box1[3] - box2[1]) > height2 * 0.5: + j += 1 + continue + + # Check if the height difference is reasonable (similar heights) + if not (1 / max_height_frac) <= (ref_height / height2) <= max_height_frac: # 1/2 <= h1/h2 <= 2 + j += 1 + continue + + # Check if annotations overlap on the X-axis + if (box1[0] <= box2[0] <= box1[2] or box1[0] <= box2[2] <= box1[2] or + box2[0] <= box1[0] <= box2[2] or box2[0] <= box1[2] <= box2[2]): + # Merge the two annotations + annotations[i].value += f"\n{annotations[j].value}" + annotations[i].merge(annotations[j]) + + # Update height and remove merged annotation + ref_height = height2 + del annotations[j] + continue + + j += 1 + i += 1 + + return annotations + + +def merge_text_and_elements(elements: list[UIElement], texts: list[UIElement], iou_threshold=0.2) -> list[UIElement]: + """ + Merge texts with elements (buttons, images, etc.) based on IoU + """ + remaining_texts = [] + + for text in texts: + max_iou, best_match = max([(text.iou(element), element) for element in elements], key=lambda x: x[0]) + + + if max_iou > iou_threshold: + best_match.value = f"{best_match.value}\n{text.value}" if best_match.value else text.value + best_match.merge(text) + else: + remaining_texts.append(text) + + elements.extend(remaining_texts) + return elements + + +def caption_buttons(ui_elements: list[UIElement], image: Image.Image, batch_size: int = 16) -> list[UIElement]: + """ + Generate captions for buttons + """ + # find elements that need to be captioned + to_be_captioned = [e for e in ui_elements if e.cls in (0, 2) and not e.value] + + # calculate crop and hash + to_be_captioned = [ + ( e, image.crop(e.box), average_hash( image.crop(e.box) ) ) + for e in to_be_captioned + ] + + # check cache table + uncached_elements: list[tuple[UIElement, Image.Image, ImageHash]] = [] + for element, cropped, img_hash in to_be_captioned: + if img_hash in icons_cache: + element.value = icons_cache[img_hash] + else: + uncached_elements.append((element, cropped, img_hash)) + + # caption close, minimize, and maximize buttons + # read in BGR + system_buttons_img = cv2.imread("./hierarchy_dl/system_buttons.png", cv2.IMREAD_GRAYSCALE) + cv2_image = np.array(image)[:85, :250] # already in RGB + cv2_image_bgr = cv2.cvtColor(cv2_image, cv2.COLOR_RGB2GRAY) + + screenshot_edges = cv2.Canny(system_buttons_img, 50, 200) + template_edges = cv2.Canny(cv2_image_bgr, 50, 200) + + # find the system buttons using pattern matching. Find area with biggest match + result = cv2.matchTemplate(screenshot_edges, template_edges, cv2.TM_CCOEFF_NORMED) + _, maxV, _, max_loc = cv2.minMaxLoc(result) + + if maxV > 0.4: + x, y = max_loc + + r = 28 + offset = 12 + + close_app_location = ([x, y, x + r, y + r], "Close") + minimize_location = ([x + r + offset, y, x + offset + 2 * r, y + r], "Minimize") + maximize_location = ([x + 2 * (r + offset), y, x + 2 * (r + offset) + r, y + r], "Zoom") + + uncached_elements.sort(key=lambda x: x[0].box[0] ** 2 + x[0].box[1] ** 2) + + for element, cropped, img_hash in uncached_elements: + for location, name in [close_app_location, minimize_location, maximize_location]: + if iou(element.box, location) > 0.1 and element.value is None: + element.value = name + break + + # remove elements that have been captioned + uncached_elements = [e for e in uncached_elements if e[0].value is None] + + # try with ocr + remaining_elements: list[tuple[UIElement, Image.Image, ImageHash]] = [] + for element, cropped, img_hash in uncached_elements: + vals = ocrmac.OCR(cropped, language_preference=['en-US'], recognition_level="accurate").recognize() + + recognized_texts = [val for val, conf, _ in vals if conf == 1.0] + + if recognized_texts: + element.value = "\n".join(recognized_texts) + icons_cache[img_hash] = element.value + else: + remaining_elements.append((element, cropped, img_hash)) + + # generate captions using DeepLearning + for i in range(0, len(remaining_elements), batch_size): + batch = remaining_elements[i:i+batch_size] + crops = [cropped for _, cropped, _ in batch] + + captions = generate_captions(crops) + + for (el, _, img_hash), caption in zip(batch, captions): + el.value = caption + icons_cache[img_hash] = caption + + return ui_elements + + +def build_tree( + ui_groups: list[UIElement], + ui_elements: list[UIElement], + size: tuple[int, int], + iou_threshold=0.1 +) -> UIElement: + """ + Build a tree from a list of UI elements and groups + """ + remaining_elements: list[UIElement] = [] + + # Assign elements to groups based on IoU + if ui_groups: + for element in ui_elements: + max_iou, best_group = max( + ((group.iou(element), group) for group in ui_groups), key=lambda x: x[0] + ) + + if max_iou > iou_threshold: + best_group.children.append(element) + else: + remaining_elements.append(element) + else: + remaining_elements.extend( ui_elements ) + + # Include groups in remaining elements and sort by area (smallest first) + remaining_elements.extend( ui_groups ) + remaining_elements.sort(key=lambda x: x.area) + + merge_occurred = True + # Continue looping until no merge occurs in a full pass. + while merge_occurred: + merge_occurred = False + i = 0 + + # Use a while loop to manage indices when items are removed. + while i < len(remaining_elements): + A = remaining_elements[i] + + # Check for a larger element to merge A into. + for j in range(i + 1, len(remaining_elements)): + B = remaining_elements[j] + + if A.iou(B) > 0 and B.cls == B.names.index("Group"): + # Merge A into B: update B's bounding box and add A as a child. + B.merge(A) + B.children.append(A) + + # Remove A from the list. + remaining_elements.pop(i) + merge_occurred = True + + # Re-sort remaining_elements as B's area may have increased. + remaining_elements.sort(key=lambda x: x.area) # O(n log n), but inserting 1 element could be O(n) + + # Break out to restart checking from the beginning. + break + + else: + # Only increment if A wasn't merged, because removal shifts indices. + i += 1 + + + root = UIElement([0, 0, size[0], size[1]], "Group") + root.children = remaining_elements + + return root + + +def clean_tree(tree: UIElement): + """ + Delete empty groups if they are leafs + """ + i = 0 + + while i < len(tree.children): + child = tree.children[i] + + if child.children: + clean_tree(child) + + if child.cls == 6: + if not child.children: + del tree.children[i] + continue + + elif len(child.children) == 1: + tree.children[i] = child.children[0] + continue + + i += 1 + + # Sort children by distance from the parent + tree.children.sort(key=lambda x: (x.box[0] - tree.box[0]) ** 2 + (x.box[1] - tree.box[1]) ** 2) diff --git a/hierarchy_heuristics/box.py b/hierarchy_heuristics/box.py new file mode 100644 index 0000000..70853c9 --- /dev/null +++ b/hierarchy_heuristics/box.py @@ -0,0 +1,233 @@ +class BBox: + def __init__(self, box): + self.box: tuple[int, int, int, int] = tuple(map(int, box)) + + def __iter__(self): + return iter(self.box) + + def __getitem__(self, idx): + return self.box[idx] + + @property + def top_left(self): + return self.box[:2] + + @property + def bottom_right(self): + return self.box[2:] + + @property + def width(self): + return self.box[2] - self.box[0] + + @property + def height(self): + return self.box[3] - self.box[1] + + @property + def area(self): + return self.width * self.height + + @property + def x1(self): + return self.box[0] + + @property + def y1(self): + return self.box[1] + + @property + def x2(self): + return self.box[2] + + @property + def y2(self): + return self.box[3] + + def iou(self, other): + x1 = max(self.box[0], other.box[0]) + y1 = max(self.box[1], other.box[1]) + x2 = min(self.box[2], other.box[2]) + y2 = min(self.box[3], other.box[3]) + + intersection = max(0, x2 - x1) * max(0, y2 - y1) + union = self.area + other.area - intersection + + if union == 0: + return 0 + + return intersection / union + + def merge_bboxes(self, other, inplace=False): + x1 = min(self.box[0], other.box[0]) + y1 = min(self.box[1], other.box[1]) + x2 = max(self.box[2], other.box[2]) + y2 = max(self.box[3], other.box[3]) + + if inplace: + self.box = (x1, y1, x2, y2) + return self + + return BBox((x1, y1, x2, y2)) + + def y_distance(self, other: "BBox"): + return min( + abs(self.y1 - other.y2), + abs(self.y2 - other.y1), + abs(self.y1 - other.y1), + abs(self.y2 - other.y2), + ) + + def x_distance(self, other: "BBox"): + return min( + abs(self.x1 - other.x2), + abs(self.x2 - other.x1), + abs(self.x1 - other.x1), + abs(self.x2 - other.x2), + ) + + +class Text(BBox): + def __init__(self, bbox, text: str) -> None: + super().__init__(bbox) + self.text_value = text + + def __repr__(self) -> str: + return f"Text(box={self.box}, text={self.text_value})" + + def __bool__(self): + return bool(self.text_value) + + def __add__(self, other): + if not other: + return self + + if not self: + return other + + bbox = ( + min(self.x1, other.x1), + min(self.y1, other.y1), + max(self.x2, other.x2), + max(self.y2, other.y2), + ) + text = self.text_value + " " + other.text_value + return Text(bbox, text) + + +class UIBox(BBox): + """Abstract class for UI elements""" + + id2class = { + 0: "AXButton", + 1: "AXCheckBox", + 2: "AXComboBox", + 3: "AXHeading", + 4: "AXImage", + 5: "AXLink", + 6: "AXRadioButton", + 7: "AXScrollBar", + 8: "AXSlider", + 9: "AXStaticText", + 10: "AXTextField", + 11: "TextGroup", + 12: "OCRText", + 13: "Group", + } + + class2id = {v: k for k, v in id2class.items()} + + def __init__(self, box): + super().__init__(box) + self.parent = None + self.cls = None + + def __repr__(self): + return f"{self.__class__.__name__}(box={self.box}, cls={self.cls})" + + def merge(self, other, cls=None, inplace=False): + raise NotImplementedError + + def other_is_inside(self, other, margin=0): + margin_x1, margin_y1, margin_x2, margin_y2 = ( + margin if isinstance(margin, tuple) else (margin, margin, margin, margin) + ) + + x1 = max(self.x1 + margin_x1, 0) + y1 = max(self.y1 + margin_y1, 0) + x2 = self.x2 - margin_x2 + y2 = self.y2 - margin_y2 + + return ( + x1 <= other.x1 <= x2 + and y1 <= other.y1 <= y2 + and x1 <= other.x2 <= x2 + and y1 <= other.y2 <= y2 + ) + + +class Group(UIBox): + group_types = ["window", "row", "text", "column"] + + def __init__(self, box, children=None, group_type="text"): + super().__init__(box) + self.cls = self.class2id["Group"] + self.group_type = group_type + self.children = children or [] + + def __repr__(self): + return f"Group(box={self.box}, group_type={self.group_type}, children={self.children})" + + def append(self, child): + self.children.append(child) + child.parent = self + + def merge(self, other, cls=None, inplace=False): + if not other: + return self + + cls = cls or self.cls + + if inplace: + self.merge_bboxes(other, inplace=True) + self.cls = cls + self.children += other.children + return self + + return Group(self.merge_bboxes(other).box, self.children + other.children) + + def finalize_bbox(self): + x1, x2, y1, y2 = 9999, 0, 9999, 0 + + for child in self.children: + x1 = min(x1, child.x1) + y1 = min(y1, child.y1) + x2 = max(x2, child.x2) + y2 = max(y2, child.y2) + + self.box = (x1, y1, x2, y2) + return self + + +class Box(UIBox): + def __init__(self, box, cls, text: Text | None = None): + super().__init__(box) + self.cls: int = int(cls) + self.text = text or Text(box, "") + + def __repr__(self): + return f"Box(box={self.box}, cls={self.cls}, text={self.text})" + + def merge(self, other, cls=None, inplace=False): + if not other: + return self + + cls = cls or self.cls + + if inplace: + self.merge_bboxes(other, inplace=True) + self.cls = cls + self.text = self.text + other.text + return self + + return Box(self.merge_bboxes(other).box, cls, self.text + other.text) diff --git a/hierarchy_heuristics/group.ipynb.py b/hierarchy_heuristics/group.ipynb.py new file mode 100644 index 0000000..672c5ef --- /dev/null +++ b/hierarchy_heuristics/group.ipynb.py @@ -0,0 +1,186 @@ +## Imports +import json +import os +import signal +from glob import glob + +import cv2 +from tqdm import tqdm + +from box import * +from metrics import * +from utils import group_elements + +config = { + # YOLO model + "ui_model_conf": 0.3, # [0, 1] + # OCR model + "ocr_conf_threshold": 0.3, # [0, 1] + # bind_text_and_boxes + "bind_text_and_boxes_iou_threshold": 0.05, # [0, 1] + # merge_overlapping_boxes + "merge_overlapping_boxes_iou_threshold": 0.3, # [0, 1] + # clickability confidence + "clickability_model_conf": 0.3, # [0, 1] + # merge_images_and_captions + "merge_images_and_captions_x_overlap_percent_threshold": 0.25, # [0, 1] + "merge_images_and_captions_y_distance_threshold": 0.02, # [0, 1] + "merge_images_and_captions_y_overlap_percent_threshold": 0.4, # [0, 1] + "merge_images_and_captions_x_distance_threshold": 0.02, # [0, 1] + # group_by_column + "group_by_column_y_distance_coefficient": 1.25, # [0, inf) + "group_by_column_width_threshold": 40, # [0, inf) + "group_by_column_max_width_coefficient": 0.55, # [0, 1] + # add_color_groups + "add_color_groups_min_box_threshold": 0.03, # [0, 1] + "add_color_groups_max_box_threshold": 0.95, # [0, 1] + "add_color_groups_color_diff_threshold": 2, # [0, 255] + "add_color_groups_area_threshold": 0.01, # [0, 1] + # group_by_row + "group_by_row_y_distance_threshold": 50, # [0, inf) + "group_by_row_height_threshold": 0.8, # [0, 1] +} + +if __name__ == "__main__": + [os.remove(img) for img in glob("./test/ideal-test/*/*/*.png") if "predicted" in img] + + + images = glob("./test/ideal-test/*/*/*.png") + images = list(filter(lambda x: "segmented" not in x, images)) + images.sort() + + jsons = list(sorted(glob("./test/ideal-test/*/*/*_simplified.json"))) + + dataset = list(zip(images, jsons)) + + + def handler(signum, frame): + raise Exception("Timeout") + + + apps = [group_elements(img, **config) for img, _ in tqdm(dataset)] + + + apps_json = [json.load(open(json_path)) for _, json_path in tqdm(dataset)] + + bgr_colors = [ + (255, 0, 0), # Blue + (0, 255, 0), # Green + (0, 0, 255), # Red + (255, 255, 0), # Yellow + (0, 255, 255), # Cyan + (255, 0, 255), # Magenta + (128, 0, 0), # Maroon + (0, 128, 0), # Dark Green + (0, 0, 128), # Navy + (128, 128, 0), # Olive + (128, 0, 128), # Purple + (0, 128, 128), # Teal + (192, 192, 192), # Silver + (128, 128, 128), # Gray + (255, 165, 0), # Orange + (255, 192, 203), # Pink + (210, 105, 30), # Chocolate + (34, 139, 34), # Forest Green + (255, 215, 0), # Gold + (135, 206, 250), # Sky Blue + ] + + + def plot_app(img, app, depth=0): + color_id = app.cls + label = Box.id2class[app.cls] + + if isinstance(app, Group): + label += f"[{app.group_type} {depth}]" + color_id += ( + Group.group_types.index(app.group_type) + if app.group_type in Group.group_types + else len(Group.group_types) + ) + + color = bgr_colors[color_id % len(bgr_colors)] + + x1, y1, x2, y2 = app.box + cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) + + text_color = (255, 255, 255) + + (w, h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1) + + # Prints the text + cv2.rectangle(img, (x1, y1 - 20), (x1 + w, y1), color, -1) + cv2.putText( + img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, text_color, 1 + ) + + if hasattr(app, "children"): + for child in app.children: + plot_app(img, child, depth + 1) + + # if save_path is not None: + # cv2.imwrite(save_path, img) + return img + + + for app, (img, _) in zip(apps, dataset): + image = cv2.imread(img) + image = plot_app(image, app) + + segmented_image_path = f"{img[:-4]}_simplified-segmented.png" + original_segmented_image = cv2.imread(segmented_image_path) + + output_image = np.concatenate((original_segmented_image, image), axis=1) + img_name = f"{img.split('/')[-1]}" + cv2.imwrite(f"./test-output/{img_name}", output_image) + + metrics = [] + leaf_metrics = [] + geds = [] + mIoU = [] + + for app, app_json in tqdm(zip(apps, apps_json), total=len(apps)): + ## F1 for edges + metrics.append(get_metrics(app, app_json, iou_threshold=0.3, leaf=False)) + leaf_metrics.append(get_metrics(app, app_json, iou_threshold=0.3, leaf=True)) + + ## GED + # try to get GED for 10 seconds. In other case add length of Ground Truth + try: + signal.signal(signal.SIGALRM, handler) + + signal.alarm(10) # 10 seconds + ged = calc_ged(app, app_json, iou_threshold=0.5) + signal.alarm(0) + + geds.append(ged) + + except Exception as e: + gt_edges = [] + get_gt_edges(app_json, gt_edges) + length = len(gt_edges) + geds.append(length) + + ## Average IoU for groups + mIoU.append(mean_groups_iou(app, app_json)) + + # ## Results + print( + f""" + Mean precision {np.mean([i[0] for i in metrics]):.2f} ± {np.std([i[0] for i in metrics]):.2f} + Mean recall {np.mean([i[1] for i in metrics]):.2f} ± {np.std([i[1] for i in metrics]):.2f} + Mean F1 score {np.mean([i[2] for i in metrics]):.2f} ± {np.std([i[2] for i in metrics]):.2f} + """ + ) + + print( + f""" + Mean leaf precision {np.mean([i[0] for i in leaf_metrics]):.2f} ± {np.std([i[0] for i in leaf_metrics]):.2f} + Mean leaf recall {np.mean([i[1] for i in leaf_metrics]):.2f} ± {np.std([i[1] for i in leaf_metrics]):.2f} + Mean leaf F1 score {np.mean([i[2] for i in leaf_metrics]):.2f} ± {np.std([i[2] for i in leaf_metrics]):.2f} + """ + ) + + print(f"Mean GEDs {np.mean(geds):.2f} ± {np.std(geds):.2f}") + + print(f"Mean average IoU for groups {np.mean(mIoU):.2f} ± {np.std(mIoU):.2f}") diff --git a/hierarchy_heuristics/main.py b/hierarchy_heuristics/main.py new file mode 100644 index 0000000..88dc05d --- /dev/null +++ b/hierarchy_heuristics/main.py @@ -0,0 +1,45 @@ +import cv2 +from utils import group_elements, plot_app, plot_all_boxes +import time + +config = { + # YOLO model + "ui_model_conf": 0.3, # [0, 1] + # OCR model + "ocr_conf_threshold": 0.3, # [0, 1] + # bind_text_and_boxes + "bind_text_and_boxes_iou_threshold": 0.05, # [0, 1] + # merge_overlapping_boxes + "merge_overlapping_boxes_iou_threshold": 0.3, # [0, 1] + # clickability confidence + "clickability_model_conf": 0.3, # [0, 1] + # merge_images_and_captions + "merge_images_and_captions_x_overlap_percent_threshold": 0.25, # [0, 1] + "merge_images_and_captions_y_distance_threshold": 0.02, # [0, 1] + "merge_images_and_captions_y_overlap_percent_threshold": 0.4, # [0, 1] + "merge_images_and_captions_x_distance_threshold": 0.02, # [0, 1] + # group_by_column + "group_by_column_y_distance_coefficient": 1.25, # [0, inf) + "group_by_column_width_threshold": 40, # [0, inf) + "group_by_column_max_width_coefficient": 0.55, # [0, 1] + # add_color_groups + "add_color_groups_min_box_threshold": 0.03, # [0, 1] + "add_color_groups_max_box_threshold": 0.95, # [0, 1] + "add_color_groups_color_diff_threshold": 2, # [0, 255] + "add_color_groups_area_threshold": 0.01, # [0, 1] + # group_by_row + "group_by_row_y_distance_threshold": 50, # [0, inf) + "group_by_row_height_threshold": 0.8, # [0, 1] +} + +if __name__ == "__main__": + img = cv2.imread("./visual-test-images/test4.png") + + start = time.time() + app = group_elements(img, verbose=False, **config) + print("Time:", time.time() - start) + + img = plot_all_boxes(img, app) + cv2.imshow("image", img) + cv2.waitKey(0) + cv2.destroyAllWindows() \ No newline at end of file diff --git a/hierarchy_heuristics/metrics.py b/hierarchy_heuristics/metrics.py new file mode 100644 index 0000000..2e42ba4 --- /dev/null +++ b/hierarchy_heuristics/metrics.py @@ -0,0 +1,182 @@ +import networkx as nx +import numpy as np + + +def get_predicted_edges(app, edges, leaf=False): + if hasattr(app, "children"): + for child in app.children: + get_predicted_edges(child, edges) + + if leaf and (not hasattr(child, "children") or not child.children): + edges.append((app, child)) + + if not leaf: + edges.append((app, child)) + + return edges + + +def get_gt_edges(app, edges, leaf=False): + if "children" in app: + for child in app["children"]: + get_gt_edges(child, edges) + + if leaf and ("children" not in child or not child["children"]): + edges.append((app, child)) + elif not leaf: + edges.append((app, child)) + + return edges + + +def iou(box1, box2): + x1 = max(box1[0], box2[0]) + y1 = max(box1[1], box2[1]) + x2 = min(box1[2], box2[2]) + y2 = min(box1[3], box2[3]) + + intersection = max(0, x2 - x1) * max(0, y2 - y1) + union = ( + (box1[2] - box1[0]) * (box1[3] - box1[1]) + + (box2[2] - box2[0]) * (box2[3] - box2[1]) + - intersection + ) + + return intersection / union + + +def get_metrics(app, app_json, iou_threshold=0.3, leaf=False): + preds = [] + get_predicted_edges(app, preds, leaf=leaf) + + gts = [] + get_gt_edges(app_json, gts, leaf=leaf) + + tp = 0 + fp = 0 + fn = 0 + + for pred in preds: + iou_max = 0 + + for gt in gts: + gt_box = gt[0]["xyxy_retina"] + + iou_max = max(iou_max, iou(pred[0].box, gt_box)) + + if iou_max >= iou_threshold: + tp += 1 + else: + fp += 1 + + for gt in gts: + iou_max = 0 + gt_box = gt[0]["xyxy_retina"] + + for pred in preds: + iou_max = max(iou_max, iou(pred[0].box, gt_box)) + + if iou_max < iou_threshold: + fn += 1 + + precision = tp / (tp + fp) if (tp + fp) != 0 else 0 + recall = tp / (tp + fn) if (tp + fn) != 0 else 0 + f1 = 2 * precision * recall / (precision + recall) if precision + recall != 0 else 0 + + return precision, recall, f1 + + +def node_match_function_wrapper(iou_threshold): + def node_match_function(node1, node2): + return iou(node1["box"], node2["box"]) >= iou_threshold + + return node_match_function + + +def calc_ged(app, app_json, iou_threshold=0.3): + predicted_edges = [] + get_predicted_edges(app, predicted_edges) + + gt_edges = [] + get_gt_edges(app_json, gt_edges) + + G1 = nx.Graph() + for edge in predicted_edges: + if edge[0] not in G1.nodes: + G1.add_node(edge[0], box=edge[0].box) + + if edge[1] not in G1.nodes: + G1.add_node(edge[1], box=edge[1].box) + + G1.add_edge(edge[0], edge[1]) + + G2 = nx.Graph() + for edge in gt_edges: + node1 = edge[0].copy() + node1["children"] = None + box = node1["xyxy_retina"] + node1 = tuple(node1.items()) + + if node1 not in G2.nodes: + G2.add_node(node1, box=box) + + node2 = edge[1].copy() + node2["children"] = None + box = node2["xyxy_retina"] + node2 = tuple(node2.items()) + + if node2 not in G2.nodes: + G2.add_node(node2, box=box) + + G2.add_edge(node1, node2) + + return next( + nx.optimize_graph_edit_distance( + G1, G2, node_match=node_match_function_wrapper(iou_threshold) + ) + ) + + +def get_groups(app, groups): + if hasattr(app, "children"): + for child in app.children: + get_groups(child, groups) + + if app.children: + groups.append(app) + + return groups + + +def get_gt_groups(app, groups): + if "children" in app: + for child in app["children"]: + get_gt_groups(child, groups) + + if app["children"]: + groups.append(app) + + return groups + + +def mean_groups_iou(app, app_json): + groups = [] + get_groups(app, groups) + + gt_groups = [] + gt_groups = get_gt_groups(app_json, gt_groups) + + ious = [] + + for group in groups: + iou_max = max( + [iou(group.box, gt_group["xyxy_retina"]) for gt_group in gt_groups] + ) + ious.append(iou_max) + + # for gt_group in gt_groups: + # iou_max = max([iou(group.box, gt_group["xyxy_retina"]) for group in groups]) + # + # ious.append(iou_max) + + return np.mean(ious) if ious else 0 diff --git a/hierarchy_heuristics/requirements.txt b/hierarchy_heuristics/requirements.txt new file mode 100644 index 0000000..6e83597 --- /dev/null +++ b/hierarchy_heuristics/requirements.txt @@ -0,0 +1,9 @@ +matplotlib==3.9.2 +networkx==3.4.1 +numpy==2.1.2 +ocrmac==0.1.6 +opencv-python==4.10.0.84 +pillow==11.0.0 +tqdm==4.66.5 +ultralytics==8.3.18 +gdown==5.2.0 \ No newline at end of file diff --git a/hierarchy_heuristics/simplify_tree.py b/hierarchy_heuristics/simplify_tree.py new file mode 100644 index 0000000..e5ff293 --- /dev/null +++ b/hierarchy_heuristics/simplify_tree.py @@ -0,0 +1,327 @@ +import json +import math + +import cv2 +from bs4 import Tag + + +def extract_ids_from_output(item): + """ + Convert the extracted IDs to a list of integers. + The format is a bit complex to motivate the model to return a more accurate output. + """ + numbers = [] + if isinstance(item, dict): + for value in item.values(): + numbers.extend(extract_ids_from_output(value)) + elif isinstance(item, list): + for element in item: + numbers.extend(extract_ids_from_output(element)) + elif isinstance(item, int): + numbers.append(item) + return numbers + + +def create_xml_element(element): + # Only include elements with meaningful content + if ( + not any( + [ + element.get("name"), + element.get("description"), + element.get("value"), + element.get("children"), + ] + ) + and element["role"] == "AXGroup" + ): + return None + + attrib = { + "role_description": element.get("role_description", ""), + "id": element["id"], + "name": element.get("name") if element.get("name") else "", + "description": ( + element.get("description") if element.get("description") else "" + ), + "value": str(element["value"]) if element.get("value") is not None else "", + "position": element.get("position", "0.0;0.0"), + "size": element.get("size", "0.0;0.0"), + } + + xml_element = Tag(name=element["role"], attrs=attrib) + meaningful_children = list( + filter( + lambda x: x is not None, + [create_xml_element(child) for child in element.get("children", [])], + ) + ) + + # Merge condition: Check if it's a row with only one cell + if ( + element["role"] == "AXRow" + and len(meaningful_children) == 1 + and meaningful_children[0].name == "AXCell" + ): + cell = meaningful_children[0] + cell_children = list(cell.children) + if len(cell_children) == 1: + merged_element = cell_children[0] + merged_element["role_description"] = f"merged {attrib['role_description']}" + return merged_element + + for child in meaningful_children: + xml_element.append(child) + + # Remove elements that are groups without meaningful children + if not list(xml_element.children) and element["role"] == "AXGroup": + return None + + return xml_element + + +def pretty_print_xml(xml_tree): + if xml_tree is None: + return "" + + if type(xml_tree) == str: + return xml_tree + + return xml_tree.prettify() + + +def map_ids(element, id_mapping, current_id=1): + original_id = element["id"] + id_mapping[current_id] = original_id + element["id"] = str(current_id) + current_id += 1 + + for child in element.find_all(recursive=False): + current_id = map_ids(child, id_mapping, current_id) + + return current_id + + +def json_to_xml(json_obj): + id_mapping = {} + root_element = create_xml_element(json_obj) + if root_element is None: + return None, id_mapping + + # Map ids of elements + map_ids(root_element, id_mapping) + return root_element, id_mapping + + +def add_ids_to_json(json_obj, curr_id=0): + json_obj["id"] = curr_id + curr_id += 1 + + for child in json_obj["children"]: + _, curr_id = add_ids_to_json(child, curr_id) + curr_id += 1 + + return json_obj, curr_id + + +def format_json(json_object): + new_json_object = {} + new_json_object["@children"] = [] + + for key, value in json_object.items(): + print(key, value) + if not key.startswith("@"): + if isinstance(value, dict): + format_json(value) + value["@role"] = key + new_json_object["@children"].append(value) + else: + for child in value: + format_json(child) + child["@role"] = key + new_json_object["@children"].append(value) + else: + new_json_object[key] = value + + return new_json_object + + +def xml2dict(xml_object: Tag, width, height): + dict_object = {"role": xml_object.name, **xml_object.attrs, "children": list()} + position = xml_object.get("position", "0.0;0.0") + size = xml_object.get("size", "0.0;0.0") + + position = list(map(float, position.split(";"))) if position else (0, 0) + size = list(map(float, size.split(";"))) if size else (0, 0) + + try: + position = list(map(int, position)) + except Exception as e: + if abs(position[0]) == float("inf"): + position[0] = 0 if position[0] < 0 else width // 2 + elif math.isnan(position[0]): + position[0] = 0 + else: + position[0] = int(position[0]) + + if abs(position[1]) == float("inf"): + position[1] = 0 if position[1] < 0 else height // 2 + elif math.isnan(position[1]): + position[1] = 0 + else: + position[1] = int(position[1]) + + try: + size = list(map(int, size)) + except Exception as e: + if abs(size[0]) == float("inf"): + size[0] = 0 if position[0] < 0 else width // 2 - position[0] + elif math.isnan(size[0]): + size[0] = 0 + else: + size[0] = int(size[0]) + + if abs(size[1]) == float("inf"): + size[1] = 0 if size[1] < 0 else height // 2 - position[1] + elif math.isnan(size[1]): + size[1] = 0 + else: + size[1] = int(size[1]) + + del dict_object["position"] + del dict_object["size"] + + dict_object["xyxy"] = [*position, position[0] + size[0], position[1] + size[1]] + dict_object["xyxy_retina"] = [ + 2 * position[0], + 2 * position[1], + 2 * (position[0] + size[0]), + 2 * (position[1] + size[1]), + ] + + if dict_object["role"] == "AXWindow": + dict_object["xyxy"] = [0, 0, width // 2, height // 2] + dict_object["xyxy_retina"] = [0, 0, width, height] + + for child in xml_object.children: + dict_object["children"].append(xml2dict(child, width, height)) + + return dict_object + + +def simplify_tree(json_path: str, width, height): + with open(json_path, "r") as f: + json_object = json.load(f) + + json_object, _ = add_ids_to_json(json_object) + + root_element, id_mapping = json_to_xml(json_object) + + # convert to dict + root_element_json = xml2dict(root_element, width, height) + + # save + with open(f"{json_path[:-5]}_simplified.json", "w") as f: + json.dump(root_element_json, f, indent=2) + + +types = [ + "AXComboBox", + "AXLink", + "AXMenuBar", + "AXPage", + "AXHeading", + "AXListMarker", + "AXList", + "AXOpaqueProviderGroup", + "AXDateTimeArea", + "AXSlider", + "AXWindow", + "AXDisclosureTriangle", + "AXSheet", + "AXMenu", + "AXMenuButton", + "No role", + "AXCell", + "AXColorWell", + "AXTextField", + "AXIncrementor", + "AXScrollArea", + "AXButton", + "AXPopover", + "AXColumn", + "JavaAxIgnore", + "AXRadioButton", + "AXLevelIndicator", + "AXMenuItem", + "AXStaticText", + "AXRadioGroup", + "AXGroup", + "AXScrollBar", + "AXSplitGroup", + "AXToolbar", + "AXRuler", + "AXProgressIndicator", + "AXValueIndicator", + "AXTabGroup", + "AXGrowArea", + "AXImage", + "AXRow", + "AXGenericElement", + "AXWebArea", + "AXCheckBox", + "AXOutline", + "AXGrid", + "AXBrowser", + "AXSplitter", + "AXBusyIndicator", + "AXUnknown", + "AXTextArea", + "AXPopUpButton", + "AXTable", +] + +bgr_colors = [ + (255, 0, 0), # Blue + (0, 255, 0), # Green + (0, 0, 255), # Red + (255, 255, 0), # Yellow + (0, 255, 255), # Cyan + (255, 0, 255), # Magenta + (128, 0, 0), # Maroon + (0, 128, 0), # Dark Green + (0, 0, 128), # Navy + (128, 128, 0), # Olive + (128, 0, 128), # Purple + (0, 128, 128), # Teal + (192, 192, 192), # Silver + (128, 128, 128), # Gray + (255, 165, 0), # Orange + (255, 192, 203), # Pink + (210, 105, 30), # Chocolate + (34, 139, 34), # Forest Green + (255, 215, 0), # Gold + (135, 206, 250), # Sky Blue +] + + +def plot_json(img, json_object, save_path=None): + color = bgr_colors[types.index(json_object["role"]) % len(bgr_colors)] + + x1, y1, x2, y2 = json_object["xyxy_retina"] + cv2.rectangle(img, (x1, y1), (x2, y2), color, 2) + + label = json_object["role"] + text_color = (255, 255, 255) + + (w, h), _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.6, 1) + + # Prints the text + cv2.rectangle(img, (x1, y1 - 20), (x1 + w, y1), color, -1) + cv2.putText(img, label, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, text_color, 1) + + for child in json_object["children"]: + plot_json(img, child) + + if save_path is not None: + cv2.imwrite(save_path, img) diff --git a/hierarchy_heuristics/utils.py b/hierarchy_heuristics/utils.py new file mode 100644 index 0000000..a08b22c --- /dev/null +++ b/hierarchy_heuristics/utils.py @@ -0,0 +1,785 @@ +from collections import defaultdict +import os + +import cv2 +import matplotlib.pyplot as plt +import numpy as np +from PIL import Image +from ocrmac import ocrmac +from ultralytics import YOLO +import gdown + +from box import * + +os.makedirs("models", exist_ok=True) + +if not os.path.exists("./models/ui_types_best_v4.pt"): + print("Downloading element detection model...") + gdown.download( + "https://drive.google.com/uc?id=1kFmzkda5k-88Lp59P4LTRSq1HDzyHyQ5", + "./models/ui_types_best_v4.pt", + quiet=False, + ) + +if not os.path.exists("./models/clickability.pt"): + print("Downloading clickability model...") + gdown.download( + "https://drive.google.com/uc?id=1Q0GPtEgqpFmXOlIVC4Z2wJAzjLToWCLH", + "./models/clickability.pt", + quiet=False, + ) + +model = YOLO("./models/ui_types_best_v4.pt") +clickability_model = YOLO("./models/clickability.pt") + + +def plot_app(image, app, curr_depth=0, depths=[]): + if curr_depth == 0: + depths.clear() + img = None + if curr_depth >= len(depths): + img = image.copy() + depths.append(img) + else: + img = depths[curr_depth] + + if isinstance(app, Group): + app = [app] + + for box in app: + if isinstance(box, Group): + plot_app(image, box.children, curr_depth=curr_depth + 1) + cv2.rectangle(img, box.top_left, box.bottom_right, (0, 255, 0), 2) + else: + cv2.rectangle(img, box.top_left, box.bottom_right, (255, 0, 0), 2) + + # add class name and rectangle behind it so it is more visible + cv2.rectangle(img, (box.x1, box.y1), (box.x2, box.y1 - 20), (0, 0, 0), -1) + cv2.putText( + img, + Box.id2class[box.cls], + (box.x1, box.y1), + cv2.FONT_HERSHEY_SIMPLEX, + 0.9, + (255, 255, 255), + 2, + ) + + return depths + + +def plot_all_boxes(image, app, depth = 0): + cv2.putText(image, str(depth), app.top_left, cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 0), 2) + + if isinstance(app, Group): + cv2.rectangle(image, app.top_left, app.bottom_right, (255, 0, 0), 2) + + for child in app.children: + image = plot_all_boxes(image, child, depth + 1) + + else: + cv2.rectangle(image, app.top_left, app.bottom_right, (0, 0, 255), 2) + + return image + + +# ## Find texts, bind with boxes and merge +def ocr_image(img, conf_threshold=0.5) -> list[Text]: + ocr = ocrmac.OCR(Image.fromarray(img), language_preference=["en-US"]).recognize( + px=True + ) + return [Text(box, text) for text, conf, box in ocr if conf > conf_threshold] + + +def bind_text_and_boxes( + boxes: list[Box], text_boxes: list[Text], iou_threshold=0.5 +) -> list[Box]: + # bind Text to Boxes. Bind Text with largest iou but not less than iou_threshold + for text_box in text_boxes: + max_iou = 0 + max_box = None + + for box in boxes: + iou = text_box.iou(box) + + if iou > max_iou: + max_iou = iou + max_box = box + + if max_iou > iou_threshold: + max_box.text += text_box + else: + boxes.append(Box(text_box, Box.class2id["OCRText"], text=text_box)) + + return boxes + + +def mergable(box1: UIBox, box2: UIBox) -> int: + """ + Returns: + int: -1 if not mergable, class id if mergable + """ + if box1.cls == box2.cls: + return box1.cls + + text_class = [Box.class2id["OCRText"], Box.class2id["AXStaticText"]] + + text_mergeable = [ + Box.class2id["AXButton"], + Box.class2id["AXLink"], + Box.class2id["AXStaticText"], + Box.class2id["AXRadioButton"], + Box.class2id["AXCheckBox"], + Box.class2id["AXComboBox"], + Box.class2id["AXTextField"], + Box.class2id["AXHeading"], + ] + + # if static text and button or link + if box1.cls in text_class and box2.cls in text_mergeable: + return box2.cls + + if box2.cls in text_class and box1.cls in text_mergeable: + return box1.cls + + # TODO: forbid. Make image a child of button + if box1.cls == Box.class2id["AXButton"] and box2.cls == Box.class2id["AXImage"]: + return Box.class2id["AXButton"] + + if box2.cls == Box.class2id["AXButton"] and box1.cls == Box.class2id["AXImage"]: + return Box.class2id["AXButton"] + + return -1 + + +def merge_overlapping_boxes(boxes: list[UIBox], iou_threshold=0.5): + # sort by y1 + boxes.sort(key=lambda box: box.y1) + + i = 0 + while i < len(boxes): + j = i + 1 + + while j < len(boxes): + if i == j: + j += 1 + continue + + if boxes[i].x1 < boxes[j].x2 and boxes[j].x1 < boxes[i].x2: # x-overlap + merge_cls = mergable(boxes[i], boxes[j]) + + if boxes[i].y2 > boxes[j].y1 and merge_cls != -1: # y-overlap + iou = boxes[i].iou(boxes[j]) + + if iou > iou_threshold: + boxes[j].merge(boxes[i], inplace=True, cls=merge_cls) + + del boxes[i] + i -= 1 + + if j > i: + j -= 1 + + break + j += 1 + # else: + # break + + i += 1 + + return boxes + + +def text_grouping(boxes: list[Box]) -> list[UIBox]: + """ + https://docs-assets.developer.apple.com/ml-research/papers/screen-recognition-chi-2021.pdf + + We group a TextT1 with a Text below T2 if they satisfy: + 1) they have x-overlap, and + 2) the y-distance between the two texts should be less than a threshold — + we choose min(T1.heiht, T2.heiht). + """ + text_boxes = [ + box + for box in boxes + if (hasattr(box, "text") and box.text) + or (isinstance(box, Group) and box.group_type == "text") + ] + + text_boxes.sort(key=lambda box: box.text.y1) + + for i, box1 in enumerate(text_boxes): + group = Group(box1.text.box, group_type="text") + # group = Group(box1.box, group_type="text") + group.append(box1) + + for j, box2 in enumerate(text_boxes[i + 1 :], start=i + 1): + if i == j: + continue + + if box2.parent: + continue + + bbox = group.children[-1].text + + if ( + bbox.x1 < box2.text.x1 < bbox.x2 + or bbox.x1 < box2.text.x2 < bbox.x2 + or box2.text.x1 < bbox.x1 < box2.text.x2 + or box2.text.x1 < bbox.x2 < box2.text.x2 + ): # x-overlap + y_distance = bbox.y_distance(box2.text) + y_threshold = min(bbox.height, box2.text.height) + 15 + + if y_distance < y_threshold: + if box2 in boxes: + boxes.remove(box2) + + group.merge_bboxes(box2.text, inplace=True) + group.append(box2) + + if len(group.children) > 1: + if box1 in boxes: + boxes.remove(box1) + + group.finalize_bbox() + boxes.append(group) + + else: + box1.parent = None + + return boxes + + +# ## Create image groups (image + text) +# Also with button +def get_overlap_percent(image: BBox, text: BBox, coordinate="x") -> float: + if coordinate == "x": + overlap = max(0, min(image.x2, text.x2) - max(image.x1, text.x1)) + return overlap / max(image.width, text.width) + else: + if text.height > image.height * 1.1: + return 0 + + overlap = max(0, min(image.y2, text.y2) - max(image.y1, text.y1)) + return overlap / max(image.height, text.height) + + +def merge_images_and_captions( + boxes: list[UIBox], + screen_shape: list[int, int], + x_overlap_percent_threshold=0.25, + y_distance_threshold=0.02, + y_overlap_percent_threshold=0.4, + x_distance_threshold=0.02, +) -> list[UIBox]: + screen_height, screen_width = screen_shape + + # mergable elements + elements_with_caption = ( + Box.class2id["AXImage"], + Box.class2id["AXButton"], + Box.class2id["AXRadioButton"], + Box.class2id["AXCheckBox"], + Box.class2id["AXComboBox"], + ) + + images = [box for box in boxes if box.cls in elements_with_caption] + text_boxes = [ + box + for box in boxes + if (hasattr(box, "text") and box.text) + or (isinstance(box, Group) and box.group_type == "text") + ] + + images.sort(key=lambda box: box.y1, reverse=True) + text_boxes.sort(key=lambda box: box.y1, reverse=True) + + for image in images: + group = Group( + image.box, children=[image], group_type=Box.id2class[image.cls][2:] + ) + + for text in text_boxes: + if ( + text.parent + and text.parent.cls == Box.class2id["Group"] + and text.parent.group_type != "text" + ): + continue + + if ( + get_overlap_percent(group, text, coordinate="x") + > x_overlap_percent_threshold + ): + y_distance = min( + abs(group.y2 - text.y1), abs(group.y2 - text.y2) + ) # distance between image and text. image must be above text + y_threshold = y_distance_threshold * screen_height + + if y_distance < y_threshold: + if text in boxes: + boxes.remove(text) + + group.merge_bboxes(text, inplace=True) + group.append(text) + text.parent = group + + if ( + get_overlap_percent(group, text, coordinate="y") + > y_overlap_percent_threshold + ): + x_distance = group.x_distance( + text + ) # image might be on the left or right of text + x_threshold = x_distance_threshold * screen_width + + if x_distance < x_threshold: + if text in boxes: + boxes.remove(text) + + group.merge_bboxes(text, inplace=True) + group.append(text) + text.parent = group + + if len(group.children) > 1: + boxes.append(group) + image.parent = group + + if image in boxes: + boxes.remove(image) + + else: + image.parent = None + + return boxes + + +# ## Group by columns +# elements that are +# 1) close to each other (y coordinate) +# 2) has +- same x1 (maybe x2 too?) +# 3) +- same height? +# 4) has same type? +# +def group_by_column( + boxes: list[UIBox], + y_distance_coefficient=1.25, + width_threshold=40, + max_width_coefficient=0.55, +) -> list[UIBox]: + boxes.sort(key=lambda box: box.x1) + + i = 0 + while i < len(boxes): + box1 = boxes[i] + group = Group(box1.box, children=[box1], group_type="column") + + j = 0 + while j < len(boxes): + if i == j: + j += 1 + continue + + box2 = boxes[j] + + min_height = min(group.height, box2.height) + + if ( + group.y_distance(box2) < y_distance_coefficient * min_height + ): # which thresold to use? + max_width = max(group.width, box2.width) + min_width = min(group.width, box2.width) + + if ( + abs(group.x1 - box2.x1) < width_threshold + or abs(group.x2 - box2.x2) < width_threshold + ) and max_width * max_width_coefficient < min_width: + if box2 in boxes: + j -= 1 + boxes.remove(box2) + + if i > j: + i -= 1 + + group.merge_bboxes(box2, inplace=True) + group.append(box2) + + j += 1 + + if len(group.children) > 1: + if box1 in boxes: + boxes.remove(box1) + i -= 1 + + box1.parent = group + boxes.append(group) + + else: + box1.parent = None + + i += 1 + + return boxes + + +# create histogram of colors in image + + +def color_histogram(img, threshold): + # Read the grayscale image + unique_colors, counts = np.unique(img, return_counts=True) + + # Create a dictionary to hold color areas + color_area_dict = defaultdict(int) + + # Populate the dictionary with colors and their areas + for color, count in zip(unique_colors, counts): + color_area_dict[color] += count + + # Convert dictionary to arrays for faster processing + colors = np.array(list(color_area_dict.keys())) + areas = np.array(list(color_area_dict.values())) + + # Merging colors based on the threshold + merged_colors = [] + merged_areas = [] + + while colors.size > 0: + base_color = colors[0] + merged_area = areas[0] + + # Create a mask for similar colors + mask = np.abs(colors - base_color) <= threshold + + # Sum areas of similar colors + merged_area += areas[mask].sum() + + # Append merged color and area + merged_colors.append(base_color) + merged_areas.append(merged_area) + + # Remove merged colors from the list + colors = colors[~mask] + areas = areas[~mask] + + # Sort colors by area in descending order + sorted_indices = np.argsort(merged_areas)[::-1] + sorted_colors = [merged_colors[i] for i in sorted_indices] + sorted_areas = [merged_areas[i] for i in sorted_indices] + + return sorted_colors, sorted_areas, sum(sorted_areas) + + +def get_mask(img, threshold=2, area_threshold=0.01): + colors_list, sorted_areas, colors_sum = color_histogram(img, threshold) + + for i in range(len(colors_list)): + color = colors_list[i] + + predicted_perc = sorted_areas[i] / colors_sum + if predicted_perc < area_threshold: + break + + lower_bound = np.int16([color]) - threshold + upper_bound = np.int16([color]) + threshold + + lower_bound = np.where(lower_bound < 0, 0, lower_bound) + upper_bound = np.where(upper_bound > 255, 255, upper_bound) + + mask = (img >= lower_bound) & (img <= upper_bound) + + elements = np.sum(np.sum(mask)) + + percentage = elements / img.size + if percentage < area_threshold: + break + + mask = mask.astype(np.uint8) * 255 + + yield mask, percentage + + +def add_color_groups( + img, + boxes: list[UIBox], + verbose=0, + min_box_threshold=0.03, + max_box_threshold=0.95, + color_diff_threshold=2, + area_threshold=0.01, +): + min_area = min_box_threshold * img.size + max_area = max_box_threshold * img.size + + kernel = np.ones((10, 10), np.uint8) + + color_groups: list[Group] = [] + + for mask, percentage in get_mask(img, color_diff_threshold, area_threshold): + if verbose >= 1: + print(f"Percentage: {percentage * 100:.2f}%") + + original_mask = mask.copy() + + mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel) + + if verbose >= 3: + mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2RGB) + plt.imshow(mask) + plt.show() + mask = cv2.cvtColor(mask, cv2.COLOR_RGB2GRAY) + + contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)[ + -2 + ] + for cnt in contours: + x, y, w, h = cv2.boundingRect(cnt) + box_area = w * h + + if not (min_area < box_area < max_area): + continue + + group = Group((x, y, x + w, y + h), group_type="color") + color_groups.append(group) + + if verbose >= 1: + print(f" - Box area: {box_area / img.size * 100:.5f}%") + + if verbose >= 2: + mask_copy = original_mask.copy() + mask_copy = cv2.cvtColor(mask_copy, cv2.COLOR_GRAY2BGR) + cv2.rectangle(mask_copy, (x, y), (x + w, y + h), (255, 0, 0), 2) + plt.imshow(mask_copy) + plt.show() + + if verbose >= 1: + print("End") + + color_groups.sort(key=lambda group: group.area) + + # add boxes to color groups + for color_group in color_groups: + i = 0 + while i < len(boxes): + box = boxes[i] + + if box.parent: + continue + + # box is inside color box + if color_group.other_is_inside(box, margin=10): + color_group.append(box) + + if box in boxes: + boxes.remove(box) + i -= 1 + + i += 1 + + color_groups = [box for box in color_groups if box.children] + + # merge color groups + i = 0 + while i < len(color_groups): + gr1 = color_groups[i] + j = 0 + + while j < len(color_groups): + if i == j: + j += 1 + continue + + gr2 = color_groups[j] + + if gr1.other_is_inside(gr2): + gr1.append(gr2) + color_groups.remove(gr2) + j -= 1 + + if i > j: + i -= 1 + + j += 1 + + i += 1 + + boxes += color_groups + + return boxes + + +# ## Group by row +def group_by_row(app: Group, y_distance_threshold=20, height_threshold=0.8) -> Group: + app.children.sort(key=lambda group: group.x1) + + for child in app.children: + if isinstance(child, Group): + group_by_row(child, y_distance_threshold, height_threshold) + + i = 0 + while i < len(app.children): + box1 = app.children[i] + + row_group = Group(box1.box, group_type="row") + prev_parent = box1.parent + row_group.append(box1) + + j = 0 + while j < len(app.children): + box2 = app.children[j] + + if box1 is box2: + j += 1 + continue + + # if they have +- same y1 or y2 coordinate + if ( + abs(box2.y1 - row_group.y1) <= y_distance_threshold + or abs(box2.y2 - row_group.y2) <= y_distance_threshold + ): + # if they have +- same height + min_height = min(row_group.height, box2.height) + max_height = max(row_group.height, box2.height) + + if min_height / max_height > height_threshold: + if box2 in app.children: + app.children.remove(box2) + j -= 1 + + if i > j: + i -= 1 + + row_group.merge_bboxes(box2, inplace=True) + row_group.append(box2) + + j += 1 + + if len(row_group.children) > 1: + app.children[i] = row_group + else: + box1.parent = prev_parent + + i += 1 + + return app + + +def add_clickability(image, boxes, verbose, conf=0.3): + clickability_pred = clickability_model(image, verbose=verbose, conf=conf)[0] + clickability_boxes = zip(clickability_pred.boxes.xyxy, clickability_pred.boxes.cls) + clickability_boxes = [(BBox(box), cls) for box, cls in clickability_boxes] + + for box in boxes: + if box.cls in ( + Box.class2id["AXStaticText"], + Box.class2id["OCRText"], + Box.class2id["AXImage"], + ): + max_iou = 0 + max_elem = None + + for bbox, cls in clickability_boxes: + curr_iou = box.iou(bbox) + + if curr_iou > 0.1 and curr_iou > max_iou: + max_iou = curr_iou + max_elem = (bbox, cls) + + if max_elem and max_elem[1] == 1: + box.cls = Box.class2id["AXButton"] + box.merge_bboxes(max_elem[0], inplace=True) + + return boxes + + +def group_elements(img: np.ndarray, **kwargs) -> Group: + verbose = kwargs.get("verbose", False) + preds = model(img, conf=kwargs.get("ui_model_conf", 0.3), verbose=verbose)[0] + + img = cv2.cvtColor(preds.orig_img, cv2.COLOR_BGR2RGB) + img_gray = cv2.cvtColor(img.copy(), cv2.COLOR_BGR2GRAY) + + boxes, classes = ( + preds.boxes.xyxy, + preds.boxes.cls, + ) + boxes = [Box(box, cls) for box, cls in zip(boxes, classes)] + + # use OCR to get more boxes + ocr_text = ocr_image(img, conf_threshold=kwargs.get("ocr_conf_threshold", 0.3)) + + boxes = bind_text_and_boxes( + boxes, + ocr_text, + iou_threshold=kwargs.get("bind_text_and_boxes_iou_threshold", 0.05), + ) + + # # delete boxes with iou > threshold + boxes = merge_overlapping_boxes( + boxes, iou_threshold=kwargs.get("merge_overlapping_boxes_iou_threshold", 0.3) + ) + + # add clickability + boxes = add_clickability( + img, boxes, verbose, conf=kwargs.get("clickability_model_conf", 0.3) + ) + + # # text grouping + boxes = text_grouping(boxes) + + boxes = merge_images_and_captions( + boxes, + img.shape[:2], + x_overlap_percent_threshold=kwargs.get( + "merge_images_and_captions_x_overlap_percent_threshold", 0.25 + ), + y_distance_threshold=kwargs.get( + "merge_images_and_captions_y_distance_threshold", 0.02 + ), + y_overlap_percent_threshold=kwargs.get( + "merge_images_and_captions_y_overlap_percent_threshold", 0.4 + ), + x_distance_threshold=kwargs.get( + "merge_images_and_captions_x_distance_threshold", 0.02 + ), + ) + + boxes = group_by_column( + boxes, + y_distance_coefficient=kwargs.get( + "group_by_column_y_distance_coefficient", 1.25 + ), + width_threshold=kwargs.get("group_by_column_width_threshold", 40), + max_width_coefficient=kwargs.get("group_by_column_max_width_coefficient", 0.55), + ) + + # # color groups + boxes = add_color_groups( + img_gray, + boxes, + verbose=0, + min_box_threshold=kwargs.get("add_color_groups_min_box_threshold", 0.03), + max_box_threshold=kwargs.get("add_color_groups_max_box_threshold", 0.95), + color_diff_threshold=kwargs.get("add_color_groups_color_diff_threshold", 2), + area_threshold=kwargs.get("add_color_groups_area_threshold", 0.01), + ) + + app = Group((0, 0, img.shape[1], img.shape[0]), children=boxes, group_type="Window") + + app = group_by_row( + app, + y_distance_threshold=kwargs.get("group_by_row_y_distance_threshold", 10), + height_threshold=kwargs.get("group_by_row_height_threshold", 0.8), + ) + + if verbose: + depths = plot_app(img, app) + + for i, img_plot in enumerate(depths): + print(f"Depth: {i}") + plt.figure(figsize=(15, 15)) + plt.imshow(img_plot) + plt.show() + + return app diff --git a/output_visualisation/example_data/custom_acc.json b/output_visualisation/example_data/custom_acc.json new file mode 100644 index 0000000..37ff305 --- /dev/null +++ b/output_visualisation/example_data/custom_acc.json @@ -0,0 +1,2915 @@ +{ + "cls": "Group", + "value": null, + "box": [ + 0, + 0, + 3456, + 1976 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 173, + 14, + 261, + 82 + ], + "children": [ + { + "cls": "AXButton", + "value": "go to news | news | news", + "box": [ + 171, + 0, + 261, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3101, + 13, + 3180, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "add to cart | go to cart | go to cart", + "box": [ + 3097, + 0, + 3185, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2998, + 8, + 3091, + 91 + ], + "children": [ + { + "cls": "AXButton", + "value": "edit | edit text | edit text", + "box": [ + 3006, + 0, + 3092, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3187, + 12, + 3287, + 92 + ], + "children": [ + { + "cls": "AXButton", + "value": "click to save | save | save the article", + "box": [ + 3190, + 0, + 3275, + 100 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3275, + 15, + 3398, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "delete | delete the file | delete the file", + "box": [ + 3276, + 0, + 3361, + 100 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "save | save the image | save the image", + "box": [ + 3362, + 1, + 3447, + 98 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3187, + 12, + 3287, + 92 + ], + "children": [ + { + "cls": "AXButton", + "value": "click to save | save | save the article", + "box": [ + 3190, + 0, + 3275, + 100 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 5, + 98, + 261, + 140 + ], + "children": [ + { + "cls": "Text", + "value": "Al Pro", + "box": [ + 25, + 110, + 95, + 135 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 13, + 140, + 260, + 195 + ], + "children": [ + { + "cls": "AXImage", + "value": "(AI", + "box": [ + 35, + 153, + 70, + 185 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Chats", + "box": [ + 80, + 160, + 155, + 185 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "add a | add to chat | send message", + "box": [ + 32, + 150, + 67, + 190 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 4, + 252, + 260, + 308 + ], + "children": [ + { + "cls": "AXButton", + "value": "\uf8ff Referral", + "box": [ + 35, + 264, + 178, + 296 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "add to cart | add to cart | go to cart", + "box": [ + 33, + 264, + 66, + 296 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 7, + 195, + 260, + 252 + ], + "children": [ + { + "cls": "AXButton", + "value": "Help", + "box": [ + 70, + 208, + 140, + 240 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "go to search | search", + "box": [ + 34, + 208, + 66, + 240 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2, + 308, + 258, + 365 + ], + "children": [ + { + "cls": "AXButton", + "value": "M About", + "box": [ + 35, + 321, + 160, + 352 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 288, + 271, + 823, + 320 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 284, + 437, + 312 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Now", + "box": [ + 758, + 285, + 813, + 305 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 287, + 318, + 820, + 371 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 330, + 437, + 356 + ], + "children": [] + }, + { + "cls": "Text", + "value": "11:48", + "box": [ + 753, + 331, + 813, + 356 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 288, + 271, + 823, + 320 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 284, + 437, + 312 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Now", + "box": [ + 758, + 285, + 813, + 305 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3022, + 847, + 3443, + 924 + ], + "children": [ + { + "cls": "Text", + "value": "Guide", + "box": [ + 3099, + 867, + 3214, + 912 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 283, + 208, + 825, + 269 + ], + "children": [ + { + "cls": "Text", + "value": "\u2022 History", + "box": [ + 316, + 235, + 442, + 260 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2829, + 969, + 3443, + 1093 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 2822, + 967, + 3431, + 1101 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Set up a budget\nfor a student.", + "box": [ + 2863, + 997, + 3064, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the home page | go to the home page | go to home page", + "box": [ + 2829, + 960, + 3410, + 1101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1589, + 966, + 2198, + 1094 + ], + "children": [ + { + "cls": "Text", + "value": "Start conversations\nconfidently at events.", + "box": [ + 1632, + 997, + 1898, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | start conversations | start conversations", + "box": [ + 1588, + 964, + 2201, + 1099 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2204, + 965, + 2820, + 1095 + ], + "children": [ + { + "cls": "Text", + "value": "Organize vegetarian meals\nfor a busy student.", + "box": [ + 2245, + 997, + 2576, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | open the homepage", + "box": [ + 2208, + 962, + 2816, + 1100 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2211, + 958, + 3402, + 1097 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 2829, + 969, + 3443, + 1093 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 2822, + 967, + 3431, + 1101 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Set up a budget\nfor a student.", + "box": [ + 2863, + 997, + 3064, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the home page | go to the home page | go to home page", + "box": [ + 2829, + 960, + 3410, + 1101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2204, + 965, + 2820, + 1095 + ], + "children": [ + { + "cls": "Text", + "value": "Organize vegetarian meals\nfor a busy student.", + "box": [ + 2245, + 997, + 2576, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | open the homepage", + "box": [ + 2208, + 962, + 2816, + 1100 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1661, + 842, + 3456, + 951 + ], + "children": [ + { + "cls": "Text", + "value": "Help", + "box": [ + 1883, + 867, + 1974, + 917 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Plan", + "box": [ + 2501, + 872, + 2581, + 907 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3022, + 847, + 3443, + 924 + ], + "children": [ + { + "cls": "Text", + "value": "Guide", + "box": [ + 3099, + 867, + 3214, + 912 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1575, + 952, + 3189, + 1100 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 1589, + 966, + 2198, + 1094 + ], + "children": [ + { + "cls": "Text", + "value": "Start conversations\nconfidently at events.", + "box": [ + 1632, + 997, + 1898, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | start conversations | start conversations", + "box": [ + 1588, + 964, + 2201, + 1099 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2211, + 958, + 3402, + 1097 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 2829, + 969, + 3443, + 1093 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 2822, + 967, + 3431, + 1101 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Set up a budget\nfor a student.", + "box": [ + 2863, + 997, + 3064, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the home page | go to the home page | go to home page", + "box": [ + 2829, + 960, + 3410, + 1101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2204, + 965, + 2820, + 1095 + ], + "children": [ + { + "cls": "Text", + "value": "Organize vegetarian meals\nfor a busy student.", + "box": [ + 2245, + 997, + 2576, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | open the homepage", + "box": [ + 2208, + 962, + 2816, + 1100 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 835, + 844, + 3164, + 947 + ], + "children": [ + { + "cls": "Text", + "value": "2/ Write", + "box": [ + 1179, + 865, + 1371, + 920 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1661, + 842, + 3456, + 951 + ], + "children": [ + { + "cls": "Text", + "value": "Help", + "box": [ + 1883, + 867, + 1974, + 917 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Plan", + "box": [ + 2501, + 872, + 2581, + 907 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3022, + 847, + 3443, + 924 + ], + "children": [ + { + "cls": "Text", + "value": "Guide", + "box": [ + 3099, + 867, + 3214, + 912 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 762, + 0, + 3214, + 103 + ], + "children": [ + { + "cls": "AXImage", + "value": "+", + "box": [ + 766, + 26, + 815, + 75 + ], + "children": [] + }, + { + "cls": "Text", + "value": "GPT-40 v", + "box": [ + 884, + 40, + 1019, + 65 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the previous page | go to the previous page | go to the previous page", + "box": [ + 853, + 18, + 1035, + 87 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3101, + 13, + 3180, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "add to cart | go to cart | go to cart", + "box": [ + 3097, + 0, + 3185, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2998, + 8, + 3091, + 91 + ], + "children": [ + { + "cls": "AXButton", + "value": "edit | edit text | edit text", + "box": [ + 3006, + 0, + 3092, + 101 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 792, + 1809, + 3315, + 1961 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 872, + 1786, + 1900, + 1818 + ], + "children": [] + }, + { + "cls": "AXTextArea", + "value": null, + "box": [ + 874, + 1787, + 1502, + 1819 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Message", + "box": [ + 894, + 1900, + 1014, + 1930 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 0, + 107, + 275, + 1944 + ], + "children": [ + { + "cls": "Text", + "value": "v2.0.5 (78)", + "box": [ + 30, + 1910, + 145, + 1936 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to settings | settings", + "box": [ + 190, + 1901, + 267, + 1960 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to previous | select the number | select number", + "box": [ + 0, + 1898, + 181, + 1964 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 5, + 98, + 261, + 140 + ], + "children": [ + { + "cls": "Text", + "value": "Al Pro", + "box": [ + 25, + 110, + 95, + 135 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 13, + 140, + 260, + 195 + ], + "children": [ + { + "cls": "AXImage", + "value": "(AI", + "box": [ + 35, + 153, + 70, + 185 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Chats", + "box": [ + 80, + 160, + 155, + 185 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "add a | add to chat | send message", + "box": [ + 32, + 150, + 67, + 190 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 4, + 252, + 260, + 308 + ], + "children": [ + { + "cls": "AXButton", + "value": "\uf8ff Referral", + "box": [ + 35, + 264, + 178, + 296 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "add to cart | add to cart | go to cart", + "box": [ + 33, + 264, + 66, + 296 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 7, + 195, + 260, + 252 + ], + "children": [ + { + "cls": "AXButton", + "value": "Help", + "box": [ + 70, + 208, + 140, + 240 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "go to search | search", + "box": [ + 34, + 208, + 66, + 240 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2, + 308, + 258, + 365 + ], + "children": [ + { + "cls": "AXButton", + "value": "M About", + "box": [ + 35, + 321, + 160, + 352 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 277, + 204, + 842, + 1873 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 287, + 318, + 820, + 371 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 330, + 437, + 356 + ], + "children": [] + }, + { + "cls": "Text", + "value": "11:48", + "box": [ + 753, + 331, + 813, + 356 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 288, + 271, + 823, + 320 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 284, + 437, + 312 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Now", + "box": [ + 758, + 285, + 813, + 305 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 283, + 208, + 825, + 269 + ], + "children": [ + { + "cls": "Text", + "value": "\u2022 History", + "box": [ + 316, + 235, + 442, + 260 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 835, + 844, + 3164, + 947 + ], + "children": [ + { + "cls": "Text", + "value": "2/ Write", + "box": [ + 1179, + 865, + 1371, + 920 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1661, + 842, + 3456, + 951 + ], + "children": [ + { + "cls": "Text", + "value": "Help", + "box": [ + 1883, + 867, + 1974, + 917 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Plan", + "box": [ + 2501, + 872, + 2581, + 907 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3022, + 847, + 3443, + 924 + ], + "children": [ + { + "cls": "Text", + "value": "Guide", + "box": [ + 3099, + 867, + 3214, + 912 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 792, + 1809, + 3315, + 1961 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 872, + 1786, + 1900, + 1818 + ], + "children": [] + }, + { + "cls": "AXTextArea", + "value": null, + "box": [ + 874, + 1787, + 1502, + 1819 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Message", + "box": [ + 894, + 1900, + 1014, + 1930 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 850, + 0, + 3451, + 864 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 3275, + 15, + 3398, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "delete | delete the file | delete the file", + "box": [ + 3276, + 0, + 3361, + 100 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "save | save the image | save the image", + "box": [ + 3362, + 1, + 3447, + 98 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3187, + 12, + 3287, + 92 + ], + "children": [ + { + "cls": "AXButton", + "value": "click to save | save | save the article", + "box": [ + 3190, + 0, + 3275, + 100 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 762, + 0, + 3214, + 103 + ], + "children": [ + { + "cls": "AXImage", + "value": "+", + "box": [ + 766, + 26, + 815, + 75 + ], + "children": [] + }, + { + "cls": "Text", + "value": "GPT-40 v", + "box": [ + 884, + 40, + 1019, + 65 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the previous page | go to the previous page | go to the previous page", + "box": [ + 853, + 18, + 1035, + 87 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3101, + 13, + 3180, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "add to cart | go to cart | go to cart", + "box": [ + 3097, + 0, + 3185, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2998, + 8, + 3091, + 91 + ], + "children": [ + { + "cls": "AXButton", + "value": "edit | edit text | edit text", + "box": [ + 3006, + 0, + 3092, + 101 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 833, + 960, + 3456, + 1967 + ], + "children": [ + { + "cls": "Text", + "value": "Pen a dog's diary\nfor a day.", + "box": [ + 1014, + 997, + 1230, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to previous | go to previous page | previous page", + "box": [ + 3372, + 1886, + 3431, + 1946 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1575, + 952, + 3189, + 1100 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 1589, + 966, + 2198, + 1094 + ], + "children": [ + { + "cls": "Text", + "value": "Start conversations\nconfidently at events.", + "box": [ + 1632, + 997, + 1898, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | start conversations | start conversations", + "box": [ + 1588, + 964, + 2201, + 1099 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2211, + 958, + 3402, + 1097 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 2829, + 969, + 3443, + 1093 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 2822, + 967, + 3431, + 1101 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Set up a budget\nfor a student.", + "box": [ + 2863, + 997, + 3064, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the home page | go to the home page | go to home page", + "box": [ + 2829, + 960, + 3410, + 1101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2204, + 965, + 2820, + 1095 + ], + "children": [ + { + "cls": "Text", + "value": "Organize vegetarian meals\nfor a busy student.", + "box": [ + 2245, + 997, + 2576, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | open the homepage", + "box": [ + 2208, + 962, + 2816, + 1100 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 277, + 204, + 842, + 1873 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 287, + 318, + 820, + 371 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 330, + 437, + 356 + ], + "children": [] + }, + { + "cls": "Text", + "value": "11:48", + "box": [ + 753, + 331, + 813, + 356 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 288, + 271, + 823, + 320 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 284, + 437, + 312 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Now", + "box": [ + 758, + 285, + 813, + 305 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 283, + 208, + 825, + 269 + ], + "children": [ + { + "cls": "Text", + "value": "\u2022 History", + "box": [ + 316, + 235, + 442, + 260 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 835, + 844, + 3164, + 947 + ], + "children": [ + { + "cls": "Text", + "value": "2/ Write", + "box": [ + 1179, + 865, + 1371, + 920 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1661, + 842, + 3456, + 951 + ], + "children": [ + { + "cls": "Text", + "value": "Help", + "box": [ + 1883, + 867, + 1974, + 917 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Plan", + "box": [ + 2501, + 872, + 2581, + 907 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3022, + 847, + 3443, + 924 + ], + "children": [ + { + "cls": "Text", + "value": "Guide", + "box": [ + 3099, + 867, + 3214, + 912 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 792, + 1809, + 3315, + 1961 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 872, + 1786, + 1900, + 1818 + ], + "children": [] + }, + { + "cls": "AXTextArea", + "value": null, + "box": [ + 874, + 1787, + 1502, + 1819 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Message", + "box": [ + 894, + 1900, + 1014, + 1930 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 0, + 19, + 3438, + 1976 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 299, + 121, + 820, + 185 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Chats", + "box": [ + 306, + 39, + 396, + 65 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Q Search", + "box": [ + 311, + 139, + 447, + 171 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the next page | go to the next page", + "box": [ + 34, + 34, + 66, + 66 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 173, + 14, + 261, + 82 + ], + "children": [ + { + "cls": "AXButton", + "value": "go to news | news | news", + "box": [ + 171, + 0, + 261, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 0, + 107, + 275, + 1944 + ], + "children": [ + { + "cls": "Text", + "value": "v2.0.5 (78)", + "box": [ + 30, + 1910, + 145, + 1936 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to settings | settings", + "box": [ + 190, + 1901, + 267, + 1960 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to previous | select the number | select number", + "box": [ + 0, + 1898, + 181, + 1964 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 5, + 98, + 261, + 140 + ], + "children": [ + { + "cls": "Text", + "value": "Al Pro", + "box": [ + 25, + 110, + 95, + 135 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 13, + 140, + 260, + 195 + ], + "children": [ + { + "cls": "AXImage", + "value": "(AI", + "box": [ + 35, + 153, + 70, + 185 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Chats", + "box": [ + 80, + 160, + 155, + 185 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "add a | add to chat | send message", + "box": [ + 32, + 150, + 67, + 190 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 4, + 252, + 260, + 308 + ], + "children": [ + { + "cls": "AXButton", + "value": "\uf8ff Referral", + "box": [ + 35, + 264, + 178, + 296 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "add to cart | add to cart | go to cart", + "box": [ + 33, + 264, + 66, + 296 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 7, + 195, + 260, + 252 + ], + "children": [ + { + "cls": "AXButton", + "value": "Help", + "box": [ + 70, + 208, + 140, + 240 + ], + "children": [] + }, + { + "cls": "AXImage", + "value": "go to search | search", + "box": [ + 34, + 208, + 66, + 240 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2, + 308, + 258, + 365 + ], + "children": [ + { + "cls": "AXButton", + "value": "M About", + "box": [ + 35, + 321, + 160, + 352 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 850, + 0, + 3451, + 864 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 3275, + 15, + 3398, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "delete | delete the file | delete the file", + "box": [ + 3276, + 0, + 3361, + 100 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "save | save the image | save the image", + "box": [ + 3362, + 1, + 3447, + 98 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3187, + 12, + 3287, + 92 + ], + "children": [ + { + "cls": "AXButton", + "value": "click to save | save | save the article", + "box": [ + 3190, + 0, + 3275, + 100 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 762, + 0, + 3214, + 103 + ], + "children": [ + { + "cls": "AXImage", + "value": "+", + "box": [ + 766, + 26, + 815, + 75 + ], + "children": [] + }, + { + "cls": "Text", + "value": "GPT-40 v", + "box": [ + 884, + 40, + 1019, + 65 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the previous page | go to the previous page | go to the previous page", + "box": [ + 853, + 18, + 1035, + 87 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3101, + 13, + 3180, + 89 + ], + "children": [ + { + "cls": "AXButton", + "value": "add to cart | go to cart | go to cart", + "box": [ + 3097, + 0, + 3185, + 101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2998, + 8, + 3091, + 91 + ], + "children": [ + { + "cls": "AXButton", + "value": "edit | edit text | edit text", + "box": [ + 3006, + 0, + 3092, + 101 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 833, + 960, + 3456, + 1967 + ], + "children": [ + { + "cls": "Text", + "value": "Pen a dog's diary\nfor a day.", + "box": [ + 1014, + 997, + 1230, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to previous | go to previous page | previous page", + "box": [ + 3372, + 1886, + 3431, + 1946 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1575, + 952, + 3189, + 1100 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 1589, + 966, + 2198, + 1094 + ], + "children": [ + { + "cls": "Text", + "value": "Start conversations\nconfidently at events.", + "box": [ + 1632, + 997, + 1898, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | start conversations | start conversations", + "box": [ + 1588, + 964, + 2201, + 1099 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2211, + 958, + 3402, + 1097 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 2829, + 969, + 3443, + 1093 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 2822, + 967, + 3431, + 1101 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Set up a budget\nfor a student.", + "box": [ + 2863, + 997, + 3064, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to the home page | go to the home page | go to home page", + "box": [ + 2829, + 960, + 3410, + 1101 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 2204, + 965, + 2820, + 1095 + ], + "children": [ + { + "cls": "Text", + "value": "Organize vegetarian meals\nfor a busy student.", + "box": [ + 2245, + 997, + 2576, + 1058 + ], + "children": [] + }, + { + "cls": "AXButton", + "value": "go to home page | open the homepage", + "box": [ + 2208, + 962, + 2816, + 1100 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 277, + 204, + 842, + 1873 + ], + "children": [ + { + "cls": "Group", + "value": null, + "box": [ + 287, + 318, + 820, + 371 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 330, + 437, + 356 + ], + "children": [] + }, + { + "cls": "Text", + "value": "11:48", + "box": [ + 753, + 331, + 813, + 356 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 288, + 271, + 823, + 320 + ], + "children": [ + { + "cls": "Text", + "value": "New Chat", + "box": [ + 311, + 284, + 437, + 312 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Now", + "box": [ + 758, + 285, + 813, + 305 + ], + "children": [] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 283, + 208, + 825, + 269 + ], + "children": [ + { + "cls": "Text", + "value": "\u2022 History", + "box": [ + 316, + 235, + 442, + 260 + ], + "children": [] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 835, + 844, + 3164, + 947 + ], + "children": [ + { + "cls": "Text", + "value": "2/ Write", + "box": [ + 1179, + 865, + 1371, + 920 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 1661, + 842, + 3456, + 951 + ], + "children": [ + { + "cls": "Text", + "value": "Help", + "box": [ + 1883, + 867, + 1974, + 917 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Plan", + "box": [ + 2501, + 872, + 2581, + 907 + ], + "children": [] + }, + { + "cls": "Group", + "value": null, + "box": [ + 3022, + 847, + 3443, + 924 + ], + "children": [ + { + "cls": "Text", + "value": "Guide", + "box": [ + 3099, + 867, + 3214, + 912 + ], + "children": [] + } + ] + } + ] + } + ] + }, + { + "cls": "Group", + "value": null, + "box": [ + 792, + 1809, + 3315, + 1961 + ], + "children": [ + { + "cls": "AXTextArea", + "value": null, + "box": [ + 872, + 1786, + 1900, + 1818 + ], + "children": [] + }, + { + "cls": "AXTextArea", + "value": null, + "box": [ + 874, + 1787, + 1502, + 1819 + ], + "children": [] + }, + { + "cls": "Text", + "value": "Message", + "box": [ + 894, + 1900, + 1014, + 1930 + ], + "children": [] + } + ] + } + ] + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/output_visualisation/example_data/screenshot.png b/output_visualisation/example_data/screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..5c027ec7a14241aabcaf5a05d864345691dd4e23 GIT binary patch literal 278138 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

b`OZj7fd;=e!8&XoLWHs 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¥6wfY2gghA>D4qVE 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 literal 0 HcmV?d00001 diff --git a/output_visualisation/example_data/system_acc.json b/output_visualisation/example_data/system_acc.json new file mode 100644 index 0000000..bfce793 --- /dev/null +++ b/output_visualisation/example_data/system_acc.json @@ -0,0 +1,1037 @@ +{ + "name": "Chats", + "role": "AXWindow", + "description": null, + "role_description": "standard window", + "value": null, + "position": "0.00;38.00", + "size": "1728;988", + "children": [ + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "0.00;0.00", + "size": "1728;988", + "children": [ + { + "name": null, + "role": "AXSplitGroup", + "description": null, + "role_description": "split group", + "value": null, + "position": "0.00;0.00", + "size": "1728;988", + "children": [ + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "0.00;0.00", + "size": "140;988", + "children": [ + { + "name": null, + "role": "AXScrollArea", + "description": null, + "role_description": "scroll area", + "value": null, + "position": "0.00;52.00", + "size": "140;896", + "children": [ + { + "name": null, + "role": "AXOutline", + "description": "Sidebar", + "role_description": "outline", + "value": null, + "position": "0.00;52.00", + "size": "140;896", + "children": [ + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "outline row", + "value": null, + "position": "0.00;52.00", + "size": "140;19", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "10.00;52.00", + "size": "120;19", + "children": [ + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "14.00;52.00", + "size": "124;19", + "children": [ + { + "name": null, + "role": "AXHeading", + "description": "AI Pro", + "role_description": "heading", + "value": null, + "position": "14.00;54.50", + "size": "33;14", + "children": [] + } + ] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "outline row", + "value": null, + "position": "0.00;71.00", + "size": "140;28", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "10.00;71.00", + "size": "120;28", + "children": [ + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Chats", + "position": "19.00;76.79", + "size": "56;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "outline row", + "value": null, + "position": "0.00;99.00", + "size": "140;28", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "10.00;99.00", + "size": "120;28", + "children": [ + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Help", + "position": "19.50;105.00", + "size": "48;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "outline row", + "value": null, + "position": "0.00;127.00", + "size": "140;28", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "10.00;127.00", + "size": "120;28", + "children": [ + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Referral", + "position": "20.00;133.04", + "size": "68;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "outline row", + "value": null, + "position": "0.00;155.00", + "size": "140;28", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "10.00;155.00", + "size": "120;28", + "children": [ + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "About", + "position": "19.00;161.00", + "size": "58;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXColumn", + "description": null, + "role_description": "column", + "value": null, + "position": "10.00;52.00", + "size": "120;896", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "v2.0.5 (78)", + "role_description": "button", + "value": null, + "position": "16.00;957.00", + "size": "57;14", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Settings", + "role_description": "button", + "value": null, + "position": "108.00;956.00", + "size": "16;16", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXSplitter", + "description": null, + "role_description": "splitter", + "value": 140.0, + "position": "140.00;52.00", + "size": "1;936", + "children": [] + }, + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "141.00;0.00", + "size": "280;988", + "children": [ + { + "name": null, + "role": "AXScrollArea", + "description": null, + "role_description": "scroll area", + "value": null, + "position": "141.00;52.00", + "size": "280;936", + "children": [ + { + "name": null, + "role": "AXTable", + "description": null, + "role_description": "list", + "value": null, + "position": "141.00;52.00", + "size": "280;936", + "children": [ + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "table row", + "value": null, + "position": "141.00;62.00", + "size": "280;30", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "157.00;62.00", + "size": "248;30", + "children": [ + { + "name": null, + "role": "AXTextField", + "description": null, + "role_description": "search text field", + "value": "", + "position": "150.00;62.00", + "size": "262;30", + "children": [ + { + "name": "", + "role": "AXButton", + "description": "Search", + "role_description": "button", + "value": null, + "position": "152.00;66.00", + "size": "25;22", + "children": [] + } + ] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "table row", + "value": null, + "position": "141.00;112.00", + "size": "280;0", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "141.00;112.00", + "size": "280;0", + "children": [ + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "157.00;112.00", + "size": "248;0", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "table row", + "value": null, + "position": "141.00;112.00", + "size": "280;24", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "157.00;112.00", + "size": "248;24", + "children": [ + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "History", + "position": "159.00;116.94", + "size": "60;14", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "table row", + "value": null, + "position": "141.00;136.00", + "size": "280;24", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "157.00;136.00", + "size": "248;24", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "New Chat, Now", + "role_description": "button", + "value": null, + "position": "157.00;140.00", + "size": "248;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXRow", + "description": null, + "role_description": "table row", + "value": null, + "position": "141.00;160.00", + "size": "280;24", + "children": [ + { + "name": null, + "role": "AXCell", + "description": null, + "role_description": "cell", + "value": null, + "position": "157.00;160.00", + "size": "248;24", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "New Chat, 11:48", + "role_description": "button", + "value": null, + "position": "157.00;164.00", + "size": "248;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXColumn", + "description": null, + "role_description": "column", + "value": null, + "position": "151.00;52.00", + "size": "260;936", + "children": [] + } + ] + } + ] + } + ] + }, + { + "name": null, + "role": "AXSplitter", + "description": null, + "role_description": "splitter", + "value": 280.0, + "position": "421.00;52.00", + "size": "1;936", + "children": [] + }, + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "422.00;0.00", + "size": "1306;988", + "children": [ + { + "name": null, + "role": "AXScrollArea", + "description": null, + "role_description": "scroll area", + "value": null, + "position": "438.00;76.00", + "size": "1274;834", + "children": [ + { + "name": null, + "role": "AXOpaqueProviderGroup", + "description": null, + "role_description": "list", + "value": null, + "position": "-436.00;429.00", + "size": "2148;122", + "children": [ + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Create", + "position": "-335.00;430.50", + "size": "100;28", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Devise a zero-gravity sport, for space travelers.", + "role_description": "button", + "value": null, + "position": "-436.00;477.50", + "size": "300;74", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Explain", + "position": "-29.00;429.00", + "size": "104;32", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Describe rainbows, to a 5-year-old.", + "role_description": "button", + "value": null, + "position": "-128.00;477.50", + "size": "300;74", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Entertain", + "position": "267.50;431.50", + "size": "128;26", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Reimagine a classic fairy tale, set in the modern world.", + "role_description": "button", + "value": null, + "position": "180.00;477.50", + "size": "300;74", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Write", + "position": "593.00;431.25", + "size": "91;26", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Pen a dog's diary, for a day.", + "role_description": "button", + "value": null, + "position": "488.00;477.50", + "size": "300;74", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Help", + "position": "908.00;431.25", + "size": "79;26", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Start conversations, confidently at events.", + "role_description": "button", + "value": null, + "position": "796.00;477.50", + "size": "300;74", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Plan", + "position": "1220.00;431.50", + "size": "70;27", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Organize vegetarian meals, for a busy student.", + "role_description": "button", + "value": null, + "position": "1104.00;477.50", + "size": "300;74", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Guide", + "position": "1518.50;431.50", + "size": "89;26", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": "Set up a budget, for a student.", + "role_description": "button", + "value": null, + "position": "1412.00;477.50", + "size": "300;74", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXScrollBar", + "description": null, + "role_description": "scroll bar", + "value": 1.0, + "position": "438.00;895.00", + "size": "1274;15", + "children": [ + { + "name": null, + "role": "AXValueIndicator", + "description": null, + "role_description": "value indicator", + "value": 1.0, + "position": "949.50;895.00", + "size": "760;15", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "increment arrow button", + "value": null, + "position": "438.00;895.00", + "size": "0;0", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "decrement arrow button", + "value": null, + "position": "438.00;895.00", + "size": "0;0", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "increment page button", + "value": null, + "position": "1710.00;895.00", + "size": "2;15", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "decrement page button", + "value": null, + "position": "438.00;895.00", + "size": "511;15", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXScrollArea", + "description": null, + "role_description": "scroll area", + "value": null, + "position": "422.00;52.00", + "size": "1306;882", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Message", + "position": "450.00;951.00", + "size": "54;16", + "children": [] + }, + { + "name": null, + "role": "AXScrollArea", + "description": null, + "role_description": "scroll area", + "value": null, + "position": "444.00;951.00", + "size": "1226;16", + "children": [ + { + "name": null, + "role": "AXTextArea", + "description": null, + "role_description": "text entry area", + "value": "", + "position": "444.00;951.00", + "size": "1211;16", + "children": [] + }, + { + "name": null, + "role": "AXScrollBar", + "description": null, + "role_description": "scroll bar", + "value": 0.0, + "position": "1655.00;951.00", + "size": "15;16", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "Arrow Up Circle", + "role_description": "button", + "value": null, + "position": "1688.00;947.00", + "size": "24;24", + "children": [] + } + ] + } + ] + } + ] + }, + { + "name": null, + "role": "AXToolbar", + "description": null, + "role_description": "toolbar", + "value": null, + "position": "0.00;0.00", + "size": "1728;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Hide Sidebar", + "role_description": "button", + "value": null, + "position": "87.00;0.00", + "size": "42;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Hide Sidebar", + "role_description": "button", + "value": null, + "position": "96.50;14.00", + "size": "24;18", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "New Chat", + "role_description": "button", + "value": null, + "position": "377.00;0.00", + "size": "36;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "New Chat", + "role_description": "button", + "value": null, + "position": "386.00;14.00", + "size": "18;17", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "430.00;0.00", + "size": "88;52", + "children": [ + { + "name": "GPT-4o", + "role": "AXPopUpButton", + "description": null, + "role_description": "pop up button", + "value": null, + "position": "434.00;12.00", + "size": "80;28", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "Edit Chat", + "role_description": "button", + "value": null, + "position": "1507.00;0.00", + "size": "36;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Edit Chat", + "role_description": "button", + "value": null, + "position": "1516.00;14.00", + "size": "18;17", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "Pin Chat", + "role_description": "button", + "value": null, + "position": "1552.00;0.00", + "size": "37;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Pin Chat", + "role_description": "button", + "value": null, + "position": "1561.00;14.00", + "size": "19;22", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "Clear Chat", + "role_description": "button", + "value": null, + "position": "1598.00;0.00", + "size": "39;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Clear Chat", + "role_description": "button", + "value": null, + "position": "1607.50;14.00", + "size": "20;20", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "Delete Chat", + "role_description": "button", + "value": null, + "position": "1637.00;0.00", + "size": "37;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Delete Chat", + "role_description": "button", + "value": null, + "position": "1646.00;14.00", + "size": "19;22", + "children": [] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": "Share Chat", + "role_description": "button", + "value": null, + "position": "1683.00;0.00", + "size": "37;52", + "children": [ + { + "name": null, + "role": "AXButton", + "description": "Share Chat", + "role_description": "button", + "value": null, + "position": "1692.00;14.00", + "size": "19;23", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "close button", + "value": null, + "position": "19.00;18.00", + "size": "14;16", + "children": [] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "full screen button", + "value": null, + "position": "59.00;18.00", + "size": "14;16", + "children": [ + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "59.00;18.00", + "size": "14;16", + "children": [ + { + "name": null, + "role": "AXGroup", + "description": null, + "role_description": "group", + "value": null, + "position": "59.00;18.00", + "size": "14;16", + "children": [] + } + ] + } + ] + }, + { + "name": null, + "role": "AXButton", + "description": null, + "role_description": "minimize button", + "value": null, + "position": "39.00;18.00", + "size": "14;16", + "children": [] + }, + { + "name": null, + "role": "AXStaticText", + "description": null, + "role_description": "text", + "value": "Chats", + "position": "149.00;0.00", + "size": "228;52", + "children": [] + } + ] +} \ No newline at end of file diff --git a/output_visualisation/visualiser_app.py b/output_visualisation/visualiser_app.py new file mode 100644 index 0000000..eef88a3 --- /dev/null +++ b/output_visualisation/visualiser_app.py @@ -0,0 +1,756 @@ +""" +This is a Streamlit app for visualizing accessibility metadata. + +To run the app, use the following command: +streamlit run visualiser_app.py + +Also, please select dark theme in the settings. +""" + + +import streamlit as st +import json +import os +import numpy as np +import pandas as pd +import plotly.graph_objects as go +from PIL import Image +import plotly.express as px + +class StreamlitAccessibilityVisualizer: + def __init__(self, image_path, custom_json_path, system_json_path, system_scale_factor=2.0): + """ + Initialize the accessibility visualizer with file paths + + Parameters: + - image_path: Path to the app screenshot image + - custom_json_path: Path to the custom accessibility JSON file + - system_json_path: Path to the system accessibility JSON file + - system_scale_factor: Scale factor to apply to system data coordinates (default: 2.0) + """ + self.image_path = image_path + self.custom_json_path = custom_json_path + self.system_json_path = system_json_path + self.custom_data = None + self.system_data = None + self.elements = {'custom': [], 'system': []} + self.fig = None + self.system_scale_factor = system_scale_factor + + # Load data + self.load_data() + self.extract_elements() + + def load_data(self): + """Load the JSON data and image""" + # Load custom JSON + with open(self.custom_json_path, 'r') as f: + self.custom_data = json.load(f) + + # Load system JSON + with open(self.system_json_path, 'r') as f: + self.system_data = json.load(f) + + # Load image + self.image = Image.open(self.image_path) + self.img_width, self.img_height = self.image.size + + def extract_elements_from_custom(self, node, elements, parent=None, depth=0): + """Recursively extract elements with bounding boxes from custom data""" + # Extract information for this node if it has a bounding box + if 'box' in node and len(node['box']) == 4: + x1, y1, x2, y2 = node['box'] + width = x2 - x1 + height = y2 - y1 + + # Only include elements with non-zero width and height + if width > 0 and height > 0: + element = { + 'x0': x1, + 'y0': y1, + 'x1': x2, + 'y1': y2, + 'width': width, + 'height': height, + 'value': node.get('value', None), + 'cls': node.get('cls', None), + 'depth': depth + } + elements.append(element) + + # Process children recursively + if 'children' in node and node['children']: + for child in node['children']: + self.extract_elements_from_custom(child, elements, node, depth + 1) + + def extract_elements_from_system(self, node, elements, parent=None, depth=0): + """Recursively extract elements with position and size from system data""" + # Extract information for this node if it has position and size + if 'position' in node and 'size' in node: + try: + pos = node['position'].split(';') + size = node['size'].split(';') + + if len(pos) == 2 and len(size) == 2: + # Apply scaling factor to adjust system coordinates to match the screenshot scale + x = float(pos[0]) * self.system_scale_factor + y = float(pos[1]) * self.system_scale_factor + width = float(size[0]) * self.system_scale_factor + height = float(size[1]) * self.system_scale_factor + + # Only include elements with non-zero width and height + if width > 0 and height > 0: + element = { + 'x0': x, + 'y0': y, + 'x1': x + width, + 'y1': y + height, + 'width': width, + 'height': height, + 'value': node.get('value', None), + 'name': node.get('name', None), + 'role': node.get('role', None), + 'description': node.get('description', None), + 'depth': depth + } + elements.append(element) + except (ValueError, IndexError): + pass # Skip if conversion fails + + # Process children recursively + if 'children' in node and node['children']: + for child in node['children']: + self.extract_elements_from_system(child, elements, node, depth + 1) + + def extract_elements(self): + """Extract elements from both data sources""" + # Extract from custom data + self.extract_elements_from_custom(self.custom_data, self.elements['custom']) + + # Extract from system data + self.extract_elements_from_system(self.system_data, self.elements['system']) + + print(f"Extracted {len(self.elements['custom'])} elements from custom data") + print(f"Extracted {len(self.elements['system'])} elements from system data") + + def create_figure_config(self): + """Create a configuration object for the Plotly figure""" + return { + 'displayModeBar': True, + 'displaylogo': False, + 'modeBarButtonsToAdd': ['toggleHover'], + 'modeBarButtonsToRemove': ['lasso2d', 'select2d'], + 'toImageButtonOptions': { + 'format': 'png', + 'filename': f'accessibility_visualization', + 'height': 800, + 'width': 1200, + 'scale': 2 + } + } + + def create_plotly_figure(self, data_source, element_types=None, min_size=0, max_depth=None): + """ + Create a plotly figure with the app screenshot as background + + Parameters: + - data_source: 'custom' or 'system' + - element_types: List of element types to include (cls for custom, role for system) + - min_size: Minimum element size (width*height) to include + - max_depth: Maximum depth of elements to include + + Returns: + - fig: Plotly figure + """ + # Create figure + self.fig = go.Figure() + + # Calculate aspect ratio and set figure size + aspect_ratio = self.img_height / self.img_width + display_width = min(1200, self.img_width) # Limit max width for large images + display_height = int(display_width * aspect_ratio) + + # Add the screenshot as a background image + self.fig.add_layout_image( + dict( + source=self.image, + xref="x", + yref="y", + x=0, + y=0, + sizex=self.img_width, + sizey=self.img_height, + sizing="contain", # "contain" to preserve aspect ratio + opacity=1, + layer="below" + ) + ) + + # Set axes properties + self.fig.update_xaxes( + range=[0, self.img_width], + showticklabels=False, + showgrid=False, + zeroline=False, + scaleanchor="y", # Lock the aspect ratio + scaleratio=1 # 1:1 aspect ratio + ) + + self.fig.update_yaxes( + range=[self.img_height, 0], # Inverted y-axis to match image coordinates + showticklabels=False, + showgrid=False, + zeroline=False + ) + + # Update layout with a dark theme + self.fig.update_layout( + title=f"Accessibility Visualization ({data_source.capitalize()} Data)", + title_font=dict(size=20, color="white"), + autosize=False, # Use fixed size instead of autosize + width=display_width, + height=display_height, + margin=dict(l=0, r=0, t=40, b=0), + hovermode="closest", + paper_bgcolor="#111111", + plot_bgcolor="#111111", + font=dict(color="white"), + legend=dict( + title_font=dict(size=14), + font=dict(size=12), + bgcolor="rgba(0,0,0,0.5)", + bordercolor="rgba(255,255,255,0.2)", + borderwidth=1, + itemsizing='constant', # Make legend items all the same size + itemwidth=30, + orientation='v', + yanchor='top', + y=1, + xanchor='right', + x=1.1, + tracegroupgap=5 + ), + modebar=dict( + bgcolor="rgba(0,0,0,0)", + color="white", + activecolor="#636EFA" + ), + dragmode="pan", # Set default interaction mode to pan + ) + + return self.fig + + def add_bounding_boxes(self, data_source, element_types=None, min_size=0, max_depth=None): + """ + Add bounding boxes to the plotly figure + + Parameters: + - data_source: 'custom' or 'system' + - element_types: List of element types to include (cls for custom, role for system) + - min_size: Minimum element size (width*height) to include + - max_depth: Maximum depth of elements to include + """ + # Get elements for the current data source + current_elements = self.elements[data_source] + + # Apply filters + filtered_elements = current_elements + + # Filter by element type + if element_types and len(element_types) > 0: + if data_source == 'custom': + filtered_elements = [e for e in filtered_elements if e.get('cls') in element_types] + else: # system + filtered_elements = [e for e in filtered_elements if e.get('role') in element_types] + + # Filter by minimum size + if min_size > 0: + filtered_elements = [e for e in filtered_elements if e.get('width', 0) * e.get('height', 0) >= min_size] + + # Filter by maximum depth + if max_depth is not None: + filtered_elements = [e for e in filtered_elements if e.get('depth', 0) <= max_depth] + + # Sort elements by depth (deepest first, to have shallower elements on top) + sorted_elements = sorted(filtered_elements, key=lambda e: e.get('depth', 0), reverse=True) + + # Get element types for the current data source + if data_source == 'custom': + all_element_types = sorted(set(e.get('cls', 'Unknown') for e in current_elements)) + else: # system + all_element_types = sorted(set(e.get('role', 'Unknown') for e in current_elements)) + + # Create a colormap + colors = px.colors.qualitative.Plotly # Use Plotly's built-in color scale + color_map = {elem_type: colors[i % len(colors)] for i, elem_type in enumerate(all_element_types)} + + # Group elements by type and add them to the figure + for elem_type in all_element_types: + if element_types and elem_type not in element_types: + continue + + color = color_map[elem_type] + + # Create lists for shape data + x0_list, y0_list, x1_list, y1_list = [], [], [], [] + hover_texts = [] + + # Create separate lists for Group elements (which will not have hover) + group_x0_list, group_y0_list, group_x1_list, group_y1_list = [], [], [], [] + + # Filter elements by type and collect data + for elem in sorted_elements: + if ((data_source == 'custom' and elem.get('cls') == elem_type) or + (data_source == 'system' and elem.get('role') == elem_type)): + + # Skip very large elements that might be the background + width = elem['width'] if 'width' in elem else elem['x1'] - elem['x0'] + height = elem['height'] if 'height' in elem else elem['y1'] - elem['y0'] + if width > self.img_width * 0.95 and height > self.img_height * 0.95: + continue + + # Check if this is a Group element + is_group = False + if (data_source == 'custom' and elem.get('cls') == 'Group') or \ + (data_source == 'system' and elem.get('role') == 'AXGroup'): + is_group = True + group_x0_list.append(elem['x0']) + group_y0_list.append(elem['y0']) + group_x1_list.append(elem['x1']) + group_y1_list.append(elem['y1']) + else: + # Only add non-Group elements to the hoverable list + x0_list.append(elem['x0']) + y0_list.append(elem['y0']) + x1_list.append(elem['x1']) + y1_list.append(elem['y1']) + + # Create simplified hover text with just the value (if available) + # Otherwise use type/role + if data_source == 'custom': + hover_text = elem.get('value', elem.get('cls', '')) + else: # system + hover_text = elem.get('value', elem.get('name', elem.get('role', ''))) + + # Make sure we have a value to show + if not hover_text: + if data_source == 'custom': + hover_text = elem.get('cls', 'Unknown') + else: + hover_text = elem.get('role', 'Unknown') + + hover_texts.append(hover_text) + + # Add shapes if we have any for this type + if x0_list: + # Add hoverable elements (non-Group elements) + self.fig.add_trace( + go.Scatter( + x=[(x0 + x1) / 2 for x0, x1 in zip(x0_list, x1_list)], # Center point for hover + y=[(y0 + y1) / 2 for y0, y1 in zip(y0_list, y1_list)], + mode='markers', + marker=dict( + size=2, + color='rgba(0,0,0,0)' # Invisible markers, just for hover + ), + hoverinfo='text', + hovertemplate="%{hovertext}", + hovertext=hover_texts, + name=elem_type, + showlegend=True, + legendgroup=elem_type, + marker_color=color # Set marker color to match the boxes (for legend) + ) + ) + + # Add rectangle shapes for hoverable elements + for i in range(len(x0_list)): + self.fig.add_shape( + type="rect", + x0=x0_list[i], + y0=y0_list[i], + x1=x1_list[i], + y1=y1_list[i], + line=dict( + color=color, + width=1, # Thinner line + ), + fillcolor="rgba(0,0,0,0)", + opacity=0.9 + ) + + # Add Group elements (non-hoverable) + if group_x0_list and elem_type in ['Group', 'AXGroup']: + # We don't add a trace for these (so they won't be hoverable) + # Just add the shapes + for i in range(len(group_x0_list)): + self.fig.add_shape( + type="rect", + x0=group_x0_list[i], + y0=group_y0_list[i], + x1=group_x1_list[i], + y1=group_y1_list[i], + line=dict( + color=color, + width=1, # Thinner line + ), + fillcolor="rgba(0,0,0,0)", + opacity=0.9 + ) + + def get_element_types(self, data_source): + """Get all element types for a data source""" + if data_source == 'custom': + return sorted(set(e.get('cls', 'Unknown') for e in self.elements['custom'])) + else: # system + return sorted(set(e.get('role', 'Unknown') for e in self.elements['system'])) + + def get_max_depth(self, data_source): + """Get the maximum depth of elements for a data source""" + if data_source == 'custom': + return max((e.get('depth', 0) for e in self.elements['custom']), default=0) + else: # system + return max((e.get('depth', 0) for e in self.elements['system']), default=0) + + +# Streamlit app +def main(): + # Configure the page with a dark theme and expanded layout + st.set_page_config( + layout="wide", + page_title="Accessibility Visualization", + page_icon="🔍", + initial_sidebar_state="collapsed", + menu_items={ + 'Get Help': 'https://github.com/yourusername/accessibility-visualizer', + 'Report a bug': 'https://github.com/yourusername/accessibility-visualizer/issues', + 'About': "Interactive visualization tool for accessibility metadata" + } + ) + + # Custom CSS for dark theme and better spacing + st.markdown(""" + + """, unsafe_allow_html=True) + + # Main app title with icon + st.markdown(""" + # 🔍 Interactive Accessibility Visualization + Visualize and analyze accessibility metadata from custom and system sources. + """) + + # File Selection (in a collapsible section) + with st.expander("File Selection", expanded=False): + # File upload controls in a horizontal layout + upload_col1, upload_col2, upload_col3 = st.columns(3) + + with upload_col1: + # Screenshot image upload + uploaded_image = st.file_uploader("Upload Screenshot Image", type=["png", "jpg", "jpeg"]) + + with upload_col2: + # Custom JSON upload + uploaded_custom_json = st.file_uploader("Upload Custom Accessibility JSON", type=["json"]) + + with upload_col3: + # System JSON upload + uploaded_system_json = st.file_uploader("Upload System Accessibility JSON", type=["json"]) + + + # Always visible filters (not in collapsible section) + # Create a 3-column layout for the always-visible filters + filter_col1, filter_col2, filter_col3 = st.columns(3) + + with filter_col1: + # Data source selection (custom vs system) + data_source = st.radio("Data Source", ["custom", "system"], horizontal=True) + + # Set system_scale_factor to a default value (not displayed in UI) + system_scale_factor = 2.0 + + # Check if files are uploaded or use default files + if uploaded_image is None or uploaded_custom_json is None or uploaded_system_json is None: + # Display help information + # Display instructions and help information + st.markdown("### How to Use This Tool") + st.markdown(""" + This tool visualizes accessibility metadata from both custom and system JSON files overlaid on an app screenshot. + + #### Getting Started: + 1. Click on "File Selection" to expand the upload section + 2. Upload your screenshot image (PNG, JPG) + 3. Upload your custom accessibility JSON file + 4. Upload your system accessibility JSON file + + #### Features: + - **Switch Data Sources**: Toggle between custom and system accessibility data + - **Filter Elements**: Show/hide specific element types + - **Depth Control**: Filter elements by their depth in the hierarchy + - **Interactive Hover**: Mouse over elements to see detailed information + """) + return + else: + # Save uploaded files temporarily + with open("temp_image.png", "wb") as f: + f.write(uploaded_image.getbuffer()) + + with open("temp_custom.json", "wb") as f: + f.write(uploaded_custom_json.getbuffer()) + + with open("temp_system.json", "wb") as f: + f.write(uploaded_system_json.getbuffer()) + + image_path = "temp_image.png" + custom_json_path = "temp_custom.json" + system_json_path = "temp_system.json" + + # Create visualizer instance with the fixed scale factor + visualizer = StreamlitAccessibilityVisualizer( + image_path, + custom_json_path, + system_json_path, + system_scale_factor=system_scale_factor + ) + + # Get element types for the selected data source + element_types = visualizer.get_element_types(data_source) + + # Add the remaining filters (element types and depth) + with filter_col2: + # Element type filter + selected_element_types = st.multiselect( + "Element types to display", + options=element_types, + default=element_types + ) + + # Set min_size to 0 (no minimum size filter) + min_size = 0 + + with filter_col3: + # Depth filter + max_depth = visualizer.get_max_depth(data_source) + depth_filter = st.slider( + "Maximum element depth", + min_value=0, + max_value=max_depth, + value=max_depth, + step=1 + ) + + # Generate visualization + fig = visualizer.create_plotly_figure( + data_source=data_source, + element_types=selected_element_types, + min_size=min_size, + max_depth=depth_filter + ) + + # Add bounding boxes and other elements + visualizer.add_bounding_boxes( + data_source=data_source, + element_types=selected_element_types, + min_size=min_size, + max_depth=depth_filter + ) + + # Create config for the figure + config = visualizer.create_figure_config() + + # Display the visualization in full width + st.plotly_chart(fig, use_container_width=True, config=config) + + # Add space between visualization and statistics + st.markdown("---") + + # Create tabs for statistics and help information + stats_tab, help_tab = st.tabs(["Element Statistics", "Help & Information"]) + + with stats_tab: + # Count elements by type + if data_source == 'custom': + df = pd.DataFrame([ + {"Type": e.get('cls', 'Unknown'), "Depth": e.get('depth', 0)} + for e in visualizer.elements['custom'] + if e.get('width', 0) * e.get('height', 0) >= min_size and e.get('depth', 0) <= depth_filter + ]) + else: # system + df = pd.DataFrame([ + {"Type": e.get('role', 'Unknown'), "Depth": e.get('depth', 0)} + for e in visualizer.elements['system'] + if e.get('width', 0) * e.get('height', 0) >= min_size and e.get('depth', 0) <= depth_filter + ]) + + if not df.empty: + type_counts = df['Type'].value_counts().reset_index() + type_counts.columns = ['Element Type', 'Count'] + + # Create three columns for the statistics + chart_cols = st.columns(3) + + with chart_cols[0]: + # Display counts table without index numbers + st.subheader(f"Element Type Counts \n ({data_source.capitalize()})") + st.dataframe( + type_counts[['Element Type', 'Count']].set_index('Element Type'), + use_container_width=True + ) + + with chart_cols[1]: + # Display pie chart of element types + st.subheader(f"Distribution of Element Types \n ({data_source.capitalize()})") + fig_pie = px.pie( + type_counts, + values='Count', + names='Element Type', + color_discrete_sequence=px.colors.qualitative.Plotly + ) + # Improve pie chart appearance + fig_pie.update_traces(textposition='inside', textinfo='percent+label') + fig_pie.update_layout( + margin=dict(t=30, b=0, l=0, r=0), + showlegend=False, + # legend=dict(orientation="h", yanchor="bottom", y=-0.2, xanchor="center", x=0.5), + paper_bgcolor="#1a1a1a", + plot_bgcolor="#1a1a1a", + font=dict(color="white") + ) + st.plotly_chart(fig_pie, use_container_width=True, config={'displayModeBar': False}) + + with chart_cols[2]: + # Display depth histogram + st.subheader(f"Element Depth Distribution \n ({data_source.capitalize()})") + fig_hist = px.histogram( + df, + x='Depth', + nbins=max_depth+1, + color_discrete_sequence=['#636EFA'] + ) + fig_hist.update_layout( + xaxis_title="Depth", + yaxis_title="Count", + margin=dict(t=30, b=0, l=0, r=0), + paper_bgcolor="#1a1a1a", + plot_bgcolor="#1a1a1a", + font=dict(color="white") + ) + st.plotly_chart(fig_hist, use_container_width=True, config={'displayModeBar': False}) + else: + st.info("No elements match the current filters.") + + with help_tab: + st.markdown(""" + ## How to Use This Tool + + This interactive visualization tool helps you analyze accessibility metadata from both custom and system sources, overlaid on an application screenshot. + + ### Getting Started + + 1. **Upload Files** (in the File Selection section): + - Screenshot image (PNG, JPG) + - Custom accessibility JSON file + - System accessibility JSON file + + 2. **Configure Visualization**: + - Switch between custom and system data sources + - Filter elements by type + - Filter elements by hierarchy depth + + 3. **Interact with Visualization**: + - Hover over elements to see detailed information + - Zoom in/out using the mousewheel + - Pan by clicking and dragging + - Use the toolbar to reset view, download as PNG, etc. + + ### Understanding the Data + + - **Custom Data**: Uses `box` attribute with [x1, y1, x2, y2] coordinates + - **System Data**: Uses `position` (x;y) and `size` (width;height) attributes + - **Element Types**: + - Custom: Group, Text, AXButton, AXImage, etc. + - System: AXWindow, AXGroup, AXButton, etc. + - **Depth**: Indicates the nesting level in the element hierarchy + + ### Element Information on Hover + + When hovering over elements, you'll see different information depending on the data source: + + - **Custom data**: Type, Value, Position, Size, Depth + - **System data**: Role, Name, Value, Description, Position, Size, Depth + """) + + # Clean up temporary files + if uploaded_image is not None: + try: + os.remove("temp_image.png") + os.remove("temp_custom.json") + os.remove("temp_system.json") + except: + pass # Ignore errors when removing temp files + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..7cba7f3 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,218 @@ +atomacos==3.3.0 +attrs==25.3.0 +certifi==2025.1.31 +charset-normalizer==3.4.1 +click==8.1.8 +contourpy==1.3.1 +cycler==0.12.1 +filelock==3.17.0 +fonttools==4.56.0 +fsspec==2025.2.0 +future==1.0.0 +huggingface-hub==0.29.1 +idna==3.10 +ImageHash==4.3.2 +iniconfig==2.1.0 +Jinja2==3.1.5 +kiwisolver==1.4.8 +macapptree==0.0.2 +MarkupSafe==3.0.2 +matplotlib==3.10.1 +mpmath==1.3.0 +networkx==3.4.2 +numpy==1.26.4 +ocrmac==1.0.0 +opencv-python==4.11.0.86 +packaging==24.2 +pandas==2.2.3 +pillow==11.1.0 +pluggy==1.5.0 +plotly==5.9.0 +psutil==7.0.0 +py-cpuinfo==9.0.0 +PyAutoGUI==0.9.41 +PyGetWindow==0.0.4 +PyMsgBox==1.0.9 +pynput==1.8.0 +pyobjc==10.3.1 +pyobjc-core==10.3.1 +pyobjc-framework-Accessibility==10.3.1 +pyobjc-framework-Accounts==10.3.1 +pyobjc-framework-AddressBook==10.3.1 +pyobjc-framework-AdServices==10.3.1 +pyobjc-framework-AdSupport==10.3.1 +pyobjc-framework-AppleScriptKit==10.3.1 +pyobjc-framework-AppleScriptObjC==10.3.1 +pyobjc-framework-ApplicationServices==10.3.1 +pyobjc-framework-AppTrackingTransparency==10.3.1 +pyobjc-framework-AudioVideoBridging==10.3.1 +pyobjc-framework-AuthenticationServices==10.3.1 +pyobjc-framework-AutomaticAssessmentConfiguration==10.3.1 +pyobjc-framework-Automator==10.3.1 +pyobjc-framework-AVFoundation==10.3.1 +pyobjc-framework-AVKit==10.3.1 +pyobjc-framework-AVRouting==10.3.1 +pyobjc-framework-BackgroundAssets==10.3.1 +pyobjc-framework-BrowserEngineKit==10.3.1 +pyobjc-framework-BusinessChat==10.3.1 +pyobjc-framework-CalendarStore==10.3.1 +pyobjc-framework-CallKit==10.3.1 +pyobjc-framework-CFNetwork==10.3.1 +pyobjc-framework-Cinematic==10.3.1 +pyobjc-framework-ClassKit==10.3.1 +pyobjc-framework-CloudKit==10.3.1 +pyobjc-framework-Cocoa==10.3.1 +pyobjc-framework-Collaboration==10.3.1 +pyobjc-framework-ColorSync==10.3.1 +pyobjc-framework-Contacts==10.3.1 +pyobjc-framework-ContactsUI==10.3.1 +pyobjc-framework-CoreAudio==10.3.1 +pyobjc-framework-CoreAudioKit==10.3.1 +pyobjc-framework-CoreBluetooth==10.3.1 +pyobjc-framework-CoreData==10.3.1 +pyobjc-framework-CoreHaptics==10.3.1 +pyobjc-framework-CoreLocation==10.3.1 +pyobjc-framework-CoreMedia==10.3.1 +pyobjc-framework-CoreMediaIO==10.3.1 +pyobjc-framework-CoreMIDI==10.3.1 +pyobjc-framework-CoreML==10.3.1 +pyobjc-framework-CoreMotion==10.3.1 +pyobjc-framework-CoreServices==10.3.1 +pyobjc-framework-CoreSpotlight==10.3.1 +pyobjc-framework-CoreText==10.3.1 +pyobjc-framework-CoreWLAN==10.3.1 +pyobjc-framework-CryptoTokenKit==10.3.1 +pyobjc-framework-DataDetection==10.3.1 +pyobjc-framework-DeviceCheck==10.3.1 +pyobjc-framework-DictionaryServices==10.3.1 +pyobjc-framework-DiscRecording==10.3.1 +pyobjc-framework-DiscRecordingUI==10.3.1 +pyobjc-framework-DiskArbitration==10.3.1 +pyobjc-framework-DVDPlayback==10.3.1 +pyobjc-framework-EventKit==10.3.1 +pyobjc-framework-ExceptionHandling==10.3.1 +pyobjc-framework-ExecutionPolicy==10.3.1 +pyobjc-framework-ExtensionKit==10.3.1 +pyobjc-framework-ExternalAccessory==10.3.1 +pyobjc-framework-FileProvider==10.3.1 +pyobjc-framework-FileProviderUI==10.3.1 +pyobjc-framework-FinderSync==10.3.1 +pyobjc-framework-FSEvents==10.3.1 +pyobjc-framework-GameCenter==10.3.1 +pyobjc-framework-GameController==10.3.1 +pyobjc-framework-GameKit==10.3.1 +pyobjc-framework-GameplayKit==10.3.1 +pyobjc-framework-HealthKit==10.3.1 +pyobjc-framework-ImageCaptureCore==10.3.1 +pyobjc-framework-InputMethodKit==10.3.1 +pyobjc-framework-InstallerPlugins==10.3.1 +pyobjc-framework-InstantMessage==10.3.1 +pyobjc-framework-Intents==10.3.1 +pyobjc-framework-IntentsUI==10.3.1 +pyobjc-framework-IOBluetooth==10.3.1 +pyobjc-framework-IOBluetoothUI==10.3.1 +pyobjc-framework-IOSurface==10.3.1 +pyobjc-framework-iTunesLibrary==10.3.1 +pyobjc-framework-KernelManagement==10.3.1 +pyobjc-framework-LatentSemanticMapping==10.3.1 +pyobjc-framework-LaunchServices==10.3.1 +pyobjc-framework-libdispatch==10.3.1 +pyobjc-framework-libxpc==10.3.1 +pyobjc-framework-LinkPresentation==10.3.1 +pyobjc-framework-LocalAuthentication==10.3.1 +pyobjc-framework-LocalAuthenticationEmbeddedUI==10.3.1 +pyobjc-framework-MailKit==10.3.1 +pyobjc-framework-MapKit==10.3.1 +pyobjc-framework-MediaAccessibility==10.3.1 +pyobjc-framework-MediaLibrary==10.3.1 +pyobjc-framework-MediaPlayer==10.3.1 +pyobjc-framework-MediaToolbox==10.3.1 +pyobjc-framework-Metal==10.3.1 +pyobjc-framework-MetalFX==10.3.1 +pyobjc-framework-MetalKit==10.3.1 +pyobjc-framework-MetalPerformanceShaders==10.3.1 +pyobjc-framework-MetalPerformanceShadersGraph==10.3.1 +pyobjc-framework-MetricKit==10.3.1 +pyobjc-framework-MLCompute==10.3.1 +pyobjc-framework-ModelIO==10.3.1 +pyobjc-framework-MultipeerConnectivity==10.3.1 +pyobjc-framework-NaturalLanguage==10.3.1 +pyobjc-framework-NetFS==10.3.1 +pyobjc-framework-Network==10.3.1 +pyobjc-framework-NetworkExtension==10.3.1 +pyobjc-framework-NotificationCenter==10.3.1 +pyobjc-framework-OpenDirectory==10.3.1 +pyobjc-framework-OSAKit==10.3.1 +pyobjc-framework-OSLog==10.3.1 +pyobjc-framework-PassKit==10.3.1 +pyobjc-framework-PencilKit==10.3.1 +pyobjc-framework-PHASE==10.3.1 +pyobjc-framework-Photos==10.3.1 +pyobjc-framework-PhotosUI==10.3.1 +pyobjc-framework-PreferencePanes==10.3.1 +pyobjc-framework-PushKit==10.3.1 +pyobjc-framework-Quartz==10.3.1 +pyobjc-framework-QuickLookThumbnailing==10.3.1 +pyobjc-framework-ReplayKit==10.3.1 +pyobjc-framework-SafariServices==10.3.1 +pyobjc-framework-SafetyKit==10.3.1 +pyobjc-framework-SceneKit==10.3.1 +pyobjc-framework-ScreenCaptureKit==10.3.1 +pyobjc-framework-ScreenSaver==10.3.1 +pyobjc-framework-ScreenTime==10.3.1 +pyobjc-framework-ScriptingBridge==10.3.1 +pyobjc-framework-SearchKit==10.3.1 +pyobjc-framework-Security==10.3.1 +pyobjc-framework-SecurityFoundation==10.3.1 +pyobjc-framework-SecurityInterface==10.3.1 +pyobjc-framework-SensitiveContentAnalysis==10.3.1 +pyobjc-framework-ServiceManagement==10.3.1 +pyobjc-framework-SharedWithYou==10.3.1 +pyobjc-framework-SharedWithYouCore==10.3.1 +pyobjc-framework-ShazamKit==10.3.1 +pyobjc-framework-Social==10.3.1 +pyobjc-framework-SoundAnalysis==10.3.1 +pyobjc-framework-Speech==10.3.1 +pyobjc-framework-SpriteKit==10.3.1 +pyobjc-framework-StoreKit==10.3.1 +pyobjc-framework-Symbols==10.3.1 +pyobjc-framework-SyncServices==10.3.1 +pyobjc-framework-SystemConfiguration==10.3.1 +pyobjc-framework-SystemExtensions==10.3.1 +pyobjc-framework-ThreadNetwork==10.3.1 +pyobjc-framework-UniformTypeIdentifiers==10.3.1 +pyobjc-framework-UserNotifications==10.3.1 +pyobjc-framework-UserNotificationsUI==10.3.1 +pyobjc-framework-VideoSubscriberAccount==10.3.1 +pyobjc-framework-VideoToolbox==10.3.1 +pyobjc-framework-Virtualization==10.3.1 +pyobjc-framework-Vision==10.3.1 +pyobjc-framework-WebKit==10.3.1 +pyparsing==3.2.1 +PyRect==0.2.0 +PyScreeze==0.1.19 +pytest==7.4.4 +python-dateutil==2.9.0.post0 +pytweening==1.2.0 +pytz==2025.1 +PyWavelets==1.8.0 +PyYAML==6.0.2 +regex==2024.11.6 +requests==2.32.3 +safetensors==0.5.3 +scipy==1.15.2 +seaborn==0.13.2 +six==1.17.0 +streamlit==1.44.1 +sympy==1.13.1 +tokenizers==0.20.3 +torch==2.6.0 +torchvision==0.21.0 +tqdm==4.67.1 +transformers==4.45.2 +typing_extensions==4.12.2 +tzdata==2025.1 +ultralytics==8.3.82 +ultralytics-thop==2.0.14 +Unidecode==1.3.8 +urllib3==2.3.0 diff --git a/screen_reader/screen_reader.py b/screen_reader/screen_reader.py new file mode 100644 index 0000000..0222ad5 --- /dev/null +++ b/screen_reader/screen_reader.py @@ -0,0 +1,384 @@ +import os +import time +import argparse +import subprocess +import tkinter as tk +from typing import Tuple, Dict + +from AppKit import NSApp +from PIL import Image, ImageChops +from pynput.mouse import Button, Controller +from macapptree import get_tree, get_app_bundle + +from hierarchy_dl.hierarchy import generate_hierarchy + +from screen_reader.utils import * +from screen_reader.screenshot import ( + get_app_info, + open_app_in_foreground, + screenshot_app, + get_app_name, +) + + +# Global mouse controller +mouse = Controller() + +# Global variables and constants +BUNDLE_ID = None +SAY_RATE = 190 +WELCOME = True +HELP = True +SYSTEM_ACCESSIBILITY = False +VOICE = "Daniel" +SKIP_GROUPS_SIZE = 5 + +WELCOME_MESSAGE = "Welcome to the ScreenReader." +HELP_MESSAGE = ( + "Use the arrow keys to navigate the UI elements. [[slnc 500]]\n" + "- Press the down arrow to navigate inside a group.\n" + "- Press the up arrow to navigate to the parent group.\n" + "- Press the left and right arrows to navigate to the previous and next elements.\n" + "- Press the space bar to click on a button or link.\n" + "- Press h to hear the instructions again.\n" + "- Press q to quit the ScreenReader." +) +SCREEN = None +SAY_PROCESS: subprocess.Popen = subprocess.Popen(["say", ""]) +ACTIVE_ELEMENT: UIElement = {} + + +def make_click_through(window: tk.Tk) -> None: + """ + Enables click-through for the given Tkinter window. + """ + ns_window = NSApp().windows()[0] + ns_window.setIgnoresMouseEvents_(True) + + +def wake_up(root: tk.Tk) -> None: + """ + Brings the Tkinter window to the foreground. + """ + root.deiconify() + root.lift() + root.focus_force() + NSApp.activateIgnoringOtherApps_(True) + root.update() + + +def create_app(position: Tuple[int, int, int, int]) -> Tuple[tk.Tk, tk.Canvas]: + """ + Creates and returns a transparent Tkinter window along with its drawing canvas. + + Args: + position (Tuple[int, int, int, int]): (x, y, width, height). + + Returns: + Tuple[tk.Tk, tk.Canvas]: The Tkinter root window and its associated canvas. + """ + root = tk.Tk() + root.title("ScreenReader") + root.overrideredirect(True) + root.wm_attributes("-transparent", True) + root.config(bg="systemTransparent") + + x, y, width, height = map(int, position) + root.geometry(f"{width}x{height}+{x}+{y}") + + canvas = tk.Canvas(root, width=width, height=height, bg="systemTransparent") + canvas.pack() + + # Allow the window to be click-through after a short delay. + root.after(100, lambda: make_click_through(root)) + return root, canvas + + +def get_message(node: UIElement) -> str: + """ + Generates a message based on the properties of a node in the accessibility tree. + + Args: + node (UIElement): A node from the accessibility data. + + Returns: + str: The generated message. + """ + message = "" + place = "" + + node_value = node.get("value", "") + node_cls = node.get("cls", "") + + if node_cls.startswith("AX"): + node_cls = node_cls[2:] + + if node_cls.endswith("Group"): + children = node.get("children", []) + message = f"{node_cls} with {len(children)} items. {'Groups content: ' if node_value else ''} {node_value}. {place}. [[slnc 500]] You are currently in a group. To interact with the items, press the down arrow." + elif node_cls.endswith("Button"): + message = f"{node_cls} with description {node_value}. You are currently on a button. To click, press the space bar." + elif node_cls.endswith("Image"): + message = f"{node_cls} with description {node_value}." + elif node_cls.endswith("Link"): + message = f"{node_cls} {node_value}. You are currently on a link. To click, press the space bar." + elif node_cls.endswith("Text"): + message = f"{node_cls} detected: {node_value}. " + else: + children = node.get("children", []) + message = f"{node_cls}, {node_value}. {f'[[slnc 500]] {node_cls} with {len(children)} items.' if children else ''}" + + parent = node.get("parent") + if parent and len(parent.get("children", [])) > 1: + object_type = "group" if node["cls"].endswith("Group") else "element" + place = f"{ordinal(node['index'])} {object_type} out of {len(node['parent']['children'])}" + place = f"[[slnc 500]] {place}" + + if message and not node_cls.endswith("Group"): + message += f" {place}." + + return message + + +def read_message(message: str, wait: bool = False) -> None: + """ + Uses the macOS 'say' command to read a message aloud. + + Args: + message (str): The message to be read. + wait (bool): If True, waits for the current speech to finish before proceeding. + """ + global SAY_PROCESS + + if not message: + return + + if wait: + SAY_PROCESS.wait() + else: + SAY_PROCESS.terminate() + + print(message) + SAY_PROCESS = subprocess.Popen(["say", "-r", str(SAY_RATE), "-v", VOICE, message]) + + +def on_press(key: str, canvas: tk.Canvas, root: tk.Tk) -> Dict: + """ + Handles key press events for navigating the accessibility tree and triggering actions. + + Args: + key (str): The key that was pressed. + canvas (tk.Canvas): The canvas used for drawing the UI. + root (tk.Tk): The Tkinter root window. + + Returns: + Dict: The updated active node. + """ + global ACTIVE_ELEMENT + active = ACTIVE_ELEMENT + old_active = active + key = key.lower() + + if key == "down" and active.get("children"): + active = active["children"][0] + elif key == "up" and active.get("parent"): + active = active["parent"] + elif key == "left" and active.get("prev"): + active = active["prev"] + elif key == "right" and active.get("next"): + active = active["next"] + elif key == "space": + # Calculate click coordinates + x = root.winfo_x() + (active["box"][0] + active["box"][2]) // 2 + y = root.winfo_y() + (active["box"][1] + active["box"][3]) // 2 + + open_app_in_foreground(BUNDLE_ID) + mouse.position = (x, y) + time.sleep(0.5) + mouse.click(Button.left) + time.sleep(0.5) + + active = get_accessibility_data(BUNDLE_ID) + wake_up(root) + elif key == "q": + root.destroy() + os.system("killall say") + exit(0) + elif key == "h": + read_message(HELP_MESSAGE) + + if old_active != active: + os.system("clear") + print(active["cls"], active["value"], active["box"]) + + canvas.delete("all") + color = "red" if active["cls"] == "Group" else "green" + canvas.create_rectangle(*active["box"], outline=color) + + message = get_message(active) + read_message(message) + + return active + + +def find_element(element: UIElement, data: UIElement) -> UIElement: + """ + Find the element in the data tree based on bounding box overlap. + + Args: + element (UIElement): The element to find. + data (UIElement): The data tree. + + Returns: + UIElement: The found element. + """ + stack = [data] + while stack: + node = stack.pop() + + if iou(element["box"], node["box"]) > 0.8: + return node + + if "children" in node: + stack.extend(node["children"]) + + return data + + +def map_system_accessibility_to_ui_element(accessibility: dict) -> UIElement: + stack = [accessibility] + + while stack: + element = stack.pop(0) + + if element["children"]: + stack.extend( element["children"] ) + + element['cls'] = element.pop("role") + + element['box'] = element.pop("visible_bbox") if "visible_bbox" in element else element.pop("bbox") + element['box'] = tuple(map(lambda x: 2 * int(x), element['box'])) if element['box'] else (0, 0, 0, 0) + + element['value'] = f"{element['role_description'] or ''} {element['description'] or ''} {element['value'] or ''}" + element['value'] = element['value'].strip() + + to_delete = set(element.keys()).difference({"cls", "box", "value", "children"}) + for d in to_delete: + del element[d] + + return accessibility + +def get_accessibility_data(bundle_id: str) -> UIElement: + """ + Generates accessibility data by taking a screenshot of the target app and + processing it. + + Args: + bundle_id (str): The bundle ID of the target application. + + Returns: + Dict: The updated accessibility data. + """ + global SCREEN, ACTIVE_ELEMENT + + open_app_in_foreground(bundle_id) + screen_path = screenshot_app(bundle_id, "./screenshots/")[0] + image = Image.open(screen_path) + + if SCREEN: + # Check if the screen has changed + diff = ImageChops.difference(SCREEN.convert("RGB"), image.convert("RGB")) + if not diff.getbbox(): + return ACTIVE_ELEMENT + + SCREEN = image + + # Generate hierarchy + if SYSTEM_ACCESSIBILITY: + data = get_tree(bundle_id) + data = map_system_accessibility_to_ui_element(data) + else: + data = generate_hierarchy(SCREEN).to_dict() + + # add prev, next and parent links + active = update_accessibility_data(data, n=SKIP_GROUPS_SIZE) + + # Try to find the previously active element on the new screen + if ACTIVE_ELEMENT: + active = find_element(ACTIVE_ELEMENT, active) + + return active + + +def parse_arguments(): + """ + Parses command-line arguments. + """ + global BUNDLE_ID, WELCOME, HELP, SAY_RATE, SYSTEM_ACCESSIBILITY, VOICE, SKIP_GROUPS_SIZE + + parser = argparse.ArgumentParser() + parser.add_argument("-b", "--bundle_id", type=str, help="The bundle ID of the target application") + parser.add_argument("-n", "--name", type=str, help="Name of the target application (alternative to bundle_id)") + parser.add_argument("-dw", "--deactivate_welcome", action="store_true", help="Deactivate welcome message. Do not read \"Welcome to the ScreenReader.\" at startup.") + parser.add_argument("-dh", "--deactivate_help", action="store_true", help="Deactivate help message. Do not read the help message at startup.") + parser.add_argument("-r", "--rate", type=int, default=SAY_RATE, help=f"The speech rate for the 'say' command. Default: {SAY_RATE}") + parser.add_argument("-v", "--voice", help="The voice to use for the 'say' command. See 'say -v \"?\" | grep en' for a list of available voices.", default=VOICE) + parser.add_argument("-sa", "--system_accessibility", action="store_true", help="Use system accessibility data instead of generating it") + parser.add_argument("-sk", "--skip-groups", type=int, default=SKIP_GROUPS_SIZE, help=f"Skip groups with less than n children. Default: {SKIP_GROUPS_SIZE}") + args = parser.parse_args() + + if args.name and not args.bundle_id: + BUNDLE_ID = get_app_bundle(args.name) + else: + BUNDLE_ID = args.bundle_id + + WELCOME = not args.deactivate_welcome + HELP = not args.deactivate_help + SAY_RATE = args.rate + SYSTEM_ACCESSIBILITY = args.system_accessibility + VOICE = args.voice + SKIP_GROUPS_SIZE = args.skip_groups + + +def main(): + """ + Main entry point for the ScreenReader application. + """ + parse_arguments() + + global ACTIVE_ELEMENT, SAY_PROCESS, WELCOME, HELP, WELCOME_MESSAGE, HELP_MESSAGE, BUNDLE_ID + + if WELCOME: + read_message(WELCOME_MESSAGE) + + if HELP: + read_message(HELP_MESSAGE, wait=True) + + ACTIVE_ELEMENT = get_accessibility_data(BUNDLE_ID) + + app_info = get_app_info(BUNDLE_ID) + app_name = get_app_name(BUNDLE_ID) + children_count = len(ACTIVE_ELEMENT.get("children", [])) + ACTIVE_ELEMENT["value"] = ( + f"{app_name}, global window, {app_info[1].replace('_', ' ')}. " + f"Press down to view {children_count} items." + ) + + root, canvas = create_app(app_info[2]) + canvas.create_rectangle(*ACTIVE_ELEMENT["box"], outline="red") + + def on_press_listener(key: str) -> None: + global ACTIVE_ELEMENT + print(key) + ACTIVE_ELEMENT = on_press(key, canvas, root) + + root.bind("", lambda event: on_press_listener(event.keysym)) + wake_up(root) + + message = get_message(ACTIVE_ELEMENT) + read_message(message, wait=True) + root.mainloop() + + +if __name__ == "__main__": + main() diff --git a/screen_reader/screenshot.py b/screen_reader/screenshot.py new file mode 100644 index 0000000..e79c96f --- /dev/null +++ b/screen_reader/screenshot.py @@ -0,0 +1,154 @@ +import os +import time +import Quartz +import AppKit +import subprocess +from typing import Iterable, List, Dict, AnyStr, Union, Iterator, Tuple + +class ScreencaptureEx(Exception): + pass + +WindowInfo = Dict[AnyStr, Union[AnyStr, int]] + +USER_OPTS_STR = "exclude_desktop on_screen_only" +FILE_EXT = "png" +COMMAND = 'screencapture {options} -l {window} -o "{filename}"' +SUCCESS = 0 +STATUS_BAR_WINDOW_IDENTIFIER = "Item-0" + + +def get_window_info() -> List[WindowInfo]: + return Quartz.CGWindowListCopyWindowInfo( + Quartz.kCGWindowListOptionAll + | Quartz.kCGWindowListExcludeDesktopElements + | Quartz.kCGWindowListOptionOnScreenOnly, + Quartz.kCGNullWindowID, + ) + + +def gen_ids_from_info( + windows: Iterable[WindowInfo], +) -> List[Tuple[int, str, str]]: # Changed return type to List + result = [] # Initialize a list to store results + for win_dict in windows: + owner = win_dict.get("kCGWindowOwnerName", "") + num = win_dict.get("kCGWindowNumber", "") + name = win_dict.get("kCGWindowName", "") + bounds = win_dict.get('kCGWindowBounds', "") + + x = bounds['X'] + y = bounds['Y'] + width = bounds['Width'] + height = bounds['Height'] + + result.append((num, owner, name, (x, y, width, height))) + return result + + +def gen_window_ids( + parent: str, +) -> List[Tuple[int, str]]: # Changed return type to List[Tuple[int, str]] + windows = get_window_info() + parent = parent.lower() + result = [] # Initialize a list to store results + + for num, owner, window_name, (x, y, width, height) in gen_ids_from_info(windows): + if parent == owner.lower(): + if window_name == STATUS_BAR_WINDOW_IDENTIFIER: + print(f"Skipping status bar window: {num}") + else: + window_name = window_name.replace(" ", "_") + result.append((num, window_name, (x, y, width, height))) + + return result # Return the list of window IDs + + +def take_screenshot(window: int, filename: str, output_folder: str) -> str: + filename = os.path.join(output_folder, filename) + + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + command = COMMAND.format(window=window, filename=filename, options="") + rc, output = subprocess.getstatusoutput(command) + if rc != SUCCESS: + raise ScreencaptureEx(f"Error: screencapture output: {output}") + + return filename + + +def get_filename(window_name, extension) -> str: + return f"{window_name}:{time.time():.2f}.{extension}" + + +def gen_windows(application_name: str) -> Iterator[int]: + windows = list(gen_window_ids(application_name)) # Convert generator to list + if not windows: # Check if the list is empty + print(f"Window with parent {application_name} not found.") + return windows # Return the list of windows + + +def screenshot_windows( + app_name: str, + output_folder: str, + extension: str = "", +) -> Iterator[str]: + windows = gen_windows(app_name) + + for window_identifier, window_name, _ in windows: + yield take_screenshot( + window_identifier, get_filename(window_name, extension), output_folder + ) + + +def screenshot_application_windows(name: str, output_folder: str, extension: str) -> List[str]: + filenames = [] + for filename in screenshot_windows(name, output_folder, extension): + filenames.append(filename) + return filenames + + +def running_app(app_bundle): + workspace = AppKit.NSWorkspace.sharedWorkspace() + for app in workspace.runningApplications(): + if app.bundleIdentifier() == app_bundle: + return app + return None + + +def screenshot_app(app_bundle: str, output_folder: str) -> List[str]: + assert app_bundle is not None, "Application bundle is not specified" + assert output_folder is not None, "Output folder is not specified" + + if not os.path.exists(output_folder): + os.makedirs(output_folder) + + app = running_app(app_bundle) + return screenshot_application_windows(app.localizedName(), output_folder, FILE_EXT) + + +# def get_window_position(processIdentifier): +# window_list = Quartz.CGWindowListCopyWindowInfo( +# Quartz.kCGWindowListOptionOnScreenOnly, +# Quartz.kCGNullWindowID +# ) + +# for window in window_list: +# if window['kCGWindowOwnerPID'] == processIdentifier: +# return window['kCGWindowBounds']['X'], window['kCGWindowBounds']['Y'] + +# return None + + +def open_app_in_foreground(app_bundle: str, wait_time: float = 2): + os.system(f"open -b {app_bundle}") + time.sleep(wait_time) + + +def get_app_name(bundle_id: str): + app = running_app(bundle_id) + return app.localizedName() + +def get_app_info(bundle_id: str): + app = get_app_name(bundle_id) + return gen_windows(app)[-1] diff --git a/screen_reader/utils.py b/screen_reader/utils.py new file mode 100644 index 0000000..6f73605 --- /dev/null +++ b/screen_reader/utils.py @@ -0,0 +1,207 @@ +from typing import List, TypedDict, Dict, Optional + +class UIElement(TypedDict): + box: List[int] + cls: str + value: Optional[str] + parent: "UIElement" + children: List["UIElement"] + prev: "UIElement" + next: "UIElement" + index: int + +def ordinal(n: int) -> str: + """ + Returns the ordinal representation of an integer (e.g., 1 -> '1st'). + """ + if 11 <= (n % 100) <= 13: + suffix = "th" + else: + suffix = ["th", "st", "nd", "rd", "th"][min(n % 10, 4)] + + return f"{n}{suffix}" + + +def iou(box1: List[int], box2: List[int]) -> float: + """ + Calculate the Intersection over Union (IoU) of two bounding boxes. + + Args: + box1 (List[int]): The first bounding box. + box2 (List[int]): The second bounding box. + + Returns: + float: The IoU value. + """ + x1, y1, x2, y2 = box1 + x1_other, y1_other, x2_other, y2_other = box2 + + inter_x1 = max(x1, x1_other) + inter_y1 = max(y1, y1_other) + inter_x2 = min(x2, x2_other) + inter_y2 = min(y2, y2_other) + + inter_width = max(0, inter_x2 - inter_x1) + inter_height = max(0, inter_y2 - inter_y1) + intersection_area = inter_width * inter_height + + area1 = (x2 - x1) * (y2 - y1) + area2 = (x2_other - x1_other) * (y2_other - y1_other) + union_area = area1 + area2 - intersection_area + + if union_area == 0: + return 0 + + return intersection_area / union_area + + +def remove_small_groups(node: Dict, n: int): + if "children" not in node: + return + + # First process children recursively. + for child in node["children"]: + remove_small_groups(child, n) + + # Now, scan through the children list and promote any group that has fewer than n children. + i = 0 + while i < len(node["children"]): # skip root node + child = node["children"][i] + + if "children" in child and len(child["children"]) < n and child["cls"].lower().endswith("group"): + # Promote the child’s children into the parent's children list. + promoted = child["children"] + # Update each promoted child's parent pointer. + for p in promoted: + p["parent"] = node + + child["parent"] = None + child["children"] = [] + child["to_remove"] = True + + # Replace the small group with its children. + node["children"] = node["children"][:i] + promoted + node["children"][i+1:] + # After inserting the promoted children, recheck the new entries. + else: + i += 1 + + def sort_heuristic(c: Dict) -> float: + """ + Heuristic function to sort children based on their bounding box coordinates. + """ + x, y = c["box"][0], c["box"][1] + + if c["parent"] is None: + return y ** 2 + 0.5 * x ** 2 + + # If the child has a parent, we want to subtract the parent's coordinates + parent_x, parent_y = c["parent"]["box"][0], c["parent"]["box"][1] + return (y - parent_y) + 0.3 * (x - parent_x) + + # re-sort the children list + node["children"].sort(key=sort_heuristic) + + # Remove any children marked for removal. + node["children"] = [c for c in node["children"] if "to_remove" not in c] + + # Update sibling links for the (possibly updated) children list. + for j, c in enumerate(node["children"]): + c["index"] = j + 1 + c["prev"] = node["children"][j - 1] if j > 0 else node["children"][-1] + c["next"] = node["children"][j + 1] if j < len(node["children"]) - 1 else node["children"][0] + + +def add_description_to_groups(node: Dict, max_len: int = 100) -> None: + """ + Adds a description to group nodes based on their children. + + Args: + node (Dict): The node to process. + """ + if "children" not in node: + return + + for child in node["children"]: + add_description_to_groups(child) + + if node["cls"].lower().endswith("group"): + descriptions = [] + + for child in node["children"]: + if child["value"] is not None: + descriptions.append(child["value"]) + + value = descriptions[0] if descriptions else "" + for item in descriptions[1:]: + new_value = f"{value}, {item}" if value else item + + if len(new_value) > max_len: + new_value = new_value[:max_len] + ", and other..." + break + + value = new_value + + + if len(value) > max_len: + value = value[:max_len] + ", and other..." + + node["value"] = value + + +def update_accessibility_data(data: Dict, n: int = 0) -> UIElement: + """ + Updates the accessibility data by adding parent, next, and previous links to nodes. + Also, remove groups that have less than n children. + + Args: + data (Dict): The original accessibility data. + n (int): The minimum number of children a group must have to be retained. + Defaults to 0. + + Returns: + Dict: The updated accessibility data. + """ + stack = [data] + data["parent"] = None + data["next"] = None + data["prev"] = None + data["index"] = 1 + + leaves = [] + + while stack: + node = stack.pop() + + # Scale down the bounding box coordinates. + node["box"] = [coord // 2 for coord in node["box"]] + + if "children" not in node: + continue + + if len(node["children"]) == 0: + leaves.append(node) + continue + + node["children"].sort(key=lambda x: (x["box"][1] ** 2 + 0.5 * x["box"][0] ** 2)) + + for i, child in enumerate(node["children"]): + child["parent"] = node + child["index"] = i + 1 + child["prev"] = node["children"][i - 1] if i > 0 else node["children"][-1] + child["next"] = ( + node["children"][i + 1] + if i < len(node["children"]) - 1 + else node["children"][0] + ) + + if child["value"] and "|" in child["value"]: + child["value"] = child["value"].split("|")[0].strip() + + stack.append(child) + + # Remove groups with less than n children + remove_small_groups(data, n) + + add_description_to_groups(data, max_len=30) + + return data \ No newline at end of file From fcbae59d28a47e267a4387853e888b6bcc283a12 Mon Sep 17 00:00:00 2001 From: Victor Muryn Date: Thu, 10 Jul 2025 17:35:37 +0300 Subject: [PATCH 2/3] updated requirements --- requirements.txt | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/requirements.txt b/requirements.txt index 7cba7f3..0b60907 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ +altair==5.5.0 atomacos==3.3.0 attrs==25.3.0 +blinker==1.9.0 +cachetools==5.5.2 certifi==2025.1.31 charset-normalizer==3.4.1 click==8.1.8 @@ -9,16 +12,21 @@ filelock==3.17.0 fonttools==4.56.0 fsspec==2025.2.0 future==1.0.0 +gitdb==4.0.12 +GitPython==3.1.44 huggingface-hub==0.29.1 idna==3.10 ImageHash==4.3.2 iniconfig==2.1.0 Jinja2==3.1.5 +jsonschema==4.24.0 +jsonschema-specifications==2025.4.1 kiwisolver==1.4.8 macapptree==0.0.2 MarkupSafe==3.0.2 matplotlib==3.10.1 mpmath==1.3.0 +narwhals==1.46.0 networkx==3.4.2 numpy==1.26.4 ocrmac==1.0.0 @@ -26,11 +34,14 @@ opencv-python==4.11.0.86 packaging==24.2 pandas==2.2.3 pillow==11.1.0 -pluggy==1.5.0 plotly==5.9.0 +pluggy==1.5.0 +protobuf==5.29.5 psutil==7.0.0 py-cpuinfo==9.0.0 +pyarrow==20.0.0 PyAutoGUI==0.9.41 +pydeck==0.9.1 PyGetWindow==0.0.4 PyMsgBox==1.0.9 pynput==1.8.0 @@ -197,19 +208,25 @@ pytweening==1.2.0 pytz==2025.1 PyWavelets==1.8.0 PyYAML==6.0.2 +referencing==0.36.2 regex==2024.11.6 requests==2.32.3 +rpds-py==0.26.0 safetensors==0.5.3 scipy==1.15.2 seaborn==0.13.2 six==1.17.0 +smmap==5.0.2 streamlit==1.44.1 sympy==1.13.1 -tokenizers==0.20.3 +tenacity==9.1.2 +tokenizers==0.21.2 +toml==0.10.2 torch==2.6.0 torchvision==0.21.0 +tornado==6.5.1 tqdm==4.67.1 -transformers==4.45.2 +transformers==4.48.0 typing_extensions==4.12.2 tzdata==2025.1 ultralytics==8.3.82 From 8c5346ece756d9488de7b99c5c91b83cae52c1f4 Mon Sep 17 00:00:00 2001 From: Victor Muryn Date: Thu, 10 Jul 2025 17:45:08 +0300 Subject: [PATCH 3/3] updated requirements --- hierarchy_dl/requirements.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/hierarchy_dl/requirements.txt b/hierarchy_dl/requirements.txt index 2ff8411..5fc7fbe 100644 --- a/hierarchy_dl/requirements.txt +++ b/hierarchy_dl/requirements.txt @@ -17,7 +17,7 @@ fonttools==4.56.0 frozenlist==1.5.0 fsspec==2024.12.0 gmpy2==2.2.1 -huggingface_hub==0.24.6 +huggingface-hub==0.24.6 idna==3.7 ImageHash==4.3.2 importlib-metadata==7.0.1 @@ -37,7 +37,6 @@ opentelemetry-api==1.26.0 packaging==24.2 pandas==2.2.3 pillow==11.1.0 -pip==25.0 propcache==0.2.0 psutil==7.0.0 py-cpuinfo==9.0.0 @@ -62,11 +61,11 @@ seaborn==0.13.2 setuptools==75.8.0 six==1.16.0 sympy==1.13.1 -tokenizers==0.20.1 +tokenizers==0.21.2 torch==2.6.0 torchvision==0.21.0 tqdm==4.67.1 -transformers==4.45.2 +transformers==4.48.0 typing_extensions==4.12.2 tzdata==2023.3 ultralytics==8.3.82