|
|
import base64 |
|
|
import requests |
|
|
from PIL import Image |
|
|
import io |
|
|
import json |
|
|
import logging |
|
|
import json |
|
|
import pandas as pd |
|
|
import re |
|
|
|
|
|
|
|
|
IMAGE_ANALYSES_KEY = "Image Analyses" |
|
|
|
|
|
logging.basicConfig(level=logging.INFO) |
|
|
|
|
|
import os |
|
|
api_key = os.environ.get("OPENAI_API_KEY") |
|
|
|
|
|
|
|
|
def encode_image(image_path): |
|
|
try: |
|
|
with Image.open(image_path) as img: |
|
|
img.thumbnail((800, 800)) |
|
|
buffer = io.BytesIO() |
|
|
img.save(buffer, format="JPEG", quality=85) |
|
|
encoded_image = base64.b64encode(buffer.getvalue()).decode('utf-8') |
|
|
return encoded_image |
|
|
except Exception as e: |
|
|
logging.error(f"Failed to encode image {image_path}: {e}") |
|
|
return None |
|
|
|
|
|
|
|
|
def get_image_dimensions(image_path): |
|
|
try: |
|
|
with Image.open(image_path) as img: |
|
|
return img.size |
|
|
except Exception as e: |
|
|
logging.error(f"Failed to get dimensions for image {image_path}: {e}") |
|
|
return (0, 0) |
|
|
|
|
|
|
|
|
image_paths = [ |
|
|
"data/competitor/image1.jpeg", |
|
|
"data/competitor/image2.jpeg", |
|
|
"data/competitor/image3.jpeg", |
|
|
"data/competitor/image4.jpeg", |
|
|
"data/competitor/image5.jpeg", |
|
|
"data/competitor/image6.jpeg" |
|
|
] |
|
|
|
|
|
|
|
|
headers = { |
|
|
"Content-Type": "application/json", |
|
|
"Authorization": f"Bearer {api_key}" |
|
|
} |
|
|
|
|
|
|
|
|
competitor_information = """ |
|
|
The competitor is a leading brand in the digital marketing space known for its consistent visual branding and innovative designs on social media. They target a tech-savvy audience aged 20-35 with a focus on modern aesthetics, engaging storytelling, and minimalist yet powerful branding. |
|
|
""" |
|
|
|
|
|
system_message = """ |
|
|
Analyze the branding, content marketing, and social media marketing effectiveness of a company for the provided Instagram post image. |
|
|
Evaluate it based on the following criteria and return a detailed JSON structure. Each criterion should have an individual score (0-10), and the total score for the category should be the average of its criteria. |
|
|
Give 9 or above only when it's extraordinary. Industry standard 7-8 |
|
|
### Categories and Definitions: |
|
|
|
|
|
#### Branding: |
|
|
- **Logo Placement**: Is the logo perfectly sized, clearly visible, and well-positioned without being intrusive? |
|
|
- **Brand Colors**: Are brand colors used consistently and without deviations? |
|
|
- **Typography**: Are the fonts aligned with the brand identity, consistent in style, and visually appealing? |
|
|
- **Brand Identity**: Does the post effectively reflect the brand's unique persona and messaging? |
|
|
- **Visual Hierarchy**: Are key elements prioritized effectively to guide the viewer’s attention? |
|
|
- **Template Consistency**: Are templates consistent with previous posts, reflecting a uniform design approach? |
|
|
- **Messaging Alignment**: Is the brand messaging clear, consistent, and reflective of the brand's tone? |
|
|
- **Subtle Branding**: Does the branding strike the right balance (not overly subtle or excessive)? |
|
|
- **Overbranding**: Does the post avoid overwhelming and distracting brand elements? |
|
|
- **Creative Variations**: Are there innovative and creative design variations in the post? |
|
|
|
|
|
#### Content Marketing: |
|
|
- **Content Visibility**: Is the primary content clear and highlighted effectively? |
|
|
- **Engagement Cues**: Are clear and compelling calls-to-action (CTAs) present and engaging? |
|
|
- **Information Overload**: Does the post avoid over-saturated visuals or excessive text? |
|
|
- **Storytelling**: Does the post convey a relevant and engaging narrative that resonates with the audience? |
|
|
- **Content Variety**: Are the posts diverse and free of monotonous or repetitive elements? |
|
|
- **Typography Consistency**: Is the typography visually attractive and consistent throughout the design? |
|
|
- **Aesthetic Coherence**: Do all elements harmonize to create a visually appealing composition? |
|
|
- **Content Relevance**: Is the content relevant to the brand and its target audience? |
|
|
- **Stock Elements**: Does the design avoid excessive use of generic or stock imagery? |
|
|
|
|
|
#### Social Media Marketing: |
|
|
- **Font Size**: Are fonts appropriately sized and legible on various screen sizes? |
|
|
- **Visibility of Text**: Is the text easy to read, with proper contrast, placement, and spacing? |
|
|
- **Logo Placement**: Does the logo placement avoid disrupting the design's aesthetic appeal? |
|
|
- **Consistency**: Does the post maintain design cohesion across elements? |
|
|
- **Alignment**: Are elements aligned professionally, creating a balanced and clean layout? |
|
|
- **Aesthetic Appeal**: Is the overall design visually engaging and suitable for the platform? |
|
|
- **Brand Elements**: Are brand assets (like logos, icons, or visuals) used effectively and sparingly? |
|
|
- **Repetitiveness**: Does the design avoid repetitive themes and offer fresh creative ideas? |
|
|
|
|
|
### Output JSON Format: |
|
|
### Only return the following format strictly |
|
|
|
|
|
{ |
|
|
"Branding Score": total_avg_score, explanation. |
|
|
"criteria_name": score, explanation. |
|
|
"criteria_name": score, explanation. |
|
|
|
|
|
"Content Marketing Score": total_avg_score, explanation. |
|
|
"criteria_name": score, explanation. |
|
|
"criteria_name": score, explanation. |
|
|
|
|
|
"Social Media Marketing Score": total_avg_score, explanation. |
|
|
"criteria_name": score, explanation. |
|
|
"criteria_name": score, explanation. |
|
|
} |
|
|
""" |
|
|
|
|
|
|
|
|
def request_analysis(system_message, user_message, model="gpt-4o-mini", max_tokens=1500): |
|
|
payload = { |
|
|
"model": model, |
|
|
"messages": [ |
|
|
{"role": "system", "content": system_message}, |
|
|
{"role": "user", "content": user_message} |
|
|
], |
|
|
"max_tokens": max_tokens |
|
|
} |
|
|
logging.debug(f"Payload Sent: {json.dumps(payload, indent=4)}") |
|
|
response = requests.post("https://api.openai.com/v1/chat/completions", headers=headers, json=payload) |
|
|
|
|
|
if response.status_code == 200: |
|
|
try: |
|
|
return response.json().get("choices", [{}])[0].get("message", {}).get("content", "Unexpected response format") |
|
|
except Exception as e: |
|
|
logging.error(f"Error parsing response: {e}") |
|
|
logging.debug(f"Raw Response: {response.text}") |
|
|
return "Error parsing the response." |
|
|
else: |
|
|
logging.error(f"API Error: {response.status_code}, {response.text}") |
|
|
return "Error with the API request." |
|
|
|
|
|
|
|
|
output_structure = { |
|
|
IMAGE_ANALYSES_KEY: [] |
|
|
} |
|
|
|
|
|
|
|
|
for image_path in image_paths: |
|
|
base64_image = encode_image(image_path) |
|
|
if not base64_image: |
|
|
output_structure[IMAGE_ANALYSES_KEY].append({ |
|
|
"Image": image_path, |
|
|
"Analysis": "Failed to encode image." |
|
|
}) |
|
|
continue |
|
|
|
|
|
width, height = get_image_dimensions(image_path) |
|
|
user_message = f""" |
|
|
Company Information: {competitor_information} |
|
|
Analyze the Instagram post with dimensions {width}x{height} pixels. Image data: {base64_image} |
|
|
""" |
|
|
|
|
|
try: |
|
|
analysis_result = request_analysis(system_message, user_message) |
|
|
try: |
|
|
|
|
|
parsed_result = json.loads(analysis_result) |
|
|
except json.JSONDecodeError: |
|
|
logging.warning(f"Response not in expected JSON format for {image_path}: {analysis_result}") |
|
|
parsed_result = {"Raw Response": analysis_result} |
|
|
|
|
|
output_structure[IMAGE_ANALYSES_KEY].append({ |
|
|
"Image": image_path, |
|
|
"Analysis": parsed_result |
|
|
}) |
|
|
except Exception as err: |
|
|
logging.error(f"Error analyzing image {image_path}: {err}") |
|
|
output_structure[IMAGE_ANALYSES_KEY].append({ |
|
|
"Image": image_path, |
|
|
"Analysis": "Error analyzing the image." |
|
|
}) |
|
|
|
|
|
|
|
|
with open("Output_File/json/competitor_analysis.json", "w") as f: |
|
|
json.dump(output_structure, f, indent=4) |
|
|
|
|
|
logging.info("Analysis completed and saved to competitor_analysis.json") |
|
|
|
|
|
|
|
|
def json_to_excel(json_file, excel_file): |
|
|
""" |
|
|
Parse the JSON file and convert it to an Excel file with structured scores and raw JSON responses. |
|
|
|
|
|
Args: |
|
|
json_file (str): Path to the input JSON file. |
|
|
excel_file (str): Path to the output Excel file. |
|
|
""" |
|
|
|
|
|
with open(json_file, 'r') as file: |
|
|
data = json.load(file) |
|
|
|
|
|
|
|
|
structured_data = [] |
|
|
|
|
|
|
|
|
score_pattern = r'"([a-zA-Z\s]+)":\s*(\d+),' |
|
|
|
|
|
|
|
|
for analysis in data.get("Image Analyses", []): |
|
|
|
|
|
image = analysis.get("Image", "Unknown") |
|
|
|
|
|
|
|
|
raw_response = analysis.get("Analysis", {}).get("Raw Response", "") |
|
|
|
|
|
|
|
|
image_data = {"Image": image, "Raw JSON Response": raw_response} |
|
|
|
|
|
|
|
|
matches = re.findall(score_pattern, raw_response) |
|
|
for criterion, score in matches: |
|
|
image_data[criterion.strip()] = int(score) |
|
|
|
|
|
|
|
|
structured_data.append(image_data) |
|
|
|
|
|
|
|
|
df = pd.DataFrame(structured_data) |
|
|
|
|
|
|
|
|
df.to_excel(excel_file, index=False) |
|
|
print(f"Data successfully written to {excel_file}") |
|
|
|
|
|
json_file_path = "Output_File/json/competitor_analysis.json" |
|
|
excel_file_path = "Output_File/excel/competitor_analysis.xlsx" |
|
|
json_to_excel(json_file_path, excel_file_path) |
|
|
|