Initial commit

This commit is contained in:
ru4ls
2025-10-17 16:49:30 +07:00
commit 3d08f95b17
13 changed files with 455 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
GOOGLE_STREET_VIEW_API_KEY = "PASTE_YOUR_API_KEY_HERE"
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+53
View File
@@ -0,0 +1,53 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg
# Logs
*.log
# Environment
env/
venv/
ENV/
.venv/
env.bak/
venv.bak/
.env
# IDE
.idea/
.vscode/
*.swp
*.swo
# ComfyUI
*.bin
*.pth
output/
input/
# Config
config.json
# OS
.DS_Store
Thumbs.db
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 ru4ls
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+163
View File
@@ -0,0 +1,163 @@
# ComfyUI Street View Loader Node
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Python 3.12](https://img.shields.io/badge/Python-3.12-blue.svg)](https://www.python.org/downloads/release/python-3120/)
A custom node for ComfyUI that allows you to load images directly from Google Street View to use as backgrounds, textures, or inputs in your workflows.
Instead of manually taking screenshots, this node programmatically fetches a clean, high-resolution image from any location on Earth with Street View coverage, giving you precise control over the camera angle, direction, and field of view.
![Street View Loader Node combine with NanoBanana in ComfyUI](media/preview.png)
---
## Why Use This Node? The Power of "Ground Truth"
While standard text-to-image models are incredible at *imagining* locations ("a charming street in Paris"), they struggle to recreate the **exact appearance and layout** of a specific, real-world place. Their output is an approximation, an artistic blend of countless images.
This node solves that problem by providing **photographic ground truth**. It pulls the real, pixel-for-pixel view of a location, ensuring your generations are anchored in reality.
This gives you the best of both worlds: the authenticity of a real photograph combined with the creative power of AI.
### Key Advantages & Use Cases
* **Guaranteed Location Accuracy:** When you need the background to be recognizably *your* street, a specific landmark, or a favorite travel spot, this node is the only way to guarantee a 100% accurate representation. The composition, architecture, and lighting are real, not AI-generated guesswork.
* **The Ultimate Base for `img2img` and ControlNet:** This is the most powerful use case. You can feed the real Street View image into your workflow as a base for:
* **Stylization:** Keep the exact composition of a place while transforming it into an oil painting, an anime scene, or a charcoal sketch.
* **Restoration/Re-imagining:** Take a view of an old neighborhood and use AI to add futuristic elements, or render it as it might have looked in a different historical period.
* **Precise Inpainting:** Add characters, objects, or fantastical elements into a real-world scene with a background that is perfectly stable and coherent.
* **Virtual Location Scouting:** For concept artists, filmmakers, and game developers. You can instantly scout real-world locations from your desktop, pull them into ComfyUI, and experiment with different styles and moods for your project without ever leaving your chair.
* **Personalized and Sentimental Art:** Create a unique piece of art based on a place with personal meaning—a childhood home, a proposal spot, or a favorite vacation view. This creates a connection that a generic prompt could never achieve.
* **Consistent Backgrounds for Testing:** When testing LoRAs, IPAdapters, or character models, using a consistent, real-world background from this node ensures that you are only seeing the changes from your model, not the random variations of a `txt2img` background.
![Street View Loader Node combine with NanoBanana in ComfyUI](media/preview_2.png)
## Features
- **Direct API Integration:** Pulls images directly from the Google Street View Static API.
- **Full Camera Control:** Adjust Location, Heading (pan), Pitch (tilt), and Field of View (zoom).
- **Secure API Key Storage:** Uses a `.env` file to keep your API key safe and out of your workflow files.
- **Clean Output:** No UI overlays, just the pure image.
- **Easy Workflow Integration:** Outputs a standard `IMAGE` tensor and a metadata string (the fetch URL) for debugging.
---
## 1. Installation
1. Navigate to your ComfyUI `custom_nodes` directory:
```bash
cd ComfyUI/custom_nodes/
```
2. Clone this repository:
```bash
git clone https://github.com/ru4ls/ComfyUI_StreetView-Loader.git
```
3. Install the required Python dependencies. Open a terminal, navigate to the new `ComfyUI_StreetView-Loader` folder, and run:
```bash
pip install -r requirements.txt
```
* this will install the `requests` and `python-dotenv` libraries, which are required for this node.*
4. **Restart ComfyUI.**
---
## 2. Setup: Getting Your Google API Key (Crucial Step)
This node requires a Google Cloud API key to function. Google provides a generous **$200/month free credit**, which is more than enough for extensive personal use without any charge.
### Step-by-Step Guide
**Part A: Create Project & Enable API**
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. If you don't have a project, create one. If you do, click the project selector at the top and click **"New Project"**. Give it a name like `ComfyUI-API`.
3. Once the project is created, search for "API Library" in the top search bar.
4. In the library, search for and select **"Street View Static API"**.
5. Click the **"Enable"** button.
**Part B: Set Up Billing**
6. You will be prompted to link a billing account. This is required, but you will **not be charged** unless you exceed the $200 free monthly credit (equivalent to tens of thousands of image requests).
**Part C: Create and Secure Your API Key**
7. In the Cloud Console search bar, navigate to **"Credentials"**.
8. Click **"+ Create Credentials"** and select **"API key"**.
9. Your new API key will be displayed. **Copy this key immediately.**
10. **(IMPORTANT!)** Click the **"Edit API key"** button. Under "API restrictions," select **"Restrict key"**.
11. In the dropdown, find and check **"Street View Static API"**, then click "OK". This ensures your key can *only* be used for this purpose, protecting your account. Click **"Save"**.
**Part D: Configure the Node**
12. In your `ComfyUI/custom_nodes/ComfyUI_StreetView-Loader/` folder, create a new file named `.env`. or rename an existing `.env.example` file to `.env`.
13. Open this `.env` file and add your copied API key in the following format:
```
GOOGLE_STREET_VIEW_API_KEY="your_actual_api_key_goes_here"
```
14. Save the file.
Your setup is now complete!
---
## 3. How to Use
1. After restarting ComfyUI, double-click the canvas and search for **"Street View Loader"**.
2. The node will appear with several input fields.
### Finding Your Parameters
The best way to get the exact `location`, `heading`, `pitch`, and `fov` is to use the Google Maps URL.
1. Open [Google Maps](https://maps.google.com) and drop into Street View at your desired location.
2. Pan, tilt, and zoom the camera until you frame the perfect shot.
3. Look at the URL in your browser's address bar. It will look like this:
`https://www.google.com/maps/@**40.74844,-73.98566**,3a,**75**y,**273.99**h,**85.73**t/data=...`
- `@**40.74844,-73.98566**`: This is the **`location`**.
- `**75**y`: This is the **`fov`** (Field of View / Zoom).
- `**273.99**h`: This is the **`heading`**.
- `**85.73**t`: This is related to the **`pitch`**. The API uses a range of -90 (straight down) to +90 (straight up) with 0 being the horizon. A `t` value of `90` in the URL is roughly a `pitch` of `0` for the API. Adjust as needed.
4. Copy these values into the corresponding fields on the node and generate!
### **The Easy Way: Using the URL Parser Node**
As of version 2.0, the recommended workflow is to use the **Street View URL Parser** node. This node extracts all the camera parameters automatically from a single Google Maps URL.
1. Add two nodes to your workflow:
- `Street View URL Parser` (found in `Ru4ls/StreetView/Utils`)
- `Street View Loader`
2. Find the exact view you want in Google Street View on your browser.
3. Copy the **entire URL** from your browser's address bar.
4. Paste the URL into the `url` field of the **Parser** node.
5. Connect the outputs of the Parser node to the inputs of the Loader node (`location` to `location`, `heading` to `heading`, etc.).
This method is faster, easier, and less error-prone than manually entering each parameter.
### Node Inputs
- `location`: The latitude and longitude (e.g., `40.74844,-73.98566`).
- `heading`: The compass direction [0 to 360]. 0 is North, 90 is East, 180 is South, 270 is West.
- `pitch`: The up/down tilt of the camera [-90 to 90]. 0 is the horizon.
- `fov`: The field of view, or zoom [10 to 120]. A lower number is more zoomed in.
- `width` / `height`: The dimensions of the output image (max 640x640 for the free API).
---
## 4. Troubleshooting
- **Node throws a `ValueError: API key not found`:** Your `.env` file is missing, in the wrong location, or the variable name is not `GOOGLE_STREET_VIEW_API_KEY`. It must be in the `ComfyUI_StreetView-Loader` folder.
- **The node outputs a black image:** This almost always means Google has no Street View imagery for that exact coordinate, or the API key is invalid/restricted. Check your key's restrictions in the Google Cloud Console and try a slightly different coordinate. The `metadata` output will contain an error message.
- **The node doesn't appear in ComfyUI:** Ensure you have fully restarted the ComfyUI server after installation.
## License
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
+20
View File
@@ -0,0 +1,20 @@
# file: ComfyUI_StreetView-Loader\__init__.py
from .nodes.streetview_loader import StreetViewLoader
from .nodes.streetview_url_parser import StreetViewURLParser
NODE_CLASS_MAPPINGS = {
"StreetViewLoader": StreetViewLoader,
"StreetViewURLParser": StreetViewURLParser,
}
NODE_DISPLAY_NAME_MAPPINGS = {
"StreetViewLoader": "Street View Loader",
"StreetViewURLParser": "Street View URL Parser",
}
__all__ = ['NODE_CLASS_MAPPINGS', 'NODE_DISPLAY_NAME_MAPPINGS']
print("------------------------------------------")
print("ComfyUI Street View Loader Node: Loaded.")
print("------------------------------------------")
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 379 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

View File
+77
View File
@@ -0,0 +1,77 @@
# file: ComfyUI_StreetView-Loader\nodes\streetview_loader.py
import torch
import numpy as np
import os
from dotenv import load_dotenv
from PIL import Image
from ..utils.connect_api_utils import fetch_streetview_image
# --- Load API Key from .env file ---
current_dir = os.path.dirname(os.path.abspath(__file__))
parent_dir = os.path.dirname(current_dir)
dotenv_path = os.path.join(parent_dir, '.env')
# Load the environment variables from the found .env file
load_dotenv(dotenv_path=dotenv_path)
# Get the API key from the loaded environment variables
API_KEY_FROM_ENV = os.getenv("GOOGLE_STREET_VIEW_API_KEY")
class StreetViewLoader:
"""
A ComfyUI node to load images directly from the Google Street View Static API,
using a secret API key stored in a .env file.
"""
@classmethod
def INPUT_TYPES(s):
"""
Defines the input fields for the node. The API key is handled in the
background and is not exposed as an input in the UI.
"""
return {
"required": {
"location": ("STRING", {
"multiline": False,
"default": "40.720032,-73.988354" # Example: Near Katz's Deli, NYC
}),
"heading": ("FLOAT", {"default": 151.78, "min": 0, "max": 360, "step": 0.1, "display": "slider"}),
"pitch": ("FLOAT", {"default": -0.76, "min": -90, "max": 90, "step": 0.1, "display": "slider"}),
"fov": ("INT", {"default": 90, "min": 10, "max": 120, "step": 1, "display": "slider"}),
"width": ("INT", {"default": 512, "min": 64, "max": 640, "step": 8}),
"height": ("INT", {"default": 512, "min": 64, "max": 640, "step": 8}),
}
}
RETURN_TYPES = ("IMAGE", "STRING")
RETURN_NAMES = ("image", "metadata")
FUNCTION = "load_image"
CATEGORY = "Ru4ls/StreetView"
def load_image(self, location, heading, pitch, fov, width, height):
if not API_KEY_FROM_ENV:
raise ValueError("Google Street View API key not found in .env file. Please ensure GOOGLE_STREET_VIEW_API_KEY is set in ComfyUI_StreetView-Loader/.env")
image_pil, metadata = fetch_streetview_image(
api_key=API_KEY_FROM_ENV,
location=location,
heading=heading,
pitch=pitch,
fov=fov,
width=width,
height=height
)
# Convert the returned PIL image to the tensor format ComfyUI expects.
image_tensor = self.pil_to_tensor(image_pil)
return (image_tensor, metadata)
def pil_to_tensor(self, image: Image.Image):
""" Helper function to convert a PIL Image to a PyTorch Tensor for ComfyUI. """
image_np = np.array(image).astype(np.float32) / 255.0
return torch.from_numpy(image_np)[None,]
+65
View File
@@ -0,0 +1,65 @@
import re
class StreetViewURLParser:
"""
A ComfyUI node that takes a Google Maps Street View URL and parses it
into its constituent parts: location, heading, pitch, and fov.
"""
@classmethod
def INPUT_TYPES(s):
return {
"required": {
"url": ("STRING", {
"multiline": True,
"default": "Paste a Google Maps Street View URL here"
}),
}
}
RETURN_TYPES = ("STRING", "FLOAT", "FLOAT", "INT")
RETURN_NAMES = ("location", "heading", "pitch", "fov")
FUNCTION = "parse_url"
CATEGORY = "Ru4ls/StreetView/Utils" # A sub-category for utilities
def parse_url(self, url):
# Default values in case parsing fails
location = "40.720032,-73.988354"
heading = 151.78
pitch = -0.76
fov = 90
if not url or "google.com/maps" not in url:
print("Invalid URL provided. Using default values.")
return (location, heading, pitch, fov)
# --- Use Regular Expressions to find the parameters ---
# 1. Parse Location (latitude,longitude)
# Looks for the @ symbol followed by two numbers separated by a comma
loc_match = re.search(r"@(-?\d+\.\d+),(-?\d+\.\d+)", url)
if loc_match:
lat = loc_match.group(1)
lon = loc_match.group(2)
location = f"{lat},{lon}"
# 2. Parse Heading, Pitch, and FOV
# Looks for the data block like ",3a,75y,273.99h,85.73t"
params_match = re.search(r",(\d+\.?\d*)y,(\d+\.?\d*)h,(\d+\.?\d*)t", url)
if params_match:
# FOV is the number before 'y'
fov = int(float(params_match.group(1)))
# Heading is the number before 'h'
heading = float(params_match.group(2))
# Pitch from URL needs conversion for the API
# URL pitch (t): 90 is horizontal.
# API pitch: 0 is horizontal.
# Conversion: API_pitch = URL_t - 90
url_pitch = float(params_match.group(3))
pitch = url_pitch - 90.0
print(f"Parsed URL: Location={location}, Heading={heading}, Pitch={pitch}, FOV={fov}")
return (location, heading, pitch, fov)
+2
View File
@@ -0,0 +1,2 @@
requests
python-dotenv
+51
View File
@@ -0,0 +1,51 @@
# file: ComfyUI_StreetView-Loader\utils\connect_api_utils.py
import requests
from PIL import Image
from io import BytesIO
def fetch_streetview_image(api_key, location, heading, pitch, fov, width, height):
"""
Connects to the Google Street View API and fetches an image.
Returns:
A tuple containing (PIL.Image, metadata_url_string) on success,
or (error_image, error_message_string) on failure.
"""
base_url = "https://maps.googleapis.com/maps/api/streetview"
params = {
"size": f"{width}x{height}",
"location": location,
"heading": heading,
"pitch": pitch,
"fov": fov,
"key": api_key,
"return_error_codes": "true"
}
try:
response = requests.get(base_url, params=params, timeout=20)
response.raise_for_status()
# Check for "ZERO_RESULTS" or other API errors which still return a 200 OK status.
# A valid JPEG starts with bytes FF D8. A valid PNG starts with 89 50 4E 47.
is_valid_image = response.content.startswith(b'\xff\xd8') or response.content.startswith(b'\x89PNG')
if not is_valid_image:
error_message = "API returned no image for this location. It might not be available."
print(f"StreetView Info: {error_message}")
error_img = Image.new('RGB', (width, height), color='black')
return (error_img, error_message)
# Success case
image_pil = Image.open(BytesIO(response.content)).convert("RGB")
metadata_url = requests.Request('GET', base_url, params=params).prepare().url
print(f"StreetView URL: {metadata_url}")
return (image_pil, metadata_url)
except requests.exceptions.RequestException as e:
error_message = f"An API request error occurred: {e}"
print(error_message)
error_img = Image.new('RGB', (width, height), color='black')
return (error_img, error_message)