import asyncio import rich import weave from pdf2image.pdf2image import convert_from_path from PIL import Image from medrag_multi_modal.document_loader.load_text import TextLoader class ImageLoader(TextLoader): """ ImageLoader is a class that extends the TextLoader class to handle the extraction and loading of images from a PDF file. This class provides functionality to convert specific pages of a PDF document into images and optionally publish these images to a Weave dataset. !!! example "Example Usage" ```python import asyncio import weave from dotenv import load_dotenv from medrag_multi_modal.document_loader import ImageLoader load_dotenv() weave.init(project_name="ml-colabs/medrag-multi-modal") url = "https://archive.org/download/GraysAnatomy41E2015PDF/Grays%20Anatomy-41%20E%20%282015%29%20%5BPDF%5D.pdf" loader = ImageLoader( url=url, document_name="Gray's Anatomy", document_file_path="grays_anatomy.pdf", ) asyncio.run( loader.load_data( start_page=31, end_page=33, weave_dataset_name="grays-anatomy-text", ) ) ``` Args: url (str): The URL of the PDF document. document_name (str): The name of the document. document_file_path (str): The path to the PDF file. """ def __init__(self, url: str, document_name: str, document_file_path: str): super().__init__(url, document_name, document_file_path) def extract_data_from_pdf_file( self, pdf_file: str, page_number: int ) -> Image.Image: image = convert_from_path( pdf_file, first_page=page_number + 1, last_page=page_number + 1 )[0] return image async def load_data(self, start_page: int, end_page: int, weave_dataset_name: str): """ Asynchronously loads images from a PDF file specified by a URL or local file path, processes the images for the specified range of pages, and optionally publishes them to a Weave dataset. This function reads the specified range of pages from a PDF document, converts each page to an image using the `pdf2image` library, and returns a list of dictionaries containing the image and metadata for each processed page. It processes pages concurrently using `asyncio` for efficiency. If a weave_dataset_name is provided, the processed pages are published to a Weave dataset. Args: start_page (int): The starting page index (0-based) to process. end_page (int): The ending page index (0-based) to process. weave_dataset_name (str): The name of the Weave dataset to publish the pages to, if provided. Returns: list[dict]: A list of dictionaries, each containing the image and metadata for a processed page. Raises: ValueError: If the specified start_page or end_page is out of bounds of the document's page count. """ start_page, end_page = self.get_page_indices(start_page, end_page) pages = [] processed_pages_counter: int = 1 total_pages = end_page - start_page async def process_page(page_idx): nonlocal processed_pages_counter pages.append( { "image": convert_from_path( self.document_file_path, first_page=page_idx + 1, last_page=page_idx + 1, )[0], "page_idx": page_idx, "document_name": self.document_name, "file_path": self.document_file_path, "file_url": self.url, } ) rich.print(f"Processed pages {processed_pages_counter}/{total_pages}") processed_pages_counter += 1 tasks = [process_page(page_idx) for page_idx in range(start_page, end_page)] for task in asyncio.as_completed(tasks): await task if weave_dataset_name: weave.publish(weave.Dataset(name=weave_dataset_name, rows=pages)) return pages