Processare immagini da Google AI Studio con Angular

Mattepuffo's logo
Processare immagini da Google AI Studio con Angular

Processare immagini da Google AI Studio con Angular

Quello che vogliamo ottenere è inviare richieste HTTP a Google AI Studio, che fa parte delle Gemini API, per processare una immagine.

Invieremo anche un prompt; il tutto da Angular.

Il codice potrebbe cambiare un pò a seconda della versione di Angular; nel mio caso parliamo della versione 19.

Prima di tutto avete bisogno di una API KEY che potete creare direttamente in Google AI Studio.

Cominciamo dall'app.config.ts dove aggiungiamo un provider:

import {ApplicationConfig, provideZoneChangeDetection} from '@angular/core';
import {provideRouter} from '@angular/router';

import {routes} from './app.routes';
import {provideHttpClient, withFetch} from "@angular/common/http";

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({eventCoalescing: true}),
    provideRouter(routes),
    provideHttpClient(withFetch())
  ]
};

Questo il mio component:

import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Component} from '@angular/core';
import {JsonPipe, NgIf} from "@angular/common";
import {FormsModule} from "@angular/forms";

@Component({
  selector: 'app-root',
  imports: [JsonPipe, NgIf, FormsModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})

export class AppComponent {
  private apiKey = '';
  private apiUrl = `https://generativelanguage.googleapis.com/v1beta/models/gemini-1.5-flash:generateContent?key=${this.apiKey}`;

  promptText: string = "";

  selectedFile: File | null = null;
  imageBase64: string | null = null;
  analysisResult: any = null;
  isLoading: boolean = false;
  errorMessage: string | null = null;
  rawAiResponseForDebug: string | null = null;

  constructor(private http: HttpClient) {
  }

  onFileSelected(event: any): void {
    const file: File = event.target.files[0];
    if (file) {
      this.selectedFile = file;
      this.errorMessage = null;
      this.analysisResult = null;
      this.rawAiResponseForDebug = null;
      const reader = new FileReader();
      reader.onload = (e: any) => {
        this.imageBase64 = e.target.result.split(',')[1];
      };
      reader.readAsDataURL(file);
    }
  }

  analyzeImage(): void {
    if (!this.selectedFile || !this.imageBase64) {
      this.errorMessage = "Per favore, seleziona un'immagine.";
      return;
    }
    if (!this.promptText.trim()) {
      this.errorMessage = "Per favore, inserisci un prompt.";
      return;
    }

    this.isLoading = true;
    this.analysisResult = null;
    this.errorMessage = null;
    this.rawAiResponseForDebug = null;

    const requestBody = {
      contents: [
        {
          parts: [
            {text: this.promptText},
            {
              inline_data: {
                mime_type: this.selectedFile.type,
                data: this.imageBase64
              }
            }
          ]
        }
      ]
    };

    const headers = new HttpHeaders({
      'Content-Type': 'application/json'
    });

    this.http.post(this.apiUrl, requestBody, {headers}).subscribe(
        (response: any) => {
          this.isLoading = false;

          try {
            const textFromAI = response.candidates?.[0]?.content?.parts?.[0]?.text;

            if (!textFromAI) {
              if (response.promptFeedback && response.promptFeedback.blockReason) {
                this.errorMessage = `Richiesta bloccata dall'AI: ${response.promptFeedback.blockReason}.`;
                if (response.promptFeedback.safetyRatings) {
                  this.errorMessage += ` Dettagli sicurezza: ${JSON.stringify(response.promptFeedback.safetyRatings)}`;
                }
                console.error('Richiesta bloccata:', response.promptFeedback);
                this.rawAiResponseForDebug = JSON.stringify(response.promptFeedback, null, 2);
              } else {
                throw new Error("Risposta dell'AI non trovata o in formato inatteso nel corpo della risposta.");
              }
              return;
            }

            this.rawAiResponseForDebug = textFromAI;

            let jsonStringToParse = textFromAI;

            const startIndex = jsonStringToParse.indexOf("```json");
            const endIndex = jsonStringToParse.lastIndexOf("```");

            if (startIndex !== -1) {
              const contentStartIndex = startIndex + 7; // Dopo "```json"
              if (endIndex > contentStartIndex) {
                jsonStringToParse = jsonStringToParse.substring(contentStartIndex, endIndex);
              } else {
                jsonStringToParse = jsonStringToParse.substring(contentStartIndex);
              }
            } else if (jsonStringToParse.indexOf("```") !== -1 && jsonStringToParse.trim().startsWith("{") && jsonStringToParse.trim().endsWith("}")) {
              console.warn("Trovati ``` ma la stringa trimmata sembra già JSON. Si procede con cautela.");
            }

            jsonStringToParse = jsonStringToParse.trim();
            console.log('Stringa finale da parsare come JSON:', jsonStringToParse);

            if (!jsonStringToParse) {
              throw new Error("Dopo la pulizia, la stringa JSON risulta vuota.");
            }

            this.analysisResult = JSON.parse(jsonStringToParse);
            this.rawAiResponseForDebug = null;

          } catch (error: any) {
            this.errorMessage = `Errore nell'interpretare la risposta dell'AI come JSON. Dettagli: ${error.message}. Controlla la console e la risposta grezza qui sotto.`;
          }
        },
        (error: any) => {
          this.isLoading = false;
          let detailedMessage = '';
          if (error.error && error.error.error && error.error.error.message) {
            detailedMessage = ` Dettaglio: ${error.error.error.message}`;
          }
          this.errorMessage = `Errore durante la chiamata API: ${error.status || error.message || 'Errore sconosciuto'}.${detailedMessage} Controlla la console.`;
          this.rawAiResponseForDebug = JSON.stringify(error.error, null, 2) || "Nessun dettaglio errore nel corpo della risposta.";
        }
    );
  }

}

Questa la pagina;

<div>
  <h2>Analizzatore Immagini con Google AI</h2>

  <div>
    <label for="prompt">Prompt:</label><br>
    <textarea id="prompt" [(ngModel)]="promptText" rows="4" cols="50" style="width: 100%;"></textarea>
  </div>
  <br>
  <div>
    <label for="imageUpload">Seleziona un'immagine:</label><br>
    <input type="file" id="imageUpload" (change)="onFileSelected($event)" accept="image/png, image/jpeg, image/webp">
  </div>
  <br>
  <button (click)="analyzeImage()" [disabled]="isLoading || !selectedFile">
    {{ isLoading ? 'Analisi in corso...' : 'Analizza Immagine' }}
  </button>

  <div *ngIf="errorMessage" style="color: red; margin-top: 15px; border: 1px solid red; padding: 10px;">
    <strong>Errore:</strong> {{ errorMessage }}
    <div *ngIf="rawAiResponseForDebug" style="margin-top: 10px;">
      <strong>Risposta grezza dall'AI (per debug):</strong>
      <pre style="background-color: #ffebeb; color: #333; white-space: pre-wrap; word-wrap: break-word;">{{ rawAiResponseForDebug }}</pre>
    </div>
  </div>

  <div *ngIf="analysisResult && !errorMessage" style="margin-top: 20px;">
    <h3>Risultato Analisi:</h3>
    <pre>{{ analysisResult | json }}</pre>
  </div>

  <div *ngIf="imageBase64" style="margin-top: 20px;">
    <h4>Anteprima Immagine (selezionata):</h4>
    <img [src]="'data:' + (selectedFile?.type || 'image/jpeg') + ';base64,' + imageBase64" alt="Anteprima immagine" style="max-width: 300px; max-height: 300px; border: 1px solid #ccc;">
  </div>
</div>

Enjoy!


Condividi

Commentami!