Any method that can find all the package required for the given python program?
import gradio as gr
from PIL import Image, ImageDraw, ImageFont
import pymupdf
import pytesseract
from paddleocr import PaddleOCR
from functools import lru_cache
from datetime import datetime
import numpy as np


logs_msg = ""
loaded_doc_len = 0

@lru_cache(maxsize=None)
def load_paddle_ocr_engine(ocr_version='PP-OCRv5'):
    """
    params:
        lang
            'chinese_cht' for Traditional Chinese (poorer accuracy) | 'ch' for Simplified Chinese (better accuracy)
        ocr_version
            'PP-OCRv5'
    """
    print("Loading PaddleOCR... Current datetime utc:", datetime.utcnow())
    ocr_engine = PaddleOCR(

        ocr_version=ocr_version,
    )
    print("Done... Current datetime utc:", datetime.utcnow())

    return ocr_engine

def load_pdf_as_images(file):
    """
    Load PDF file into a list of PIL images (one per page)
    """
    doc = pymupdf.open(file, filetype="pdf")
    pages = []
    for page in doc:
        pix = page.get_pixmap()
        img = Image.frombytes("RGB", [pix.width, pix.height], pix.samples)
        pages.append(img)
    return pages

def image_ocr_tesseract(image):
    """
    Run Tesseract OCR on a PIL image and extract bounding boxes plus texts.
    Returns list of dicts with keys: 'bbox' and 'text'
    """
    image = image.convert("RGB")
    data = pytesseract.image_to_data(image, output_type=pytesseract.Output.DICT)
    ocr_results = []
    n_boxes = len(data['level'])
    for i in range(n_boxes):
        text = data['text'][i].strip()
        if text == "":
            continue
        x, y, w, h = data['left'][i], data['top'][i], data['width'][i], data['height'][i]
        ocr_results.append({"bbox": [x, y, x+w, y+h], "text": text})
    return ocr_results

def paddle_ocr(image: Image.Image):
    """
    Runs PaddleOCR on a PIL image, returns list of dict {'bbox': [x0,y0,x1,y1], 'text': str}
    """
    global logs_msg
    model = load_paddle_ocr_engine()
    _img = image.convert("RGB")
    data = np.array(_img)
    result = model.predict(data) #Convert Image to ndarray for paddle predict
    ocr_results = []
    
    for res in result:
        res.print()
        for i in range(0, len(res["rec_polys"])):
            #print(f"Line Results: {res["rec_polys"][i]} -> {res["rec_texts"][i]}")
            _text = res["rec_texts"][i]
            _polygonData = res["rec_polys"][i]
            # Convert 4 points bbox to standard [x0, y0, x1, y1] rectangle for your renderer
            xmin = min(pt[0] for pt in _polygonData)
            xmax = max(pt[0] for pt in _polygonData)
            ymin = min(pt[1] for pt in _polygonData)
            ymax = max(pt[1] for pt in _polygonData)
            bbox = [int(xmin), int(ymin), int(xmax), int(ymax)]
            ocr_results.append({"bbox": bbox, "text": _text})
            
    return ocr_results

def render_ocr_boxes(image, ocr_results):
    global logs_msg
    draw = ImageDraw.Draw(image)
    try:
        font = ImageFont.truetype("arial.ttf", 12)
    except IOError:
        font = ImageFont.load_default()
    for res in ocr_results:
        bbox = res["bbox"]
        text = res["text"]
        draw.rectangle(bbox, outline="red", width=2)
        text_pos = (bbox[0], bbox[1] - 18 if bbox[1] > 18 else bbox[1] + 2)
        draw.text(text_pos, text, fill="red", font=font)
        logs_msg += f"{bbox} -> {text}\n"
    return image

def load_documents(uploaded_files):
    """
    Load uploaded files (images and PDFs) into list of PIL images
    """
    images = []
    for f in uploaded_files:
        if f.name.lower().endswith(".pdf"):
            images.extend(load_pdf_as_images(f))
        else:
            img = Image.open(f).convert("RGB")
            images.append(img)
    return images

def start_ocr(model, uploaded_files):
    global logs_msg, loaded_doc_len
    if uploaded_files is None or len(uploaded_files) == 0:
        logs_msg += "No files uploaded.\n"
        return None, None, logs_msg, None, 0
    
    logs_msg += "Loading documents...\n"
    images = load_documents(uploaded_files)
    logs_msg += f"{len(images)} page(s) loaded.\n"
    loaded_doc_len = len(images)
    
    ocr_results_list = []
    processed_images = []
    
    for i, img in enumerate(images):
        logs_msg += f"Processing page {i+1}...\n"
        if model == "Tesseract":
            ocr_res = image_ocr_tesseract(img)
        else:
            ocr_res = paddle_ocr(img)
        logs_msg += f"Found {len(ocr_res)} text boxes.\n"
        ocr_results_list.append(ocr_res)
        processed_img = render_ocr_boxes(img.copy(), ocr_res)
        processed_images.append(processed_img)
        
    logs_msg += "OCR processing completed.\n"
    
    return processed_images, ocr_results_list, logs_msg, images, 0  # processed_images, ocr_results, logs, original_images, page_idx

# Pagination Helpers
def on_original_prev(page_index, images):
    if images is None or len(images) == 0:
        return None, "0 / 0", 0
    new_index = max(0, page_index -1)
    return gr.update(interactive=(new_index > 0)), gr.update(interactive = (new_index < len(images) - 1)), images[new_index], gr.update(value=f"{new_index +1} / {len(images)}"), new_index

def on_original_next(page_index, images):
    if images is None or len(images) == 0:
        return None, "0 / 0", 0
    new_index = min(len(images) -1, page_index +1)
    return gr.update(interactive=(new_index > 0)), gr.update(interactive = (new_index < len(images) - 1)),images[new_index], gr.update(value=f"{new_index +1} / {len(images)}"), new_index

def on_processed_prev(page_index, processed_images):
    if processed_images is None or len(processed_images) == 0:
        return None, "0 / 0", 0
    new_index = max(0, page_index -1)
    return gr.update(interactive=(new_index > 0)), gr.update(interactive = (new_index < len(processed_images) - 1)),processed_images[new_index], gr.update(value=f"{new_index +1} / {len(processed_images)}"), new_index

def on_processed_next(page_index, processed_images):
    if processed_images is None or len(processed_images) == 0:
        return None, "0 / 0", 0
    new_index = min(len(processed_images) -1, page_index +1)
    return gr.update(interactive=(new_index > 0)), gr.update(interactive = (new_index < len(processed_images) - 1)),processed_images[new_index], gr.update(value=f"{new_index +1} / {len(processed_images)}"), new_index

# Enable Run OCR button only if files uploaded
def update_run_ocr_btn(files):
    if files is None or len(files) == 0:
        return gr.update(interactive=False)
    return gr.update(interactive=True)

# Reset everything
def on_reset():
    return (
        None, None, "", None, None, None,
        0, 0,
        gr.update(visible=False), gr.update(visible=False),
        gr.update(visible=False), gr.update(visible=False),
        gr.update(visible=False), gr.update(visible=False),
        gr.update(interactive=False),  # Run OCR button
        gr.update(interactive=False),   # Reset button
        gr.File(file_types=['.png', '.jpg', '.jpeg', '.bmp', '.pdf'], file_count="multiple", label="Upload Document (Image or PDF)", value=None),
    )

# Main OCR trigger
def on_run_ocr(model, uploaded_files):
    global loaded_doc_len
    processed_images, ocr_results_list, logs_str, original_images, page_index = start_ocr(model, uploaded_files)
    if original_images is None:
        # Clean state if none
        return on_reset()

    n_pages = loaded_doc_len
    show_pagination = n_pages > 1
    page_text = f"1 / {n_pages}"

    return (
        processed_images[0],         # processed image to display
        original_images[0],          # original image to display (for first page)
        logs_str,
        processed_images,
        ocr_results_list,
        original_images,
        0, 0,                       # reset indexes
        gr.update(value=page_text, visible=show_pagination), gr.update(value=page_text, visible=show_pagination),       # pagination texts for both
        gr.update(visible=show_pagination, interactive=False), gr.update(visible=show_pagination),
        gr.update(visible=show_pagination, interactive=False), gr.update(visible=show_pagination),
        gr.update(interactive=True), # Run OCR button enabled
        gr.update(interactive=True)  # Reset button enabled
    )


with gr.Blocks() as demo:
    gr.Markdown("## OCR Demo")

    with gr.Row():
        model_selector = gr.Dropdown(choices=["Tesseract", "Paddle"], label="Select OCR Model", value="Tesseract")
        upload = gr.File(file_types=['.png', '.jpg', '.jpeg', '.bmp', '.pdf'], file_count="multiple", label="Upload Document (Image or PDF)")
        with gr.Column():
            btn_ocr = gr.Button("Run OCR", interactive=False)
            reset_btn = gr.Button("Reset", interactive=False)

    with gr.Row():
        with gr.Column():
            gr.Markdown("**Original Document**")
            original_img = gr.Image(label="Original Document", interactive=False)
            with gr.Row():
                original_prev_btn = gr.Button("Previous Page", visible=False)
                original_next_btn = gr.Button("Next Page", visible=False)
            original_page_num = gr.Textbox(label="Page", interactive=False, max_lines=1, value="0 / 0", visible=False)
        with gr.Column():
            gr.Markdown("**OCR Processed Document**")
            processed_img = gr.Image(label="OCR Processed Document", interactive=False)
            with gr.Row():
                processed_prev_btn = gr.Button("Previous Page", visible=False)
                processed_next_btn = gr.Button("Next Page", visible=False)
            processed_page_num = gr.Textbox(label="Page", interactive=False, max_lines=1, value="0 / 0", visible=False)

    logs = gr.Textbox(label="Process Logs", interactive=False, lines=10)

    # States
    state_ocr_results = gr.State()
    state_images = gr.State()
    state_processed_images = gr.State()
    state_original_page_index = gr.State(value=0)
    state_processed_page_index = gr.State(value=0)

    # Events
    upload.change(update_run_ocr_btn, inputs=upload, outputs=btn_ocr)

    btn_ocr.click(
        on_run_ocr,
        inputs=[model_selector, upload],
        outputs=[
            processed_img, original_img, logs,
            state_processed_images, state_ocr_results, state_images,
            state_processed_page_index, state_original_page_index,
            processed_page_num, original_page_num,
            processed_prev_btn, processed_next_btn,
            original_prev_btn, original_next_btn,
            btn_ocr, reset_btn
        ]
    )

    processed_prev_btn.click(
        on_processed_prev,
        inputs=[state_processed_page_index, state_processed_images],
        outputs=[processed_prev_btn, processed_next_btn, processed_img, processed_page_num, state_processed_page_index]
    )
    processed_next_btn.click(
        on_processed_next,
        inputs=[state_processed_page_index, state_processed_images],
        outputs=[processed_prev_btn, processed_next_btn, processed_img, processed_page_num, state_processed_page_index]
    )

    original_prev_btn.click(
        on_original_prev,
        inputs=[state_original_page_index, state_images],
        outputs=[original_prev_btn, original_next_btn, original_img, original_page_num, state_original_page_index]
    )
    original_next_btn.click(
        on_original_next,
        inputs=[state_original_page_index, state_images],
        outputs=[original_prev_btn, original_next_btn, original_img, original_page_num, state_original_page_index]
    )

    reset_btn.click(
        on_reset,
        inputs=[],
        outputs=[
            processed_img, original_img, logs,
            state_processed_images, state_ocr_results, state_images,
            state_processed_page_index, state_original_page_index,
            processed_page_num, original_page_num,
            processed_prev_btn, processed_next_btn,
            original_prev_btn, original_next_btn,
            btn_ocr, reset_btn,
            upload,
        ]
    )
    
pytesseract.pytesseract.tesseract_cmd = r'C:\Program Files\Tesseract-OCR\tesseract.exe'
demo.launch()