Langchain, RAG, GPT-Vision: Analýza obrázků v pdf

GPT pdf analýza obrázky

V tomto návodu vám ukážu, jak pomocí Langchain, RAG a GPT Vision získat obrázky i text z pdf dokumentu a následně tato data analyzovat.

Tento kód budeme spouštět pomocí platformy Google Colab.

Návod

Nejprve si nainstalujeme potřebné knihovny.

! pip install pdf2image
! pip install pytesseract
! apt install poppler-utils
! apt install tesseract-ocr
! pip install kaleido cohere tiktoken python-multipart
! pip install -U langchain openai chromadb langchain-experimental # (newest versions required for multi-modal)
! pip install "unstructured[all-docs]==0.10.19" pillow pydantic lxml pillow matplotlib tiktoken open_clip_torch torch

Nastavíme cestu, kde bude uložen pdf dokument a následně i všechny soubory.

import os

path = "/content/data/"
file_name = os.listdir(path)

V následujícím kódu uděláme:

  • Nainstalujeme knihovny.
  • Importujeme data.
  • Extrahujeme obrázky/tabulky a rozdělíme text do chunks.
  • Importujeme knihovny, vytvoříme chroma databázi, přidáme obrázky a dokumenty.
  • Vytvoříme RAG.
  • Importujeme knihovny a následně dekódujeme, změníme vel. obrázku a enkódujeme.
  • Přidáme kontext a obrázky, pokud jsou přítomny, následně i systémovou výzvu pro analýzu.
  • Nastavíme API klíč a položíme dotaz.

Nahrajeme pdf dokument do prostředí Google Colab. Jako příklad jsem použil jednu studii ohledně řeckých bronzových uměleckých soch.

Kód

# Extract images, tables, and chunk text
from unstructured.partition.pdf import partition_pdf

raw_pdf_elements = partition_pdf(
    filename=path + file_name[0],
    extract_images_in_pdf=True,
    infer_table_structure=True,
    chunking_strategy="by_title",
    max_characters=4000,
    new_after_n_chars=3800,
    combine_text_under_n_chars=2000,
    image_output_dir_path=path
)  # Closing parenthesis was missing here

tables = []
texts = []
for element in raw_pdf_elements:
    if "unstructured.documents.elements.Table" in str(type(element)):
        tables.append(str(element))
    elif "unstructured.documents.elements.CompositeElement" in str(type(element)):
        texts.append(str(element))

print(len(tables))
print(len(texts))
import os
import uuid

import chromadb
import numpy as np
from langchain.vectorstores import Chroma
from langchain_experimental.open_clip import OpenCLIPEmbeddings
from PIL import Image as _PILImage

# Create chroma
vectorstore = Chroma(
    collection_name="mm_rag_clip_photos", embedding_function=OpenCLIPEmbeddings()
)

# Get image URIs with .jpg extension only
image_uris = sorted(
    [
        os.path.join(path, image_name)
        for image_name in os.listdir(path)
        if image_name.endswith(".jpg")
    ]
)

# Add images
vectorstore.add_images(uris=image_uris)

# Add documents
vectorstore.add_texts(texts=texts)

# Make retriever
retriever = vectorstore.as_retriever()
import base64
import io
from io import BytesIO

import numpy as np
from PIL import Image


def resize_base64_image(base64_string, size=(128, 128)):
    """
    Resize an image encoded as a Base64 string.

    Args:
    base64_string (str): Base64 string of the original image.
    size (tuple): Desired size of the image as (width, height).

    Returns:
    str: Base64 string of the resized image.
    """
    # Decode the Base64 string
    img_data = base64.b64decode(base64_string)
    img = Image.open(io.BytesIO(img_data))

    # Resize the image
    resized_img = img.resize(size, Image.LANCZOS)

    # Save the resized image to a bytes buffer
    buffered = io.BytesIO()
    resized_img.save(buffered, format=img.format)

    # Encode the resized image to Base64
    return base64.b64encode(buffered.getvalue()).decode("utf-8")


def is_base64(s):
    """Check if a string is Base64 encoded"""
    try:
        return base64.b64encode(base64.b64decode(s)) == s.encode()
    except Exception:
        return False


def split_image_text_types(docs):
    """Split numpy array images and texts"""
    images = []
    text = []
    for doc in docs:
        doc = doc.page_content  # Extract Document contents
        if is_base64(doc):
            # Resize image to avoid OAI server error
            images.append(
                resize_base64_image(doc, size=(250, 250))
            )  # base64 encoded str
        else:
            text.append(doc)
    return {"images": images, "texts": text}
from operator import itemgetter
from langchain.chat_models import ChatOpenAI
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda, RunnablePassthrough, RunnableParallel

def prompt_func(data_dict):
    # Joining the context texts into a single string
    formatted_texts = "\n".join(data_dict["context"]["texts"])
    messages = []

    # Adding image(s) to the messages if present
    if data_dict["context"]["images"]:
        image_message = {
            "type": "image_url",
            "image_url": {
                "url": f"data:image/jpeg;base64,{data_dict['context']['images'][0]}"
            },
        }
        messages.append(image_message)

    # Adding the text message for analysis
    text_message = {
        "type": "text",
        "text": (
             "Jsi odborný kritik umění a historik. Máš za úkol analyzovat a interpretovat obrazy, "
             "vzhledem k jejich historickému a kulturnímu významu. Vedle obrázků bude "
             "poskytnuto se souvisejícím textem i kontext nabídky. Oba budou načteny z vektorové databáze "
             "na základě klíčových slov zadaných uživatelem. Využij prosím své rozsáhlé znalosti a analytické schopnosti k poskytnutí "
             "komplexního shrnutí, které obsahuje:\n"
             "- Podrobný popis vizuálních prvků na obrázku.\n"
             "- Historický a kulturní kontext obrázku.\n"
             "- Interpretace symboliky a významu obrázku.\n"
             "- Spojení mezi obrázkem a souvisejícím textem.\n\n"
             f"Klíčová slova poskytnutá uživatelem: {data_dict['question']}\n\n"
             "Text a/nebo tabulky:\n"
            f"{formatted_texts}"
        ),
    }
    messages.append(text_message)

    return [HumanMessage(content=messages)]
from google.colab import userdata


openai_api_key = userdata.get('OPENAI_API_KEY')

model = ChatOpenAI(temperature=0,
                   openai_api_key=openai_api_key,
                   model="gpt-4-vision-preview",
                   max_tokens=1024)

# RAG pipeline
chain = (
    {
        "context": retriever | RunnableLambda(split_image_text_types),
        "question": RunnablePassthrough(),
    }
    | RunnableParallel({"response":prompt_func| model| StrOutputParser(),
                      "context": itemgetter("context"),})
)
response = chain.invoke("bronzová hlava")
#
print(response['response'])
print(response['context'])

Odpověď GPT:

Na obrázku je zobrazena bronzová hlava, která má všechny znaky klasického uměleckého díla. Hlava je vyobrazena v profilu a zdá se, že je součástí kruhového medailonu. Je zde patrný detailní modelace vlasů a obličejových rysů, stejně jako zdobení v podobě věnce nebo diadému na hlavě. Tato hlava také nese atributy, které by mohly naznačovat, že se jedná o zobrazení bohyně, možná Athény, vzhledem k přítomnosti co se zdá být sova a olivových větví, které jsou tradičně spojeny s touto bohyní moudrosti a válečné strategie v řecké mytologii.

Historický a kulturní kontext obrázku je zřejmě helénistické období, což je doba po Alexandru Velikém, kdy řecká kultura a vliv pronikaly do široké oblasti a mísily se s místními kulturami. Helénistické umění je známé svou dramatickou expresivitou a detailním zpracováním, což je vidět i na tomto medailonu.

Interpretace symboliky a významu obrázku může odkazovat na ideály moudrosti, ochrany a intelektuální síly, které Athéna představuje. Bronz jako materiál může symbolizovat trvalost a sílu, zatímco detailní zpracování povrchu může odkazovat na helénistické umělecké dovednosti a estetické hodnoty.

Spojení mezi obrázkem a souvisejícím textem je zřejmé v odkazu na helénistické umění a jeho schopnost vyjádřit hluboké a komplexní myšlenky prostřednictvím „výmluvných povrchů“. Text naznačuje, že i když jsou helénistické bronzové sochy vzdálené a neosobní, stále nám umožňují nahlédnout do jiného způsobu bytí a pojetí etického života. Tento medailon, který byl pravděpodobně nalezen v paláci v Thessaloniki a byl součástí vozu, je příkladem toho, jak helénistické umění dokázalo ztělesnit a předat tyto koncepty.

Můžeme i zobrazit referovaný obrázek z dokumentu.

from IPython.display import HTML, display


def plt_img_base64(img_base64):
    # Create an HTML img tag with the base64 string as the source
    image_html = f'<img src="data:image/jpeg;base64,{img_base64}" />'

    # Display the image by rendering the HTML
    display(HTML(image_html))

plt_img_base64(response['context']['images'][0])

Konverzace

Následně můžeme pokládat další otázky, které nás zajímají.

response = chain.invoke("Portrait of a Boy, analýza sochy a kontextu")
print(response['response'])
print(response['context'])

Odpověď:

Vizuální prvky na obrázku: Obrázek zobrazuje sochu chlapce, který je zobrazen v životní velikosti. Socha je vyrobena z mramoru a je velmi detailně zpracovaná. Chlapec má na sobě římský oděv a jeho vlasy jsou pečlivě upravené. Jeho výraz je zaměřený a zamyšlený, což naznačuje, že je to portrét někoho důležitého nebo významného.

Historický a kulturní kontext obrázku: Socha pochází z hellénistického období, které trvalo od konce 4. století př. n. l. do 1. století n. l. Toto období bylo charakterizováno rozšířením řecké kultury po celém Středomoří a Blízkém východě. Hellénistické sochařství bylo známé svou realistickou a detailní práci, což je vidět i na této soše. Socha může být portrétem nějakého významného chlapce, možná syna nějakého významného politika nebo vojevůdce.

Interpretace symboliky a významu obrázku: Socha může symbolizovat mládí, nevinnost a potenciál. Detailní zpracování a realistický výraz naznačují, že chlapec byl důležitou osobou v řecké společnosti. Jeho oděv a účes mohou naznačovat jeho společenský status nebo rodinné pozadí.

Spojení mezi obrázkem a souvisejícím textem: Text „HELLENISTIC SCULPTURE ART IN AMERICA“ naznačuje, že socha je součástí sbírky hellénistického sochařství v Americe. Text „ART IN AMERICA XX3“ a „ART IN AMERICA XX5“ může být odkazem na konkrétní výstavu nebo katalog, ve kterém je socha zahrnuta. Celkově text poskytuje kontext pro sochu a její význam v rámci amerického uměleckého světa. {‚images‘: [], ‚texts‘: [‚HELLENISTIC SCULPTURE ART IN AMERICA XX9‘, ‚ART IN AMERICA XX3‘, ‚ART IN AMERICA\n\nXX3‘, ‚HELLENISTIC SCULPTURE\n\nART IN AMERICA\n\nXX5‘]}

Kód funguje dobře na interpretaci umění, nebo jiných technických dokumentů s vizuálními prvky. Samozřejmě je tomu potřeba přizpůsobit i systémovou výzvu. Bohužel funkcionalita nedokáže zobrazit všechny typy obrázků. Když jsem např. zkoušel nějaké analytické vizualizace a grafy, tak je nebyla schopna rozeznat jako obrázky. Vždy si však můžete vyzkoušet, které obrázky se zobrazí a které ne pomocí funkce Python. U delších dokumentů trvá extrahování obrázků a tvorba vektor databáze poměrně dlouho.

Obsah článku

Líbí se Vám obsah?

Odebírejte Newsletter, ať vám nic neunikne.

Odebírejte novinky ze světa AI

Chcete-li se přihlásit k odběru tohoto blogu a dostávat upozornění na nové příspěvky e-mailem, zadejte svou e-mailovou adresu.

Sledujte mě na sítích.

Odběr novinek AI

Odebírejte novinky ze světa AI

Chcete-li se přihlásit k odběru tohoto blogu a dostávat upozornění na nové příspěvky e-mailem, zadejte svou e-mailovou adresu.

Napsat komentář

Vaše e-mailová adresa nebude zveřejněna. Vyžadované informace jsou označeny *

Podobné články

školení chatGPT

Praktické Školení chatGPT

Nabízím praktické školení chatGPT pro firmy i jednotlivce. Díky tomuto kurzu se dozvíte, jak chat GPT používat při každodenní práci i na pomoc v osobním

Číst více »