I am trying to get the new gemini flash model to work via an api call but I keep getting this error below [2025-04-18 13:01:21] | GEMINI_PROCESSOR | INFO | GeminiProcessor initialized with model: gemini-2.5-pro-exp-03-25 [2025-04-18 13:01:21] | GEMINI_PROCESSOR | INFO | Processing image: captured_frames\frame_2_20250412_210205.jpg [2025-04-18 13:01:21] | GEMINI_PROCESSOR | INFO | Error in API call: 400 INVALID_ARGUMENT. {'error': {'code': 400, 'message': '* GenerateContentRequest.contents[1].parts: contents.parts must not be empty.\n', 'status': 'INVALID_ARGUMENT'}} Traceback (most recent call last): The code class is as follows below import json import os import glob from google import genai from google.genai import types import time import cv2 import numpy as np from datetime import datetime from logging_setup import setup_logging import toml import sys class GeminiProcessor: """Class for processing images with Google's Gemini API to detect objects and calculate depths""" def __init__(self, logging_config): """Initialize the Gemini API processor""" config = toml.load("config.toml") self.log = setup_logging('GEMINI_PROCESSOR', logging_config) gemini_config = config.get("gemini", {}) self.output_dir = gemini_config.get("Output_Directory") self.results_dir = gemini_config.get("Results_Directory") self.image_path = gemini_config.get("image_directory") self.model_name = gemini_config.get("model_name") self.base_colors = [[0, 0, 255], [0, 255, 0], [255, 0, 0], [0, 255, 255]] self.safety_settings = [types.SafetySetting(category="HARM_CATEGORY_DANGEROUS_CONTENT",threshold="BLOCK_ONLY_HIGH",),] self.bounding_box_system_instructions = gemini_config.get("bounding_box_system_instructions") self.api_key = gemini_config.get("google_api_key") self.client = genai.Client(api_key=self.api_key) self.prompt = gemini_config.get("prompt") self.init_directories() self.log.info("GeminiProcessor initialized with model: %s", self.model_name) def init_directories(self): """Create necessary output directories""" for directory in [self.output_dir, self.results_dir]: if directory and not os.path.exists(directory): os.makedirs(directory) self.log.info(f"Created directory: {directory}") def parse_json(self, json_output: str): """Parse the JSON output from the Gemini API""" if not json_output or not isinstance(json_output, str): return "[]" lines = json_output.splitlines() for i, line in enumerate(lines): if line == "```json": json_output = "\n".join(lines[i+1:]) json_output = json_output.split("```")[0] break return json_output def save_parsed_json(self, json_output, image_name=None): """Parse and save JSON response to a file""" parsed_json = self.parse_json(json_output) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") base_name = f"response_{timestamp}" if image_name is None else f"{image_name}_{timestamp}" file_path = f"{self.results_dir}/{base_name}.json" try: json_data = json.loads(parsed_json) with open(file_path, 'w') as f: json.dump(json_data, f, indent=4) self.log.info(f"JSON response saved to {file_path}") return file_path except json.JSONDecodeError as e: self.log.error(f"Invalid JSON format: {e}") with open(file_path, 'w') as f: f.write(parsed_json) self.log.warning(f"Saved raw text to {file_path}") return None def plot_bounding_boxes(self, bounding_boxes, image_path: str): """Plot bounding boxes on the image""" image_files = glob.glob(f"{self.image_path}/*.*") if not image_files: self.log.error(f"No files found in {self.image_path}") return False img = cv2.imread(image_files) if img is None: self.log.error(f"Failed to read image from {image_path}") return False height, width = img.shape[:2] target_width, target_height = 640, 480 scale = min(target_width/width, target_height/height) img_resized = cv2.resize(img, (int(width*scale), int(height*scale)), interpolation=cv2.INTER_LANCZOS4) try: parsed_json = self.parse_json(bounding_boxes) boxes = json.loads(parsed_json) for i, box in enumerate(boxes): color = self.colors[i % len(self.colors)] # Convert normalized coordinates to absolute coordinates abs_y1 = int(box["box_2d"][0]/1000 * height) abs_x1 = int(box["box_2d"][1]/1000 * width) abs_y2 = int(box["box_2d"][2]/1000 * height) abs_x2 = int(box["box_2d"][3]/1000 * width) # Ensure correct order if abs_x1 > abs_x2: abs_x1, abs_x2 = abs_x2, abs_x1 if abs_y1 > abs_y2: abs_y1, abs_y2 = abs_y2, abs_y1 box["centroid"] = [(abs_x1 + abs_x2) / 2, (abs_y1 + abs_y2) / 2] cv2.rectangle(img_resized, (abs_x1, abs_y1), (abs_x2, abs_y2), color, 2) if "label" in box: label = box["label"] text_size, _ = cv2.getTextSize(label, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2) cv2.rectangle(img_resized, (abs_x1, abs_y1-5-text_size[1]), (abs_x1+text_size[0], abs_y1), color, -1) cv2.putText(img_resized, label, (abs_x1, abs_y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255,255,255), 2) timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") base_name = os.path.basename(image_path).split('.')[0] if isinstance(image_path, str) else "image" output_path = f"{self.output_dir}/{base_name}_annotated_{timestamp}.jpg" cv2.imwrite(output_path, img_resized) cv2.imshow("Detected Objects", img_resized) cv2.waitKey(1) return { "annotated_image": img_resized, "output_path": output_path, "boxes": boxes } except Exception as e: self.log.error(f"Error plotting bounding boxes: {e}") return False def run(self): """Run the Gemini API to process a single image in the directory""" client = self.client model = self.model_name image_path = self.image_path try: image_files = glob.glob(f"{self.image_path}/*.*") if not image_files: self.log.error(f"No files found in {self.image_path}") return False image_path = image_files[0] self.log.info(f"Processing image: {image_path}") img = cv2.imread(image_path) if img is None: self.log.error(f"Failed to load image: {image_path}") return False # temp_path = f"{self.output_dir}/temp_image.jpg" # cv2.imwrite(temp_path, img) # with open(temp_path, "rb") as f: # image_data = f.read() # image_parts = [ # { # "mime_type": "image/jpeg", # "data": image_data # } # ] # content = [ # {"text": self.prompt}, # {"image": image_parts} # ] response = client.models.generate_content(model=model, contents=[self.prompt, img], config=types.GenerateContentConfig( system_instruction=self.bounding_box_system_instructions, temperature=0.5, safety_settings=self.safety_settings ) ) # if os.path.exists(temp_path): # os.remove(temp_path) if response.text: # Delete previous json file for results_file in glob.glob(f"{self.results_dir}/*.json"): try: os.remove(results_file) self.log.info(f"Deleted previous result: {results_file}") except Exception as e: self.log.warning(f"Could not delete file {results_file}: {e}") # Save the parsed JSON response base_name = os.path.splitext(os.path.basename(image_path))[0] json_path = self.save_parsed_json(response.text, base_name) result = self.plot_bounding_boxes(response.text, image_path) return { "response": response.text, "json_path": json_path, "visualization": result } else: self.log.info("Empty response from API") return False except Exception as e: self.log.info(f"Error in API call: {e}") if 'temp_path' in locals() and os.path.exists(image_path): os.remove(image_path) return False below is the initiation in the main script to test it out if __name__ == '__main__': logging_config = LoggingConfig() camera = RealSenseCamera(logging_config) apriltag = ApriltagHomography(logging_config) api = GeminiProcessor(logging_config) transformer = CoordinateTransformer(logging_config) api.run() the link to the github is https://github.com/google-gemini/cookbook/blob/main/quickstarts/Spatial_understanding.ipynb Continue reading...