admin管理员组

文章数量:1400077

Script output

Grayscale rivermask

My main problem is my script is still making parts of the river more than 1px wide, particularly junctions and what i guess would be curves of 1px radius. Here is the script:

from PIL import Image
import numpy as np
from skimage import morphology
from scipy.ndimage import label

# File paths (update these to match your files)
RIVER_MASK_PATH = "river_mask.png"  # e.g., "river_mask.png"
OUTPUT_PATH = "river_map_output.png"  # e.g., "river_map_output.png"

# Load river mask as grayscale
river_img = Image.open(RIVER_MASK_PATH).convert("I")  # 16-bit grayscale (0-65535)
river_array = np.array(river_img, dtype=np.uint16)

# For 8-bit grayscale, use this instead:
# river_img = Image.open(RIVER_MASK_PATH).convert("L")  # 8-bit grayscale (0-255)
# river_array = np.array(river_img, dtype=np.uint8)

# Threshold to create a binary mask (river = True, background = False)
river_binary = river_array > 0

# Skeletonize to make rivers 1px wide
river_skeleton = morphology.skeletonize(river_binary)

# Function to convert to 4-directional connectivity
def convert_to_4connected(skeleton):
    height, width = skeleton.shape
    river_4dir = skeleton.copy()
    for y in range(height - 1):
        for x in range(width - 1):
            if skeleton[y, x]:
                # Check for diagonal connections
                if skeleton[y + 1, x + 1] and not (skeleton[y + 1, x] or skeleton[y, x + 1]):
                    # Break diagonal by adding an intermediate pixel
                    river_4dir[y + 1, x] = True  # Add vertical connection
                if skeleton[y + 1, x - 1] and not (skeleton[y + 1, x] or skeleton[y, x - 1]):
                    river_4dir[y + 1, x] = True
    return river_4dir

# Apply 4-directional connectivity
river_4dir = convert_to_4connected(river_skeleton)

# Remove small isolated rivers (≤5 pixels)
labeled_rivers, _ = label(river_4dir)
river_sizes = np.bincount(labeled_rivers.ravel())
small_rivers = np.where((river_sizes <= 5) & (river_sizes > 0))[0]
for label_id in small_rivers:
    river_4dir[labeled_rivers == label_id] = False

# Define CK3 river colors and width thresholds
def get_river_color(value, max_value=255):
    if max_value == 255:  # 8-bit
        if value <= 85:
            return (0, 255, 255)  # Narrow: Cyan
        elif value <= 170:
            return (0, 128, 255)  # Medium: Medium blue
        else:
            return (0, 0, 255)    # Wide: Pure blue
    else:  # 16-bit
        if value <= 10542:
            return (0, 255, 255)
        elif value <= 41720:
            return (0, 128, 255)
        else:
            return (0, 0, 255)

# Create a 3-channel color image
height, width = river_4dir.shape
river_map_color = np.zeros((height, width, 3), dtype=np.uint8)

# Assign colors to river pixels
max_value = 255 if river_array.dtype == np.uint8 else 65535
for y in range(height):
    for x in range(width):
        if river_4dir[y, x]:
            value = river_array[y, x]
            river_map_color[y, x] = get_river_color(value, max_value)

# Save the output as an RGB image
output_img = Image.fromarray(river_map_color, mode="RGB")
output_img.save(OUTPUT_PATH)

print(f"River map saved to {OUTPUT_PATH}")

Here is the link to the wiki page talking about rivermaps:

I've been stuck trying to fix this for a week now using all the ais (chatgpt, deepseek and grok) but can't really fix this issue.

Edit: I'm sorry if the question is not clear, let me clarify, essentially I need the resulting image to abide by ck3 rivermap rules. Specifically the perfect pixel rule. If you check the resulting image you'll see that there are parts of the rivers that are more than 2 pixels wide, particularly junctions. In the wiki there is an image example of how a rivermap should look (Ireland and part of Great Britain). I'm not a programmer by any means and it's my first time posting here so yeah my bad for not explaining the question better.

本文标签: