File size: 8,837 Bytes
62da328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ========= Copyright 2023-2024 @ CAMEL-AI.org. All Rights Reserved. =========
import base64
import logging
import json
from PIL import Image
from typing import List, Literal, Tuple
from urllib.parse import urlparse

from camel.agents import ChatAgent
from camel.configs import ChatGPTConfig
from camel.toolkits.base import BaseToolkit
from camel.toolkits import FunctionTool, CodeExecutionToolkit
from camel.types import ModelType, ModelPlatformType
from camel.models import ModelFactory, OpenAIModel
from camel.messages import BaseMessage

logger = logging.getLogger(__name__)


class ImageAnalysisToolkit(BaseToolkit):
    r"""A class representing a toolkit for image comprehension operations.

    This class provides methods for understanding images, such as identifying
    objects, text in images.
    """
    def __init__(self, model: Literal['gpt-4o', 'gpt-4o-mini'] = 'gpt-4o'):
        self.model_type = ModelType.GPT_4O
        if model == 'gpt-4o':
            self.model_type = ModelType.GPT_4O
        elif model == 'gpt-4o-mini':
            self.model_type = ModelType.GPT_4O_MINI
        else:
            raise ValueError(f"Invalid model type: {model}")

    def _construct_image_url(self, image_path: str) -> str:
        parsed_url = urlparse(image_path)
        is_url = all([parsed_url.scheme, parsed_url.netloc])

        image_url = image_path

        if not is_url:
            image_url = (
                f"data:image/jpeg;base64,{self._encode_image(image_path)}"
            )
        return image_url

            
    def _encode_image(self, image_path: str):
        r"""Encode an image by its image path.

        Arg:
            image_path (str): The path to the image file."""
        with open(image_path, "rb") as image_file:
            return base64.b64encode(image_file.read()).decode("utf-8")

    
    def _judge_if_write_code(self, question: str, image_path: str) -> Tuple[bool, str]:

        _image_url = self._construct_image_url(image_path)
        
        prompt = f"""
        Given the question <question>{question}</question>, do you think it is suitable to write python code (using libraries like cv2) to process the image to get the answer?
        Your output should be in json format (```json ```) including the following fields:
        - `image_caption`: str, A detailed caption about the image. If it is suitable for writing code, it should contains helpful instructions and necessary informations for how to writing code.
        - `if_write_code`: bool, True if it is suitable to write code to process the image, False otherwise.
        """

        messages = [
            {
                "role": "system",
                "content": "You are a helpful assistant for image relevant tasks, and can judge whether \
                the given image is suitable for writing code to process or not. "
            },
            {
                "role": "user",
                "content": [
                    {'type': 'text', 'text': prompt},
                    {
                        'type': 'image_url',
                        'image_url': {
                            'url': _image_url,
                        },
                    },
                ],
            },
        ]

        LLM = OpenAIModel(model_type=self.model_type)
        resp = LLM.run(messages) 

        result_str = resp.choices[0].message.content.lower()
        result_str = result_str.replace("```json", "").replace("```", "").strip()

        result_dict = json.loads(result_str)

        if_write_code = result_dict.get("if_write_code", False)
        image_caption = result_dict.get("image_caption", "")

        return if_write_code, image_caption
    

    def _get_image_caption(self, image_path: str) -> str:

        _image_url = self._construct_image_url(image_path)
        
        prompt = f"""
        Please make a detailed description about the image.
        """

        messages = [
            {
                "role": "user",
                "content": [
                    {'type': 'text', 'text': prompt},
                    {
                        'type': 'image_url',
                        'image_url': {
                            'url': _image_url,
                        },
                    },
                ],
            },
        ]

        LLM = OpenAIModel(model_type=self.model_type)
        resp = LLM.run(messages) 

        return resp.choices[0].message.content


    def ask_question_about_image(self, image_path: str, question: str) -> str:
        r"""Ask a question about the image based on the image path.

        Args:
            image_path (str): The path to the image file.
            question (str): The question to ask about the image.

        Returns:
            str: The answer to the question based on the image.
        """
        logger.debug(
            f"Calling ask_question_about_image with question: `{question}` and \
            image_path: `{image_path}`"
        )
        parsed_url = urlparse(image_path)
        is_url = all([parsed_url.scheme, parsed_url.netloc])

        if not (
            image_path.endswith(".jpg") or \
            image_path.endswith(".jpeg") or \
            image_path.endswith(".png")
        ):
            logger.warning(
                f"The image path `{image_path}` is not a valid image path. "
                f"Please provide a valid image path."
            )
            return f"The image path `{image_path}` is not a valid image path."

        # _image_url = image_path

        # if not is_url:
        #     _image_url = (
        #         f"data:image/jpeg;base64,{self._encode_image(image_path)}"
        #     )

        model = ModelFactory.create(
            model_platform=ModelPlatformType.OPENAI,
            model_type=self.model_type,
        )

        code_model = ModelFactory.create(
            model_platform=ModelPlatformType.OPENAI,
            model_type=ModelType.O3_MINI,
        )

        code_execution_toolkit = CodeExecutionToolkit(require_confirm=False, sandbox="subprocess", verbose=True)

        image_agent = ChatAgent(
            "You are a helpful assistant for image relevant tasks. Given a question related to the image, you can carefully check the image in detail and answer the question.",
            model,
        )

        code_agent = ChatAgent(
            "You are an expert of writing code to process special images leveraging libraries like cv2.",
            code_model,
            tools=code_execution_toolkit.get_tools(),
        )

        if not is_url:
            image_object = Image.open(image_path)
        else:
            import requests
            from io import BytesIO
            url_image = requests.get(image_path)
            image_object = Image.open(BytesIO(url_image.content))


        # if_write_code, image_caption = self._judge_if_write_code(question, image_path)

        # if if_write_code:
        #     prompt = f"""
        #     Please write and execute python code (for example, using cv2 library) to process the image and complete the task: {question}
        #     Here are the image path you need to process: {image_path}
        #     Here are the caption about the image: <image_caption>{image_caption}</image_caption>
        #     """   
        #     message = BaseMessage.make_user_message(
        #         role_name='user',
        #         content=prompt,
        #     )
        #     resp = code_agent.step(message)
        #     return resp.msgs[0].content
        

        # else:
        prompt = question
        message = BaseMessage.make_user_message(
            role_name='user',
            content=prompt,
            image_list=[image_object]
        )

        resp = image_agent.step(message)
        return resp.msgs[0].content


    def get_tools(self) -> List[FunctionTool]:
        r"""Returns a list of FunctionTool objects representing the functions
        in the toolkit.

        Returns:
            List[FunctionTool]: A list of FunctionTool objects representing the
                functions in the toolkit.
        """
        return [
            FunctionTool(self.ask_question_about_image),
        ]