diff --git a/src/__pycache__/async_request.cpython-312.pyc b/src/__pycache__/async_request.cpython-312.pyc new file mode 100644 index 0000000..4aefa6e Binary files /dev/null and b/src/__pycache__/async_request.cpython-312.pyc differ diff --git a/src/__pycache__/main.cpython-312.pyc b/src/__pycache__/main.cpython-312.pyc new file mode 100644 index 0000000..361f39c Binary files /dev/null and b/src/__pycache__/main.cpython-312.pyc differ diff --git a/src/__pycache__/scene_split.cpython-312.pyc b/src/__pycache__/scene_split.cpython-312.pyc index afc6267..abdbc76 100644 Binary files a/src/__pycache__/scene_split.cpython-312.pyc and b/src/__pycache__/scene_split.cpython-312.pyc differ diff --git a/src/__pycache__/slideshow.cpython-312.pyc b/src/__pycache__/slideshow.cpython-312.pyc index 31b3850..27ed67c 100644 Binary files a/src/__pycache__/slideshow.cpython-312.pyc and b/src/__pycache__/slideshow.cpython-312.pyc differ diff --git a/src/__pycache__/text_to_img.cpython-312.pyc b/src/__pycache__/text_to_img.cpython-312.pyc index 25186e8..c59b8f4 100644 Binary files a/src/__pycache__/text_to_img.cpython-312.pyc and b/src/__pycache__/text_to_img.cpython-312.pyc differ diff --git a/src/async_request.py b/src/async_request.py index ba48716..f846610 100644 --- a/src/async_request.py +++ b/src/async_request.py @@ -3,18 +3,37 @@ from typing import Dict, Any async def send_request(session: aiohttp.ClientSession, url: str, data: Dict[str, Any]) -> None: + """ + Sends an asynchronous POST request to the specified URL with the given data. + + Args: + session (aiohttp.ClientSession): The aiohttp session to use for the request. + url (str): The URL to send the request to. + data (Dict[str, Any]): The JSON data to include in the request body. + """ async with session.post(url, json=data) as response: print(f"Sent request to {url}") async def periodic_requests(url: str, data: Dict[str, Any], interval: int, count: int) -> None: + """ + Sends periodic asynchronous requests to a specified URL. + + Args: + url (str): The URL to send requests to. + data (Dict[str, Any]): The data to send with each request. + interval (int): The time interval (in seconds) between requests. + count (int): The total number of requests to send. + """ async with aiohttp.ClientSession() as session: for _ in range(count): - asyncio.create_task(send_request(session, url, data)) # Fire and forget - await asyncio.sleep(interval) # Wait before sending the next request + # Schedule the request to be sent immediately (fire and forget) + asyncio.create_task(send_request(session, url, data)) + # Pause execution for the specified interval without blocking the event loop + await asyncio.sleep(interval) url = "https://api.example.com/endpoint" data = {"key": "value"} -interval = 5 # Seconds between requests -count = 10 # Number of requests +interval = 5 +count = 10 asyncio.run(periodic_requests(url, data, interval, count)) \ No newline at end of file diff --git a/src/main.py b/src/main.py index df445d5..51aee01 100644 --- a/src/main.py +++ b/src/main.py @@ -1,4 +1,5 @@ import timeit +# Start the timer to measure the total execution time of the script start = timeit.default_timer() import json import os @@ -6,41 +7,55 @@ import text_to_img import shutil +# Read the story content from a text file with open("story.txt", "r", encoding="utf-8") as f: story = f.read() def clear_story_folder(folder_path: str = "story") -> None: + """ + Clears the specified folder if it exists, or creates it if it doesn't. + + Args: + folder_path (str): The path to the folder. Defaults to "story". + """ + # Check if the folder already exists if os.path.exists(folder_path): + # Remove the folder and all its contents shutil.rmtree(folder_path) + # Create a new, empty folder os.makedirs(folder_path) +# Prepare the output directory for images clear_story_folder() + +# Split the story into distinct scenes using semantic similarity +# The threshold 0.7 determines how similar sentences must be to remain in the same scene scenes = scene_split.main(story,0.7) print("Scenes splitted successfully!") -# print(scenes) -# for i in scenes: -# print(i[0]) -# Scenes to prompt -# print(scenes) number_of_scenes = len(scenes) print(f"Number of scenes: {number_of_scenes}") print("\n") +# Prompt the user for the desired artistic style image_type = input("Enter the type of image you want to generate (realistic, cartoon, abstract): ") print("\n") +# Iterate through each scene to generate a corresponding image for i, scene in enumerate(scenes, 1): + # Construct the prompt for image generation, including the user's chosen style prompt = f"Make a {image_type} image of" + scene - # print(prompt) + # Call the image generation function text_to_img.main(prompt, f"story/image-{i}") -# Folder and File name generation +# Create a dictionary mapping image filenames to their corresponding scene text story_dict = {f"story/image-{i}.png": line for i, line in enumerate(scenes, 1)} +# Save the mapping to a JSON file for use in the slideshow with open("story.json", "w") as f: json.dump(story_dict, f, indent=4) +# Stop the timer and print the total execution time end = timeit.default_timer() print(f"Time taken: {end-start} seconds") diff --git a/src/scene_split.py b/src/scene_split.py index 8ea4ead..0805f43 100644 --- a/src/scene_split.py +++ b/src/scene_split.py @@ -10,9 +10,28 @@ story_text: str = """John walked into the forest. He heard rustling behind him. The trees loomed tall as he pressed forward, his heart pounding. Later that night, he found a small cabin. It looked abandoned, but the door creaked open when he pushed it. The wind howled outside as he stepped in. Inside the cabin, an old man sat by the fire. He wore a long cloak and stared at John as if expecting him. In the morning, John woke up to find the man missing. The fire had gone cold. He stepped outside and saw footprints leading into the misty woods. With no other choice, he followed the footprints. The deeper he went, the more uneasy he felt, as if someone—or something—was watching him.""" def split_into_sentences(text: str) -> List[str]: + """ + Splits a given text into a list of sentences based on punctuation. + + Args: + text (str): The input text to split. + + Returns: + List[str]: A list of sentences found in the text. + """ return re.findall(r"[^.!?]+", text) def main(story_text: str, threshold: float = 0.5) -> List[str]: + """ + Splits the story text into meaningful scenes using semantic similarity. + + Args: + story_text (str): The full text of the story. + threshold (float): The similarity threshold for merging sentences. Defaults to 0.5. + + Returns: + List[str]: A list of merged sentences representing scenes. + """ index_name = "text-search" sentences = split_into_sentences(story_text) @@ -24,26 +43,39 @@ def main(story_text: str, threshold: float = 0.5) -> List[str]: pc = Pinecone(api_key=api_key) index = pc.Index(index_name) + # Load the SentenceTransformer model for generating text embeddings model = SentenceTransformer("all-MiniLM-L6-v2") + # Generate embeddings for all sentences at once embeddings = model.encode(sentences) merged_sentences = [] similarity_array = [] i = 0 + # Iterate through sentences to determine if they should be merged while i < len(sentences) - 1: + # Get embeddings for current and next sentence vector_1, vector_2 = embeddings[i], embeddings[i + 1] + + # Example of querying Pinecone index (if used in future) response = index.query(vector=vector_1.tolist(), top_k=1, include_values=True) + + # Calculate Cosine Similarity between the two sentence vectors + # Formula: (A . B) / (||A|| * ||B||) similarity = np.dot(vector_1, vector_2) / (np.linalg.norm(vector_1) * np.linalg.norm(vector_2)) + similarity_array.append([similarity,sentences[i],sentences[i+1]]) + + # If sentences are similar enough, merge them into one scene if similarity >= threshold: sentences[i + 1] = sentences[i] + ". " + sentences[i + 1] else: + # Otherwise, the current sentence is a complete scene merged_sentences.append(sentences[i]) i += 1 + # Append the last sentence/scene merged_sentences.append(sentences[-1]) - # print("Similarity array:", similarity_array) return merged_sentences if __name__ == "__main__": diff --git a/src/slideshow.py b/src/slideshow.py index c8c676d..a9ae93e 100644 --- a/src/slideshow.py +++ b/src/slideshow.py @@ -6,15 +6,18 @@ with open("story.json", 'r') as file: image_texts = json.load(file) +# Iterate through keys to get image paths image_paths: List[str] = list(image_texts.keys()) -# print(image_texts) +# Initialize the main window for the application root = tk.Tk() root.title("Image Slideshow with Text") +# Label widget to display the image img_label = tk.Label(root) img_label.pack() +# Label widget to display the text, with wrapping text_label = tk.Label(root, text="", font=("Arial", 14), wraplength=600) text_label.pack(pady=10) @@ -22,40 +25,64 @@ paused: bool = False def update_image() -> None: + """ + Updates the displayed image and text based on the current index. + Schedules the next update unless paused. + """ global idx, paused + # If paused, do not update the image if paused: return + # Retrieve current image path and text img_path = image_paths[idx] text = image_texts[img_path] + # Open and resize the image for display img = Image.open(img_path) img = img.resize((600, 400)) img = ImageTk.PhotoImage(img) + # Update the labels img_label.config(image=img) - img_label.image = img + img_label.image = img # Keep a reference to prevent garbage collection text_label.config(text=text) + # Move to the next index (looping back to 0 if at end) idx = (idx + 1) % len(image_texts) + + # Schedule this function to run again after 5000ms (5 seconds) root.after(5000, update_image) def toggle_pause() -> None: + """ + Toggles the pause state of the slideshow. + """ global paused paused = not paused + # If unpausing, immediately update to resume the cycle if not paused: update_image() def next_image() -> None: + """ + advances to the next image in the slideshow. + """ global idx + # Increment index with wrap-around idx = (idx + 1) % len(image_texts) update_image() def prev_image() -> None: + """ + Moves to the previous image in the slideshow. + """ global idx + # Decrement index with wrap-around idx = (idx - 1) % len(image_texts) update_image() +# Frame to hold control buttons btn_frame = tk.Frame(root) btn_frame.pack() diff --git a/src/text_to_img.py b/src/text_to_img.py index d6afb87..6c2d29f 100644 --- a/src/text_to_img.py +++ b/src/text_to_img.py @@ -11,6 +11,14 @@ client = Together(api_key=TOGETHER_API_KEY) def main(myprompt: str, img_file_name: str): + """ + Generates an image from a prompt using the Together AI API and saves it to a file. + + Args: + myprompt (str): The text prompt for scanning content. + img_file_name (str): The base name for the output image file (without extension). + """ + # Call the Together AI API to generate an image response = client.images.generate( prompt=myprompt, model="black-forest-labs/FLUX.1-schnell-Free", @@ -18,16 +26,19 @@ def main(myprompt: str, img_file_name: str): height=768, steps=1, n=1, - response_format="b64_json", + response_format="b64_json", # Request the image as a Base64 encoded string ) - # print(response.data[0].b64_json) + + # Extract the Base64 string from the response imgstring: str = response.data[0].b64_json + + # Decode the Base64 string into binary image data imgdata: bytes = base64.b64decode(imgstring) + + # Construct the filename and save the binary data filename: str = f'{img_file_name}.png' with open(filename, 'wb') as f: f.write(imgdata) - # image = Image.open(filename) - # image.show() if __name__=="__main__": main("Cat eating burger", "burger-cat") \ No newline at end of file