This commit is contained in:
Saifeddine 2023-02-07 00:29:33 +01:00
commit 89f69c2d94
98 changed files with 2173 additions and 984 deletions

View File

@ -1,18 +1,23 @@
# use this file as a whitelist
*
!assets/caution.png
!backend
!frontend/dist
!invokeai
!ldm
!pyproject.toml
!README.md
!scripts
# Guard against pulling in any models that might exist in the directory tree
**.pt*
**/*.pt*
**/*.ckpt
# unignore configs, but only ignore the custom models.yaml, in case it exists
!configs
configs/models.yaml
configs/models.yaml.orig
# ignore frontend but whitelist dist
invokeai/frontend/**
!invokeai/frontend/dist
# ignore invokeai/assets but whitelist invokeai/assets/web
invokeai/assets
!invokeai/assets/web
# ignore python cache
**/__pycache__
**/*.py[cod]
**/*.egg-info

2
.github/CODEOWNERS vendored
View File

@ -4,4 +4,4 @@ scripts/legacy_api.py @CapableWeb
tests/legacy_tests.sh @CapableWeb
installer/ @ebr
.github/workflows/ @mauwii
docker_build/ @mauwii
docker/ @mauwii

View File

@ -1,88 +0,0 @@
name: Build and push cloud image
on:
workflow_dispatch:
# push:
# branches:
# - main
# tags:
# - v*
# # we will NOT push the image on pull requests, only test buildability.
# pull_request:
# branches:
# - main
permissions:
contents: read
packages: write
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
docker:
if: github.event.pull_request.draft == false
strategy:
fail-fast: false
matrix:
arch:
- x86_64
# requires resolving a patchmatch issue
# - aarch64
runs-on: ubuntu-latest
name: ${{ matrix.arch }}
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
if: matrix.arch == 'aarch64'
- name: Docker meta
id: meta
uses: docker/metadata-action@v4
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
# see https://github.com/docker/metadata-action
# will push the following tags:
# :edge
# :main (+ any other branches enabled in the workflow)
# :<tag>
# :1.2.3 (for semver tags)
# :1.2 (for semver tags)
# :<sha>
tags: |
type=edge,branch=main
type=ref,event=branch
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha
# suffix image tags with architecture
flavor: |
latest=auto
suffix=-${{ matrix.arch }},latest=true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
# do not login to container registry on PRs
- if: github.event_name != 'pull_request'
name: Docker login
uses: docker/login-action@v2
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push cloud image
uses: docker/build-push-action@v3
with:
context: .
file: docker-build/Dockerfile.cloud
platforms: Linux/${{ matrix.arch }}
# do not push the image on PRs
push: false
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}

View File

@ -3,6 +3,7 @@ on:
push:
branches:
- 'main'
- 'update/ci/*'
tags:
- 'v*.*.*'
@ -15,14 +16,19 @@ jobs:
flavor:
- amd
- cuda
- cpu
include:
- flavor: amd
pip-extra-index-url: 'https://download.pytorch.org/whl/rocm5.2'
dockerfile: docker-build/Dockerfile
dockerfile: docker/Dockerfile
platforms: linux/amd64,linux/arm64
- flavor: cuda
pip-extra-index-url: ''
dockerfile: docker-build/Dockerfile
dockerfile: docker/Dockerfile
platforms: linux/amd64,linux/arm64
- flavor: cpu
pip-extra-index-url: 'https://download.pytorch.org/whl/cpu'
dockerfile: docker/Dockerfile
platforms: linux/amd64,linux/arm64
runs-on: ubuntu-latest
name: ${{ matrix.flavor }}
@ -34,22 +40,25 @@ jobs:
id: meta
uses: docker/metadata-action@v4
with:
images: ghcr.io/${{ github.repository }}-${{ matrix.flavor }}
github-token: ${{ secrets.GITHUB_TOKEN }}
images: ghcr.io/${{ github.repository }}
tags: |
type=ref,event=branch
type=ref,event=tag
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
type=sha
type=sha,enable=true,prefix=sha-,format=short
flavor: |
latest=true
latest=${{ matrix.flavor == 'cuda' && github.ref == 'refs/heads/main' }}
suffix=-${{ matrix.flavor }},onlatest=false
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2
with:
platforms: ${{ matrix.platforms }}
- name: Login to GitHub Container Registry
if: github.event_name != 'pull_request'
@ -60,7 +69,7 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build container
uses: docker/build-push-action@v3
uses: docker/build-push-action@v4
with:
context: .
file: ${{ matrix.dockerfile }}
@ -69,5 +78,15 @@ jobs:
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
build-args: PIP_EXTRA_INDEX_URL=${{ matrix.pip-extra-index-url }}
# cache-from: type=gha
# cache-to: type=gha,mode=max
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Output image, digest and metadata to summary
run: |
{
echo imageid: "${{ steps.docker_build.outputs.imageid }}"
echo digest: "${{ steps.docker_build.outputs.digest }}"
echo labels: "${{ steps.meta.outputs.labels }}"
echo tags: "${{ steps.meta.outputs.tags }}"
echo version: "${{ steps.meta.outputs.version }}"
} >> "$GITHUB_STEP_SUMMARY"

41
.github/workflows/pypi-release.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: PyPI Release
on:
push:
paths:
- 'ldm/invoke/_version.py'
workflow_dispatch:
jobs:
release:
if: github.repository == 'invoke-ai/InvokeAI'
runs-on: ubuntu-22.04
env:
TWINE_USERNAME: __token__
TWINE_PASSWORD: ${{ secrets.PYPI_API_TOKEN }}
TWINE_NON_INTERACTIVE: 1
steps:
- name: checkout sources
uses: actions/checkout@v3
- name: install deps
run: pip install --upgrade build twine
- name: build package
run: python3 -m build
- name: check distribution
run: twine check dist/*
- name: check PyPI versions
if: github.ref == 'refs/heads/main'
run: |
pip install --upgrade requests
python -c "\
import scripts.pypi_helper; \
EXISTS=scripts.pypi_helper.local_on_pypi(); \
print(f'PACKAGE_EXISTS={EXISTS}')" >> $GITHUB_ENV
- name: upload package
if: env.PACKAGE_EXISTS == 'False' && env.TWINE_PASSWORD != ''
run: twine upload dist/*

View File

@ -8,10 +8,11 @@ on:
- 'ready_for_review'
- 'opened'
- 'synchronize'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true
jobs:
matrix:
@ -62,28 +63,13 @@ jobs:
# github-env: $env:GITHUB_ENV
name: ${{ matrix.pytorch }} on ${{ matrix.python-version }}
runs-on: ${{ matrix.os }}
env:
PIP_USE_PEP517: '1'
steps:
- name: Checkout sources
id: checkout-sources
uses: actions/checkout@v3
- name: setup python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Set Cache-Directory Windows
if: runner.os == 'Windows'
id: set-cache-dir-windows
run: |
echo "CACHE_DIR=$HOME\invokeai\models" >> ${{ matrix.github-env }}
echo "PIP_NO_CACHE_DIR=1" >> ${{ matrix.github-env }}
- name: Set Cache-Directory others
if: runner.os != 'Windows'
id: set-cache-dir-others
run: echo "CACHE_DIR=$HOME/invokeai/models" >> ${{ matrix.github-env }}
- name: set test prompt to main branch validation
if: ${{ github.ref == 'refs/heads/main' }}
run: echo "TEST_PROMPTS=tests/preflight_prompts.txt" >> ${{ matrix.github-env }}
@ -92,26 +78,29 @@ jobs:
if: ${{ github.ref != 'refs/heads/main' }}
run: echo "TEST_PROMPTS=tests/validate_pr_prompt.txt" >> ${{ matrix.github-env }}
- name: setup python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
cache: pip
cache-dependency-path: pyproject.toml
- name: install invokeai
env:
PIP_EXTRA_INDEX_URL: ${{ matrix.extra-index-url }}
run: >
pip3 install
--use-pep517
--editable=".[test]"
- name: run pytest
id: run-pytest
run: pytest
- name: Use Cached models
id: cache-sd-model
uses: actions/cache@v3
env:
cache-name: huggingface-models
with:
path: ${{ env.CACHE_DIR }}
key: ${{ env.cache-name }}
enableCrossOsArchive: true
- name: set INVOKEAI_OUTDIR
run: >
python -c
"import os;from ldm.invoke.globals import Globals;OUTDIR=os.path.join(Globals.root,str('outputs'));print(f'INVOKEAI_OUTDIR={OUTDIR}')"
>> ${{ matrix.github-env }}
- name: run invokeai-configure
id: run-preload-models
@ -124,9 +113,8 @@ jobs:
--full-precision
# can't use fp16 weights without a GPU
- name: Run the tests
if: runner.os != 'Windows'
id: run-tests
- name: run invokeai
id: run-invokeai
env:
# Set offline mode to make sure configure preloaded successfully.
HF_HUB_OFFLINE: 1
@ -137,10 +125,11 @@ jobs:
--no-patchmatch
--no-nsfw_checker
--from_file ${{ env.TEST_PROMPTS }}
--outdir ${{ env.INVOKEAI_OUTDIR }}/${{ matrix.python-version }}/${{ matrix.pytorch }}
- name: Archive results
id: archive-results
uses: actions/upload-artifact@v3
with:
name: results_${{ matrix.pytorch }}_${{ matrix.python-version }}
path: ${{ env.INVOKEAI_ROOT }}/outputs
name: results
path: ${{ env.INVOKEAI_OUTDIR }}

View File

@ -1,78 +0,0 @@
# syntax=docker/dockerfile:1
FROM python:3.9-slim AS python-base
# use bash
SHELL [ "/bin/bash", "-c" ]
# Install necesarry packages
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y \
--no-install-recommends \
libgl1-mesa-glx=20.3.* \
libglib2.0-0=2.66.* \
libopencv-dev=4.5.* \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
ARG APPDIR=/usr/src/app
ENV APPDIR ${APPDIR}
WORKDIR ${APPDIR}
FROM python-base AS builder
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y \
--no-install-recommends \
gcc=4:10.2.* \
python3-dev=3.9.* \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# copy sources
COPY --link . .
ARG PIP_EXTRA_INDEX_URL
ENV PIP_EXTRA_INDEX_URL ${PIP_EXTRA_INDEX_URL}
# install requirements
RUN python3 -m venv invokeai \
&& ${APPDIR}/invokeai/bin/pip \
install \
--no-cache-dir \
--use-pep517 \
.
FROM python-base AS runtime
# setup environment
COPY --link . .
COPY --from=builder ${APPDIR}/invokeai ${APPDIR}/invokeai
ENV PATH=${APPDIR}/invokeai/bin:$PATH
ENV INVOKEAI_ROOT=/data
ENV INVOKE_MODEL_RECONFIGURE="--yes --default_only"
# build patchmatch
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt-get update \
&& apt-get install -y \
--no-install-recommends \
build-essential=12.9 \
&& PYTHONDONTWRITEBYTECODE=1 \
python3 -c "from patchmatch import patch_match" \
&& apt-get remove -y \
--autoremove \
build-essential \
&& apt-get autoclean \
&& rm -rf /var/lib/apt/lists/*
# set Entrypoint and default CMD
ENTRYPOINT [ "invoke" ]
CMD [ "--web", "--host=0.0.0.0" ]
VOLUME [ "/data" ]

View File

@ -1,86 +0,0 @@
#######################
#### Builder stage ####
FROM library/ubuntu:22.04 AS builder
ARG DEBIAN_FRONTEND=noninteractive
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt update && apt-get install -y \
git \
libglib2.0-0 \
libgl1-mesa-glx \
python3-venv \
python3-pip \
build-essential \
python3-opencv \
libopencv-dev
# This is needed for patchmatch support
RUN cd /usr/lib/x86_64-linux-gnu/pkgconfig/ &&\
ln -sf opencv4.pc opencv.pc
ARG WORKDIR=/invokeai
WORKDIR ${WORKDIR}
ENV VIRTUAL_ENV=${WORKDIR}/.venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
RUN --mount=type=cache,target=/root/.cache/pip \
python3 -m venv ${VIRTUAL_ENV} &&\
pip install --extra-index-url https://download.pytorch.org/whl/cu116 \
torch==1.12.0+cu116 \
torchvision==0.13.0+cu116 &&\
pip install -e git+https://github.com/invoke-ai/PyPatchMatch@0.1.3#egg=pypatchmatch
COPY . .
RUN --mount=type=cache,target=/root/.cache/pip \
cp environments-and-requirements/requirements-lin-cuda.txt requirements.txt && \
pip install -r requirements.txt &&\
pip install -e .
#######################
#### Runtime stage ####
FROM library/ubuntu:22.04 as runtime
ARG DEBIAN_FRONTEND=noninteractive
ENV PYTHONUNBUFFERED=1
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
--mount=type=cache,target=/var/lib/apt,sharing=locked \
apt update && apt install -y --no-install-recommends \
git \
curl \
ncdu \
iotop \
bzip2 \
libglib2.0-0 \
libgl1-mesa-glx \
python3-venv \
python3-pip \
build-essential \
python3-opencv \
libopencv-dev &&\
apt-get clean && apt-get autoclean
ARG WORKDIR=/invokeai
WORKDIR ${WORKDIR}
ENV INVOKEAI_ROOT=/mnt/invokeai
ENV VIRTUAL_ENV=${WORKDIR}/.venv
ENV PATH="$VIRTUAL_ENV/bin:$PATH"
COPY --from=builder ${WORKDIR} ${WORKDIR}
COPY --from=builder /usr/lib/x86_64-linux-gnu/pkgconfig /usr/lib/x86_64-linux-gnu/pkgconfig
# build patchmatch
RUN python -c "from patchmatch import patch_match"
## workaround for non-existent initfile when runtime directory is mounted; see #1613
RUN touch /root/.invokeai
ENTRYPOINT ["bash"]
CMD ["-c", "python3 scripts/invoke.py --web --host 0.0.0.0"]

View File

@ -1,44 +0,0 @@
# Directory in the container where the INVOKEAI_ROOT (runtime dir) will be mounted
INVOKEAI_ROOT=/mnt/invokeai
# Host directory to contain the runtime dir. Will be mounted at INVOKEAI_ROOT path in the container
HOST_MOUNT_PATH=${HOME}/invokeai
IMAGE=local/invokeai:latest
USER=$(shell id -u)
GROUP=$(shell id -g)
# All downloaded models, config, etc will end up in ${HOST_MOUNT_PATH} on the host.
# This is consistent with the expected non-Docker behaviour.
# Contents can be moved to a persistent storage and used to prime the cache on another host.
build:
DOCKER_BUILDKIT=1 docker build -t local/invokeai:latest -f Dockerfile.cloud ..
configure:
docker run --rm -it --runtime=nvidia --gpus=all \
-v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} \
-e INVOKEAI_ROOT=${INVOKEAI_ROOT} \
${IMAGE} -c "python scripts/configure_invokeai.py"
# Run the container with the runtime dir mounted and the web server exposed on port 9090
web:
docker run --rm -it --runtime=nvidia --gpus=all \
-v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} \
-e INVOKEAI_ROOT=${INVOKEAI_ROOT} \
-p 9090:9090 \
${IMAGE} -c "python scripts/invoke.py --web --host 0.0.0.0"
# Run the cli with the runtime dir mounted
cli:
docker run --rm -it --runtime=nvidia --gpus=all \
-v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} \
-e INVOKEAI_ROOT=${INVOKEAI_ROOT} \
${IMAGE} -c "python scripts/invoke.py"
# Run the container with the runtime dir mounted and open a bash shell
shell:
docker run --rm -it --runtime=nvidia --gpus=all \
-v ${HOST_MOUNT_PATH}:${INVOKEAI_ROOT} ${IMAGE} --
.PHONY: build configure web cli shell

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
# Variables shared by build.sh and run.sh
REPOSITORY_NAME=${REPOSITORY_NAME:-$(basename "$(git rev-parse --show-toplevel)")}
VOLUMENAME=${VOLUMENAME:-${REPOSITORY_NAME,,}_data}
ARCH=${ARCH:-$(uname -m)}
PLATFORM=${PLATFORM:-Linux/${ARCH}}
CONTAINER_FLAVOR=${CONTAINER_FLAVOR:-cuda}
INVOKEAI_BRANCH=$(git branch --show)
INVOKEAI_TAG=${REPOSITORY_NAME,,}-${CONTAINER_FLAVOR}:${INVOKEAI_TAG:-${INVOKEAI_BRANCH##*/}}

86
docker/Dockerfile Normal file
View File

@ -0,0 +1,86 @@
# syntax=docker/dockerfile:1
ARG PYTHON_VERSION=3.9
##################
## base image ##
##################
FROM python:${PYTHON_VERSION}-slim AS python-base
# prepare for buildkit cache
RUN rm -f /etc/apt/apt.conf.d/docker-clean
# Install necesarry packages
RUN \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
apt-get update \
&& apt-get install \
-yqq \
--no-install-recommends \
libgl1-mesa-glx=20.3.* \
libglib2.0-0=2.66.* \
libopencv-dev=4.5.* \
&& rm -rf /var/lib/apt/lists/*
# set working directory and path
ARG APPDIR=/usr/src
ARG APPNAME=InvokeAI
WORKDIR ${APPDIR}
ENV PATH=${APPDIR}/${APPNAME}/bin:$PATH
#######################
## build pyproject ##
#######################
FROM python-base AS pyproject-builder
ENV PIP_USE_PEP517=1
# prepare for buildkit cache
ARG PIP_CACHE_DIR=/var/cache/buildkit/pip
ENV PIP_CACHE_DIR ${PIP_CACHE_DIR}
RUN mkdir -p ${PIP_CACHE_DIR}
# Install dependencies
RUN \
--mount=type=cache,target=${PIP_CACHE_DIR} \
--mount=type=cache,target=/var/cache/apt,sharing=locked \
apt-get update \
&& apt-get install \
-yqq \
--no-install-recommends \
build-essential=12.9 \
gcc=4:10.2.* \
python3-dev=3.9.* \
&& rm -rf /var/lib/apt/lists/*
# create virtual environment
RUN --mount=type=cache,target=${PIP_CACHE_DIR} \
python3 -m venv "${APPNAME}" \
--upgrade-deps
# copy sources
COPY --link . .
# install pyproject.toml
ARG PIP_EXTRA_INDEX_URL
ENV PIP_EXTRA_INDEX_URL ${PIP_EXTRA_INDEX_URL}
ARG PIP_PACKAGE=.
RUN --mount=type=cache,target=${PIP_CACHE_DIR} \
"${APPDIR}/${APPNAME}/bin/pip" install ${PIP_PACKAGE}
# build patchmatch
RUN python3 -c "from patchmatch import patch_match"
#####################
## runtime image ##
#####################
FROM python-base AS runtime
# setup environment
COPY --from=pyproject-builder --link ${APPDIR}/${APPNAME} ${APPDIR}/${APPNAME}
ENV INVOKEAI_ROOT=/data
ENV INVOKE_MODEL_RECONFIGURE="--yes --default_only"
# set Entrypoint and default CMD
ENTRYPOINT [ "invokeai" ]
CMD [ "--web", "--host=0.0.0.0" ]
VOLUME [ "/data" ]
LABEL org.opencontainers.image.authors="mauwii@outlook.de"

View File

@ -2,30 +2,31 @@
set -e
# How to use: https://invoke-ai.github.io/InvokeAI/installation/INSTALL_DOCKER/#setup
#
# Some possible pip extra-index urls (cuda 11.7 is available without extra url):
#
# CUDA 11.6: https://download.pytorch.org/whl/cu116
# ROCm 5.2: https://download.pytorch.org/whl/rocm5.2
# CPU: https://download.pytorch.org/whl/cpu
#
# as found on https://pytorch.org/get-started/locally/
cd "$(dirname "$0")" || exit 1
SCRIPTDIR=$(dirname "$0")
cd "$SCRIPTDIR" || exit 1
source ./env.sh
DOCKERFILE=${INVOKE_DOCKERFILE:-"./Dockerfile"}
DOCKERFILE=${INVOKE_DOCKERFILE:-Dockerfile}
# print the settings
echo -e "You are using these values:\n"
echo -e "Dockerfile:\t ${DOCKERFILE}"
echo -e "extra-index-url: ${PIP_EXTRA_INDEX_URL:-none}"
echo -e "Volumename:\t ${VOLUMENAME}"
echo -e "arch:\t\t ${ARCH}"
echo -e "Platform:\t ${PLATFORM}"
echo -e "Invokeai_tag:\t ${INVOKEAI_TAG}\n"
echo -e "Dockerfile:\t\t${DOCKERFILE}"
echo -e "index-url:\t\t${PIP_EXTRA_INDEX_URL:-none}"
echo -e "Volumename:\t\t${VOLUMENAME}"
echo -e "Platform:\t\t${PLATFORM}"
echo -e "Registry:\t\t${CONTAINER_REGISTRY}"
echo -e "Repository:\t\t${CONTAINER_REPOSITORY}"
echo -e "Container Tag:\t\t${CONTAINER_TAG}"
echo -e "Container Image:\t${CONTAINER_IMAGE}\n"
# Create docker volume
if [[ -n "$(docker volume ls -f name="${VOLUMENAME}" -q)" ]]; then
echo -e "Volume already exists\n"
else
@ -34,9 +35,10 @@ else
fi
# Build Container
docker build \
DOCKER_BUILDKIT=1 docker build \
--platform="${PLATFORM}" \
--tag="${INVOKEAI_TAG}" \
${PIP_EXTRA_INDEX_URL:+--build-arg=PIP_EXTRA_INDEX_URL="${PIP_EXTRA_INDEX_URL}"} \
--tag="${CONTAINER_IMAGE}" \
${PIP_EXTRA_INDEX_URL:+--build-arg="PIP_EXTRA_INDEX_URL=${PIP_EXTRA_INDEX_URL}"} \
${PIP_PACKAGE:+--build-arg="PIP_PACKAGE=${PIP_PACKAGE}"} \
--file="${DOCKERFILE}" \
..

38
docker/env.sh Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
if [[ -z "$PIP_EXTRA_INDEX_URL" ]]; then
# Decide which container flavor to build if not specified
if [[ -z "$CONTAINER_FLAVOR" ]] && python -c "import torch" &>/dev/null; then
# Check for CUDA and ROCm
CUDA_AVAILABLE=$(python -c "import torch;print(torch.cuda.is_available())")
ROCM_AVAILABLE=$(python -c "import torch;print(torch.version.hip is not None)")
if [[ "$(uname -s)" != "Darwin" && "${CUDA_AVAILABLE}" == "True" ]]; then
CONTAINER_FLAVOR="cuda"
elif [[ "$(uname -s)" != "Darwin" && "${ROCM_AVAILABLE}" == "True" ]]; then
CONTAINER_FLAVOR="rocm"
else
CONTAINER_FLAVOR="cpu"
fi
fi
# Set PIP_EXTRA_INDEX_URL based on container flavor
if [[ "$CONTAINER_FLAVOR" == "rocm" ]]; then
PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/rocm"
elif [[ "$CONTAINER_FLAVOR" == "cpu" ]]; then
PIP_EXTRA_INDEX_URL="https://download.pytorch.org/whl/cpu"
# elif [[ -z "$CONTAINER_FLAVOR" || "$CONTAINER_FLAVOR" == "cuda" ]]; then
# PIP_PACKAGE=${PIP_PACKAGE-".[xformers]"}
fi
fi
# Variables shared by build.sh and run.sh
REPOSITORY_NAME="${REPOSITORY_NAME-$(basename "$(git rev-parse --show-toplevel)")}"
VOLUMENAME="${VOLUMENAME-"${REPOSITORY_NAME,,}_data"}"
ARCH="${ARCH-$(uname -m)}"
PLATFORM="${PLATFORM-Linux/${ARCH}}"
INVOKEAI_BRANCH="${INVOKEAI_BRANCH-$(git branch --show)}"
CONTAINER_REGISTRY="${CONTAINER_REGISTRY-"ghcr.io"}"
CONTAINER_REPOSITORY="${CONTAINER_REPOSITORY-"$(whoami)/${REPOSITORY_NAME}"}"
CONTAINER_FLAVOR="${CONTAINER_FLAVOR-cuda}"
CONTAINER_TAG="${CONTAINER_TAG-"${INVOKEAI_BRANCH##*/}-${CONTAINER_FLAVOR}"}"
CONTAINER_IMAGE="${CONTAINER_REGISTRY}/${CONTAINER_REPOSITORY}:${CONTAINER_TAG}"
CONTAINER_IMAGE="${CONTAINER_IMAGE,,}"

View File

@ -4,27 +4,28 @@ set -e
# How to use: https://invoke-ai.github.io/InvokeAI/installation/INSTALL_DOCKER/#run-the-container
# IMPORTANT: You need to have a token on huggingface.co to be able to download the checkpoints!!!
cd "$(dirname "$0")" || exit 1
SCRIPTDIR=$(dirname "$0")
cd "$SCRIPTDIR" || exit 1
source ./env.sh
echo -e "You are using these values:\n"
echo -e "Volumename:\t${VOLUMENAME}"
echo -e "Invokeai_tag:\t${INVOKEAI_TAG}"
echo -e "Invokeai_tag:\t${CONTAINER_IMAGE}"
echo -e "local Models:\t${MODELSPATH:-unset}\n"
docker run \
--interactive \
--tty \
--rm \
--platform="$PLATFORM" \
--platform="${PLATFORM}" \
--name="${REPOSITORY_NAME,,}" \
--hostname="${REPOSITORY_NAME,,}" \
--mount=source="$VOLUMENAME",target=/data \
--mount=source="${VOLUMENAME}",target=/data \
${MODELSPATH:+-u "$(id -u):$(id -g)"} \
${MODELSPATH:+--mount=type=bind,source=${MODELSPATH},target=/data/models} \
${HUGGING_FACE_HUB_TOKEN:+--env=HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN}} \
${MODELSPATH:+--mount="type=bind,source=${MODELSPATH},target=/data/models"} \
${HUGGING_FACE_HUB_TOKEN:+--env="HUGGING_FACE_HUB_TOKEN=${HUGGING_FACE_HUB_TOKEN}"} \
--publish=9090:9090 \
--cap-add=sys_nice \
${GPU_FLAGS:+--gpus=${GPU_FLAGS}} \
"$INVOKEAI_TAG" ${1:+$@}
${GPU_FLAGS:+--gpus="${GPU_FLAGS}"} \
"${CONTAINER_IMAGE}" ${1:+$@}

View File

@ -16,10 +16,6 @@ title: Installing with Docker
For general use, install locally to leverage your machine's GPU.
!!! tip "For running on a cloud instance/service"
Check out the [Running InvokeAI in the cloud with Docker](#running-invokeai-in-the-cloud-with-docker) section below
## Why containers?
They provide a flexible, reliable way to build and deploy InvokeAI. You'll also
@ -78,38 +74,40 @@ Some Suggestions of variables you may want to change besides the Token:
<figure markdown>
| Environment-Variable | Default value | Description |
| -------------------- | ----------------------------- | -------------------------------------------------------------------------------------------- |
| `HUGGINGFACE_TOKEN` | No default, but **required**! | This is the only **required** variable, without it you can't download the huggingface models |
| `REPOSITORY_NAME` | The Basename of the Repo folder | This name will used as the container repository/image name |
| `VOLUMENAME` | `${REPOSITORY_NAME,,}_data` | Name of the Docker Volume where model files will be stored |
| `ARCH` | arch of the build machine | can be changed if you want to build the image for another arch |
| `INVOKEAI_TAG` | latest | the Container Repository / Tag which will be used |
| `PIP_REQUIREMENTS` | `requirements-lin-cuda.txt` | the requirements file to use (from `environments-and-requirements`) |
| `CONTAINER_FLAVOR` | cuda | the flavor of the image, which can be changed if you build f.e. with amd requirements file. |
| `INVOKE_DOCKERFILE` | `docker-build/Dockerfile` | the Dockerfile which should be built, handy for development |
| Environment-Variable <img width="220" align="right"/> | Default value <img width="360" align="right"/> | Description |
| ----------------------------------------------------- | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `HUGGING_FACE_HUB_TOKEN` | No default, but **required**! | This is the only **required** variable, without it you can't download the huggingface models |
| `REPOSITORY_NAME` | The Basename of the Repo folder | This name will used as the container repository/image name |
| `VOLUMENAME` | `${REPOSITORY_NAME,,}_data` | Name of the Docker Volume where model files will be stored |
| `ARCH` | arch of the build machine | Can be changed if you want to build the image for another arch |
| `CONTAINER_REGISTRY` | ghcr.io | Name of the Container Registry to use for the full tag |
| `CONTAINER_REPOSITORY` | `$(whoami)/${REPOSITORY_NAME}` | Name of the Container Repository |
| `CONTAINER_FLAVOR` | `cuda` | The flavor of the image to built, available options are `cuda`, `rocm` and `cpu`. If you choose `rocm` or `cpu`, the extra-index-url will be selected automatically, unless you set one yourself. |
| `CONTAINER_TAG` | `${INVOKEAI_BRANCH##*/}-${CONTAINER_FLAVOR}` | The Container Repository / Tag which will be used |
| `INVOKE_DOCKERFILE` | `Dockerfile` | The Dockerfile which should be built, handy for development |
| `PIP_EXTRA_INDEX_URL` | | If you want to use a custom pip-extra-index-url |
</figure>
#### Build the Image
I provided a build script, which is located in `docker-build/build.sh` but still
needs to be executed from the Repository root.
I provided a build script, which is located next to the Dockerfile in
`docker/build.sh`. It can be executed from repository root like this:
```bash
./docker-build/build.sh
./docker/build.sh
```
The build Script not only builds the container, but also creates the docker
volume if not existing yet, or if empty it will just download the models.
volume if not existing yet.
#### Run the Container
After the build process is done, you can run the container via the provided
`docker-build/run.sh` script
`docker/run.sh` script
```bash
./docker-build/run.sh
./docker/run.sh
```
When used without arguments, the container will start the webserver and provide
@ -119,7 +117,7 @@ also do so.
!!! example "run script example"
```bash
./docker-build/run.sh "banana sushi" -Ak_lms -S42 -s10
./docker/run.sh "banana sushi" -Ak_lms -S42 -s10
```
This would generate the legendary "banana sushi" with Seed 42, k_lms Sampler and 10 steps.
@ -130,16 +128,18 @@ also do so.
## Running the container on your GPU
If you have an Nvidia GPU, you can enable InvokeAI to run on the GPU by running the container with an extra
environment variable to enable GPU usage and have the process run much faster:
If you have an Nvidia GPU, you can enable InvokeAI to run on the GPU by running
the container with an extra environment variable to enable GPU usage and have
the process run much faster:
```bash
GPU_FLAGS=all ./docker-build/run.sh
GPU_FLAGS=all ./docker/run.sh
```
This passes the `--gpus all` to docker and uses the GPU.
If you don't have a GPU (or your host is not yet setup to use it) you will see a message like this:
If you don't have a GPU (or your host is not yet setup to use it) you will see a
message like this:
`docker: Error response from daemon: could not select device driver "" with capabilities: [[gpu]].`
@ -147,84 +147,8 @@ You can use the full set of GPU combinations documented here:
https://docs.docker.com/config/containers/resource_constraints/#gpu
For example, use `GPU_FLAGS=device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a` to choose a specific device identified by a UUID.
## Running InvokeAI in the cloud with Docker
We offer an optimized Ubuntu-based image that has been well-tested in cloud deployments. Note: it also works well locally on Linux x86_64 systems with an Nvidia GPU. It *may* also work on Windows under WSL2 and on Intel Mac (not tested).
An advantage of this method is that it does not need any local setup or additional dependencies.
See the `docker-build/Dockerfile.cloud` file to familizarize yourself with the image's content.
### Prerequisites
- a `docker` runtime
- `make` (optional but helps for convenience)
- Huggingface token to download models, or an existing InvokeAI runtime directory from a previous installation
Neither local Python nor any dependencies are required. If you don't have `make` (part of `build-essentials` on Ubuntu), or do not wish to install it, the commands from the `docker-build/Makefile` are readily adaptable to be executed directly.
### Building and running the image locally
1. Clone this repo and `cd docker-build`
1. `make build` - this will build the image. (This does *not* require a GPU-capable system).
1. _(skip this step if you already have a complete InvokeAI runtime directory)_
- `make configure` (This does *not* require a GPU-capable system)
- this will create a local cache of models and configs (a.k.a the _runtime dir_)
- enter your Huggingface token when prompted
1. `make web`
1. Open the `http://localhost:9090` URL in your browser, and enjoy the banana sushi!
To use InvokeAI on the cli, run `make cli`. To open a Bash shell in the container for arbitraty advanced use, `make shell`.
#### Building and running without `make`
(Feel free to adapt paths such as `${HOME}/invokeai` to your liking, and modify the CLI arguments as necessary).
!!! example "Build the image and configure the runtime directory"
```Shell
cd docker-build
DOCKER_BUILDKIT=1 docker build -t local/invokeai:latest -f Dockerfile.cloud ..
docker run --rm -it -v ${HOME}/invokeai:/mnt/invokeai local/invokeai:latest -c "python scripts/configure_invokeai.py"
```
!!! example "Run the web server"
```Shell
docker run --runtime=nvidia --gpus=all --rm -it -v ${HOME}/invokeai:/mnt/invokeai -p9090:9090 local/invokeai:latest
```
Access the Web UI at http://localhost:9090
!!! example "Run the InvokeAI interactive CLI"
```
docker run --runtime=nvidia --gpus=all --rm -it -v ${HOME}/invokeai:/mnt/invokeai local/invokeai:latest -c "python scripts/invoke.py"
```
### Running the image in the cloud
This image works anywhere you can run a container with a mounted Docker volume. You may either build this image on a cloud instance, or build and push it to your Docker registry. To manually run this on a cloud instance (such as AWS EC2, GCP or Azure VM):
1. build this image either in the cloud (you'll need to pull the repo), or locally
1. `docker tag` it as `your-registry/invokeai` and push to your registry (i.e. Dockerhub)
1. `docker pull` it on your cloud instance
1. configure the runtime directory as per above example, using `docker run ... configure_invokeai.py` script
1. use either one of the `docker run` commands above, substituting the image name for your own image.
To run this on Runpod, please refer to the following Runpod template: https://www.runpod.io/console/gpu-secure-cloud?template=vm19ukkycf (you need a Runpod subscription). When launching the template, feel free to set the image to pull your own build.
The template's `README` provides ample detail, but at a high level, the process is as follows:
1. create a pod using this Docker image
1. ensure the pod has an `INVOKEAI_ROOT=<path_to_your_persistent_volume>` environment variable, and that it corresponds to the path to your pod's persistent volume mount
1. Run the pod with `sleep infinity` as the Docker command
1. Use Runpod basic SSH to connect to the pod, and run `python scripts/configure_invokeai.py` script
1. Stop the pod, and change the Docker command to `python scripts/invoke.py --web --host 0.0.0.0`
1. Run the pod again, connect to your pod on HTTP port 9090, and enjoy the banana sushi!
Running on other cloud providers such as Vast.ai will likely work in a similar fashion.
For example, use `GPU_FLAGS=device=GPU-3a23c669-1f69-c64e-cf85-44e9b07e7a2a` to
choose a specific device identified by a UUID.
---
@ -240,13 +164,12 @@ Running on other cloud providers such as Vast.ai will likely work in a similar f
If you're on a **Linux container** the `invoke` script is **automatically
started** and the output dir set to the Docker volume you created earlier.
If you're **directly on macOS follow these startup instructions**.
With the Conda environment activated (`conda activate ldm`), run the interactive
If you're **directly on macOS follow these startup instructions**. With the
Conda environment activated (`conda activate ldm`), run the interactive
interface that combines the functionality of the original scripts `txt2img` and
`img2img`:
Use the more accurate but VRAM-intensive full precision math because
half-precision requires autocast and won't work.
By default the images are saved in `outputs/img-samples/`.
`img2img`: Use the more accurate but VRAM-intensive full precision math because
half-precision requires autocast and won't work. By default the images are saved
in `outputs/img-samples/`.
```Shell
python3 scripts/invoke.py --full_precision
@ -262,9 +185,9 @@ invoke> q
### Text to Image
For quick (but bad) image results test with 5 steps (default 50) and 1 sample
image. This will let you know that everything is set up correctly.
Then increase steps to 100 or more for good (but slower) results.
The prompt can be in quotes or not.
image. This will let you know that everything is set up correctly. Then increase
steps to 100 or more for good (but slower) results. The prompt can be in quotes
or not.
```Shell
invoke> The hulk fighting with sheldon cooper -s5 -n1
@ -277,10 +200,9 @@ You'll need to experiment to see if face restoration is making it better or
worse for your specific prompt.
If you're on a container the output is set to the Docker volume. You can copy it
wherever you want.
You can download it from the Docker Desktop app, Volumes, my-vol, data.
Or you can copy it from your Mac terminal. Keep in mind `docker cp` can't expand
`*.png` so you'll need to specify the image file name.
wherever you want. You can download it from the Docker Desktop app, Volumes,
my-vol, data. Or you can copy it from your Mac terminal. Keep in mind
`docker cp` can't expand `*.png` so you'll need to specify the image file name.
On your host Mac (you can use the name of any container that mounted the
volume):

8
installer/create_installer.sh Executable file → Normal file
View File

@ -14,12 +14,13 @@ fi
VERSION=$(cd ..; python -c "from ldm.invoke import __version__ as version; print(version)")
PATCH=""
VERSION="v${VERSION}${PATCH}"
LATEST_TAG="v2.3-latest"
echo Building installer for version $VERSION
echo "Be certain that you're in the 'installer' directory before continuing."
read -p "Press any key to continue, or CTRL-C to exit..."
read -e -p "Commit and tag this repo with ${VERSION} and 'latest'? [n]: " input
read -e -p "Commit and tag this repo with '${VERSION}' and '${LATEST_TAG}'? [n]: " input
RESPONSE=${input:='n'}
if [ "$RESPONSE" == 'y' ]; then
git commit -a
@ -28,8 +29,9 @@ if [ "$RESPONSE" == 'y' ]; then
echo "Existing/invalid tag"
exit -1
fi
git push origin :refs/tags/latest
git tag -fa latest
git push origin :refs/tags/$LATEST_TAG
git tag -fa $LATEST_TAG
fi
# ----------------------

25
installer/install.sh.in Normal file → Executable file
View File

@ -3,5 +3,28 @@
# make sure we are not already in a venv
# (don't need to check status)
deactivate >/dev/null 2>&1
scriptdir=$(dirname "$0")
cd $scriptdir
exec python3 $(dirname $0)/main.py ${@}
function version { echo "$@" | awk -F. '{ printf("%d%03d%03d%03d\n", $1,$2,$3,$4); }'; }
MINIMUM_PYTHON_VERSION=3.9.0
PYTHON=""
for candidate in python3.10 python3.9 python3 python python3.11 ; do
if ppath=`which $candidate`; then
python_version=$($ppath -V | awk '{ print $2 }')
if [ $(version $python_version) -ge $(version "$MINIMUM_PYTHON_VERSION") ]; then
PYTHON=$ppath
break
fi
fi
done
if [ -z "$PYTHON" ]; then
echo "A suitable Python interpreter could not be found"
echo "Please install Python 3.9 or higher before running this script. See instructions at $INSTRUCTIONS for help."
read -p "Press any key to exit"
exit -1
fi
exec $PYTHON ./main.py ${@}

View File

@ -38,7 +38,8 @@ class Installer:
self.reqs = INSTALLER_REQS
self.preflight()
if os.getenv("VIRTUAL_ENV") is not None:
raise NotImplementedError("A virtual environment is already activated. Please 'deactivate' before installation.")
print("A virtual environment is already activated. Please 'deactivate' before installation.")
sys.exit(-1)
self.bootstrap()
def preflight(self) -> None:
@ -248,6 +249,7 @@ class InvokeAiInstance:
"--require-virtualenv",
"torch",
"torchvision",
"--force-reinstall",
"--find-links" if find_links is not None else None,
find_links,
"--extra-index-url" if extra_index_url is not None else None,
@ -283,7 +285,7 @@ class InvokeAiInstance:
if FF_USE_LOCAL_WHEEL:
# if no wheel, try to do a source install before giving up
try:
src = str(next(Path.cwd().glob("InvokeAI-*.whl")))
src = str(next(Path(__file__).parent.glob("InvokeAI-*.whl")))
except StopIteration:
try:
src = Path(__file__).parents[1].expanduser().resolve()
@ -324,6 +326,7 @@ class InvokeAiInstance:
Configure the InvokeAI runtime directory
"""
# set sys.argv to a consistent state
new_argv = [sys.argv[0]]
for i in range(1,len(sys.argv)):
el = sys.argv[i]
@ -338,15 +341,12 @@ class InvokeAiInstance:
introduction()
from ldm.invoke.config import configure_invokeai
from ldm.invoke.config import invokeai_configure
# NOTE: currently the config script does its own arg parsing! this means the command-line switches
# from the installer will also automatically propagate down to the config script.
# this may change in the future with config refactoring!
# set sys.argv to a consistent state
configure_invokeai.main()
invokeai_configure.main()
def install_user_scripts(self):
"""
@ -355,7 +355,10 @@ class InvokeAiInstance:
ext = "bat" if OS == "Windows" else "sh"
for script in ["invoke", "update"]:
#scripts = ['invoke', 'update']
scripts = ['invoke']
for script in scripts:
src = Path(__file__).parent / "templates" / f"{script}.{ext}.in"
dest = self.runtime / f"{script}.{ext}"
shutil.copy(src, dest)
@ -443,6 +446,7 @@ def get_torch_source() -> (Union[str, None],str):
url = "https://download.pytorch.org/whl/cpu"
if device == 'cuda':
url = 'https://download.pytorch.org/whl/cu117'
optional_modules = '[xformers]'
# in all other cases, Torch wheels should be coming from PyPi as of Torch 1.13

View File

@ -13,7 +13,7 @@ echo 3. run textual inversion training
echo 4. merge models (diffusers type only)
echo 5. re-run the configure script to download new models
echo 6. open the developer console
set /P restore="Please enter 1, 2, 3, 4 or 5: [5] "
set /P restore="Please enter 1, 2, 3, 4 or 5: [2] "
if not defined restore set restore=2
IF /I "%restore%" == "1" (
echo Starting the InvokeAI command-line..

View File

@ -47,11 +47,11 @@ if [ "$0" != "bash" ]; then
;;
3)
echo "Starting Textual Inversion:"
exec textual_inversion --gui $@
exec invokeai-ti --gui $@
;;
4)
echo "Merging Models:"
exec merge_models --gui $@
exec invokeai-merge --gui $@
;;
5)
echo "Developer Console:"

View File

@ -626,9 +626,10 @@ class InvokeAIWebServer:
printable_parameters["init_mask"][:64] + "..."
)
print(
f">> Image generation requested: {printable_parameters}\nESRGAN parameters: {esrgan_parameters}\nFacetool parameters: {facetool_parameters}"
)
print(f'\n>> Image Generation Parameters:\n\n{printable_parameters}\n')
print(f'>> ESRGAN Parameters: {esrgan_parameters}')
print(f'>> Facetool Parameters: {facetool_parameters}')
self.generate_images(
generation_parameters,
esrgan_parameters,
@ -1154,7 +1155,7 @@ class InvokeAIWebServer:
image, os.path.basename(path), self.thumbnail_image_path
)
print(f'>> Image generated: "{path}"')
print(f'\n\n>> Image generated: "{path}"\n')
self.write_log_message(f'[Generated] "{path}": {command}')
if progress.total_iterations > progress.current_iteration:
@ -1193,8 +1194,6 @@ class InvokeAIWebServer:
progress.set_current_iteration(progress.current_iteration + 1)
print(generation_parameters)
def diffusers_step_callback_adapter(*cb_args, **kwargs):
if isinstance(cb_args[0], PipelineIntermediateState):
progress_state: PipelineIntermediateState = cb_args[0]
@ -1209,12 +1208,18 @@ class InvokeAIWebServer:
)
except KeyboardInterrupt:
# Clear the CUDA cache on an exception
self.empty_cuda_cache()
self.socketio.emit("processingCanceled")
raise
except CanceledException:
# Clear the CUDA cache on an exception
self.empty_cuda_cache()
self.socketio.emit("processingCanceled")
pass
except Exception as e:
# Clear the CUDA cache on an exception
self.empty_cuda_cache()
print(e)
self.socketio.emit("error", {"message": (str(e))})
print("\n")
@ -1222,6 +1227,12 @@ class InvokeAIWebServer:
traceback.print_exc()
print("\n")
def empty_cuda_cache(self):
if self.generate.device.type == "cuda":
import torch.cuda
torch.cuda.empty_cache()
def parameters_to_generated_image_metadata(self, parameters):
try:
# top-level metadata minus `image` or `images`
@ -1305,8 +1316,6 @@ class InvokeAIWebServer:
rfc_dict["variations"] = variations
print(parameters)
if rfc_dict["type"] == "img2img":
rfc_dict["strength"] = parameters["strength"]
rfc_dict["fit"] = parameters["fit"] # TODO: Noncompliant

View File

@ -1,28 +1,20 @@
# Stable Diffusion Web UI
# InvokeAI UI dev setup
## Run
The UI is in `invokeai/frontend`.
- `python scripts/dream.py --web` serves both frontend and backend at
http://localhost:9090
## Environment set up
## Evironment
Install [node](https://nodejs.org/en/download/) (includes npm) and optionally
Install [node](https://nodejs.org/en/download/) (includes npm) and
[yarn](https://yarnpkg.com/getting-started/install).
From `frontend/` run `npm install` / `yarn install` to install the frontend
packages.
From `invokeai/frontend/` run `yarn install` to get everything set up.
## Dev
1. From `frontend/`, run `npm dev` / `yarn dev` to start the dev server.
2. Run `python scripts/dream.py --web`.
3. Navigate to the dev server address e.g. `http://localhost:5173/`.
1. Start the dev server: `yarn dev`
2. Start the InvokeAI UI per usual: `invokeai --web`
3. Point your browser to the dev server address e.g. `http://localhost:5173/`
To build for dev: `npm build-dev` / `yarn build-dev`
To build for dev: `yarn build-dev`
To build for production: `npm build` / `yarn build`
## TODO
- Search repo for "TODO"
To build for production: `yarn build`

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -7,8 +7,8 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>InvokeAI - A Stable Diffusion Toolkit</title>
<link rel="shortcut icon" type="icon" href="./assets/favicon.0d253ced.ico" />
<script type="module" crossorigin src="./assets/index.2a9ecaa6.js"></script>
<link rel="stylesheet" href="./assets/index.8badc8b4.css">
<script type="module" crossorigin src="./assets/index.b7daf15c.js"></script>
<link rel="stylesheet" href="./assets/index.1536494e.css">
<script type="module">try{import.meta.url;import("_").catch(()=>1);}catch(e){}window.__vite_is_modern_browser=true;</script>
<script type="module">!function(){if(window.__vite_is_modern_browser)return;console.warn("vite: loading legacy build because dynamic import or import.meta.url is unsupported, syntax error above should be ignored");var e=document.getElementById("vite-legacy-polyfill"),n=document.createElement("script");n.src=e.src,n.onload=function(){System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))},document.body.appendChild(n)}();</script>
</head>
@ -18,6 +18,6 @@
<script nomodule>!function(){var e=document,t=e.createElement("script");if(!("noModule"in t)&&"onbeforeload"in t){var n=!1;e.addEventListener("beforeload",(function(e){if(e.target===t)n=!0;else if(!e.target.hasAttribute("nomodule")||!n)return;e.preventDefault()}),!0),t.type="module",t.src=".",e.head.appendChild(t),t.remove()}}();</script>
<script nomodule crossorigin id="vite-legacy-polyfill" src="./assets/polyfills-legacy-dde3a68a.js"></script>
<script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-8f3bb5f8.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
<script nomodule crossorigin id="vite-legacy-entry" data-src="./assets/index-legacy-7649c4ae.js">System.import(document.getElementById('vite-legacy-entry').getAttribute('data-src'))</script>
</body>
</html>

View File

@ -21,6 +21,7 @@
"langSpanish": "Spanish",
"langJapanese": "Japanese",
"langDutch": "Dutch",
"langUkranian": "Ukranian",
"text2img": "Text To Image",
"img2img": "Image To Image",
"unifiedCanvas": "Unified Canvas",

View File

@ -21,6 +21,7 @@
"langSpanish": "Spanish",
"langJapanese": "Japanese",
"langDutch": "Dutch",
"langUkranian": "Ukranian",
"text2img": "Text To Image",
"img2img": "Image To Image",
"unifiedCanvas": "Unified Canvas",

View File

@ -19,6 +19,8 @@
"langPolish": "Polacco",
"langSimplifiedChinese": "Cinese semplificato",
"langSpanish": "Spagnolo",
"langJapanese": "Giapponese",
"langDutch": "Olandese",
"text2img": "Testo a Immagine",
"img2img": "Immagine a Immagine",
"unifiedCanvas": "Tela unificata",
@ -34,6 +36,7 @@
"upload": "Caricamento",
"close": "Chiudi",
"load": "Carica",
"back": "Indietro",
"statusConnected": "Collegato",
"statusDisconnected": "Disconnesso",
"statusError": "Errore",

View File

@ -0,0 +1,53 @@
{
"hotkeysLabel": арячi клавіші",
"themeLabel": "Тема",
"languagePickerLabel": "Мова",
"reportBugLabel": "Повідомити про помилку",
"githubLabel": "Github",
"discordLabel": "Discord",
"settingsLabel": "Налаштування",
"darkTheme": "Темна",
"lightTheme": "Світла",
"greenTheme": "Зелена",
"langEnglish": "Англійська",
"langRussian": "Російська",
"langItalian": "Iталійська",
"langPortuguese": "Португальська",
"langFrench": "Французька",
"text2img": "Зображення із тексту (text2img)",
"img2img": "Зображення із зображення (img2img)",
"unifiedCanvas": "Універсальне полотно",
"nodes": "Вузли",
"nodesDesc": "Система генерації зображень на основі нодів (вузлів) вже розробляється. Слідкуйте за новинами про цю чудову функцію.",
"postProcessing": "Постобробка",
"postProcessDesc1": "Invoke AI пропонує широкий спектр функцій постобробки. Збільшення зображення (upscale) та відновлення облич вже доступні в інтерфейсі. Отримайте доступ до них з меню 'Додаткові параметри' на вкладках 'Зображення із тексту' та 'Зображення із зображення'. Обробляйте зображення безпосередньо, використовуючи кнопки дій із зображеннями над поточним зображенням або в режимі перегляду.",
"postProcessDesc2": "Найближчим часом буде випущено спеціальний інтерфейс для більш сучасних процесів постобробки.",
"postProcessDesc3": "Інтерфейс командного рядка Invoke AI пропонує різні інші функції, включаючи збільшення Embiggen",
"training": "Навчання",
"trainingDesc1": "Спеціальний інтерфейс для навчання власних моделей з використанням Textual Inversion та Dreambooth",
"trainingDesc2": "InvokeAI вже підтримує навчання моделей за допомогою TI, через інтерфейс командного рядка.",
"upload": "Завантажити",
"close": "Закрити",
"load": "Завантажити",
"statusConnected": "Підключено",
"statusDisconnected": "Відключено",
"statusError": "Помилка",
"statusPreparing": "Підготування",
"statusProcessingCanceled": "Обробка перервана",
"statusProcessingComplete": "Обробка завершена",
"statusGenerating": "Генерація",
"statusGeneratingTextToImage": "Генерація зображення із тексту",
"statusGeneratingImageToImage": "Генерація зображення із зображення",
"statusGeneratingInpainting": "Домальовка всередині",
"statusGeneratingOutpainting": "Домальовка зовні",
"statusGenerationComplete": "Генерація завершена",
"statusIterationComplete": "Iтерація завершена",
"statusSavingImage": "Збереження зображення",
"statusRestoringFaces": "Відновлення облич",
"statusRestoringFacesGFPGAN": "Відновлення облич (GFPGAN)",
"statusRestoringFacesCodeFormer": "Відновлення облич (CodeFormer)",
"statusUpscaling": "Збільшення",
"statusUpscalingESRGAN": "Збільшення (ESRGAN)",
"statusLoadingModel": "Завантаження моделі",
"statusModelChanged": "Модель змінено"
}

View File

@ -0,0 +1,16 @@
{
"generations": "Генерації",
"showGenerations": "Показувати генерації",
"uploads": "Завантаження",
"showUploads": "Показувати завантаження",
"galleryImageSize": "Розмір зображень",
"galleryImageResetSize": "Аатоматичний розмір",
"gallerySettings": "Налаштування галереї",
"maintainAspectRatio": "Зберігати пропорції",
"autoSwitchNewImages": "Автоматично вибирати нові",
"singleColumnLayout": "Одна колонка",
"pinGallery": "Закріпити галерею",
"allImagesLoaded": "Всі зображення завантажені",
"loadMore": "Завантажити більше",
"noImagesInGallery": "Зображень немає"
}

View File

@ -0,0 +1,207 @@
{
"keyboardShortcuts": "Клавіатурні скорочення",
"appHotkeys": "Гарячі клавіші програми",
"generalHotkeys": "Загальні гарячі клавіші",
"galleryHotkeys": "Гарячі клавіші галереї",
"unifiedCanvasHotkeys": "Гарячі клавіші універсального полотна",
"invoke": {
"title": "Invoke",
"desc": "Згенерувати зображення"
},
"cancel": {
"title": "Скасувати",
"desc": "Скасувати генерацію зображення"
},
"focusPrompt": {
"title": "Переключитися на введення запиту",
"desc": "Перемикання на область введення запиту"
},
"toggleOptions": {
"title": "Показати/приховати параметри",
"desc": "Відкривати і закривати панель параметрів"
},
"pinOptions": {
"title": "Закріпити параметри",
"desc": "Закріпити панель параметрів"
},
"toggleViewer": {
"title": "Показати перегляд",
"desc": "Відкривати і закривати переглядач зображень"
},
"toggleGallery": {
"title": "Показати галерею",
"desc": "Відкривати і закривати скриньку галереї"
},
"maximizeWorkSpace": {
"title": "Максимізувати робочий простір",
"desc": "Приховати панелі і максимізувати робочу область"
},
"changeTabs": {
"title": "Переключити вкладку",
"desc": "Переключитися на іншу робочу область"
},
"consoleToggle": {
"title": "Показати консоль",
"desc": "Відкривати і закривати консоль"
},
"setPrompt": {
"title": "Використовувати запит",
"desc": "Використати запит із поточного зображення"
},
"setSeed": {
"title": "Використовувати сід",
"desc": "Використовувати сід поточного зображення"
},
"setParameters": {
"title": "Використовувати всі параметри",
"desc": "Використовувати всі параметри поточного зображення"
},
"restoreFaces": {
"title": "Відновити обличчя",
"desc": "Відновити обличчя на поточному зображенні"
},
"upscale": {
"title": "Збільшення",
"desc": "Збільшити поточне зображення"
},
"showInfo": {
"title": "Показати метадані",
"desc": "Показати метадані з поточного зображення"
},
"sendToImageToImage": {
"title": "Відправити в img2img",
"desc": "Надіслати поточне зображення в Image To Image"
},
"deleteImage": {
"title": "Видалити зображення",
"desc": "Видалити поточне зображення"
},
"closePanels": {
"title": "Закрити панелі",
"desc": "Закриває відкриті панелі"
},
"previousImage": {
"title": "Попереднє зображення",
"desc": "Відображати попереднє зображення в галереї"
},
"nextImage": {
"title": "Наступне зображення",
"desc": "Відображення наступного зображення в галереї"
},
"toggleGalleryPin": {
"title": "Закріпити галерею",
"desc": "Закріплює і відкріплює галерею"
},
"increaseGalleryThumbSize": {
"title": "Збільшити розмір мініатюр галереї",
"desc": "Збільшує розмір мініатюр галереї"
},
"reduceGalleryThumbSize": {
"title": "Зменшує розмір мініатюр галереї",
"desc": "Зменшує розмір мініатюр галереї"
},
"selectBrush": {
"title": "Вибрати пензель",
"desc": "Вибирає пензель для полотна"
},
"selectEraser": {
"title": "Вибрати ластик",
"desc": "Вибирає ластик для полотна"
},
"reduceBrushSize": {
"title": "Зменшити розмір пензля",
"desc": "Зменшує розмір пензля/ластика полотна"
},
"increaseBrushSize": {
"title": "Збільшити розмір пензля",
"desc": "Збільшує розмір пензля/ластика полотна"
},
"reduceBrushOpacity": {
"title": "Зменшити непрозорість пензля",
"desc": "Зменшує непрозорість пензля полотна"
},
"increaseBrushOpacity": {
"title": "Збільшити непрозорість пензля",
"desc": "Збільшує непрозорість пензля полотна"
},
"moveTool": {
"title": "Інструмент переміщення",
"desc": "Дозволяє переміщатися по полотну"
},
"fillBoundingBox": {
"title": "Заповнити обмежувальну рамку",
"desc": "Заповнює обмежувальну рамку кольором пензля"
},
"eraseBoundingBox": {
"title": "Стерти обмежувальну рамку",
"desc": "Стирає область обмежувальної рамки"
},
"colorPicker": {
"title": "Вибрати колір",
"desc": "Вибирає засіб вибору кольору полотна"
},
"toggleSnap": {
"title": "Увімкнути прив'язку",
"desc": "Вмикає/вимикає прив'язку до сітки"
},
"quickToggleMove": {
"title": "Швидке перемикання переміщення",
"desc": "Тимчасово перемикає режим переміщення"
},
"toggleLayer": {
"title": "Переключити шар",
"desc": "Перемикання маски/базового шару"
},
"clearMask": {
"title": "Очистити маску",
"desc": "Очистити всю маску"
},
"hideMask": {
"title": "Приховати маску",
"desc": "Приховує/показує маску"
},
"showHideBoundingBox": {
"title": "Показати/приховати обмежувальну рамку",
"desc": "Переключити видимість обмежувальної рамки"
},
"mergeVisible": {
"title": "Об'єднати видимі",
"desc": "Об'єднати всі видимі шари полотна"
},
"saveToGallery": {
"title": "Зберегти в галерею",
"desc": "Зберегти поточне полотно в галерею"
},
"copyToClipboard": {
"title": "Копіювати в буфер обміну",
"desc": "Копіювати поточне полотно в буфер обміну"
},
"downloadImage": {
"title": "Завантажити зображення",
"desc": "Завантажити вміст полотна"
},
"undoStroke": {
"title": "Скасувати пензель",
"desc": "Скасувати мазок пензля"
},
"redoStroke": {
"title": "Повторити мазок пензля",
"desc": "Повторити мазок пензля"
},
"resetView": {
"title": "Вид за замовчуванням",
"desc": "Скинути вид полотна"
},
"previousStagingImage": {
"title": "Попереднє зображення",
"desc": "Попереднє зображення"
},
"nextStagingImage": {
"title": "Наступне зображення",
"desc": "Наступне зображення"
},
"acceptStagingImage": {
"title": "Прийняти зображення",
"desc": "Прийняти поточне зображення"
}
}

View File

@ -1,24 +1,34 @@
{
"modelManager": "Gestione Modelli",
"model": "Modello",
"allModels": "Tutti i Modelli",
"checkpointModels": "Checkpoint",
"diffusersModels": "Diffusori",
"safetensorModels": "SafeTensor",
"modelAdded": "Modello Aggiunto",
"modelUpdated": "Modello Aggiornato",
"modelEntryDeleted": "Modello Rimosso",
"cannotUseSpaces": "Impossibile utilizzare gli spazi",
"addNew": "Aggiungi nuovo",
"addNewModel": "Aggiungi nuovo Modello",
"addCheckpointModel": "Aggiungi modello Checkpoint / Safetensor",
"addDiffuserModel": "Aggiungi Diffusori",
"addManually": "Aggiungi manualmente",
"manual": "Manuale",
"name": "Nome",
"nameValidationMsg": "Inserisci un nome per il modello",
"description": "Descrizione",
"descriptionValidationMsg": "Aggiungi una descrizione per il modello",
"config": "Config",
"config": "Configurazione",
"configValidationMsg": "Percorso del file di configurazione del modello.",
"modelLocation": "Posizione del modello",
"modelLocationValidationMsg": "Percorso dove si trova il modello.",
"repo_id": "Repo ID",
"repoIDValidationMsg": "Repository online del modello",
"vaeLocation": "Posizione file VAE",
"vaeLocationValidationMsg": "Percorso dove si trova il file VAE.",
"vaeRepoID": "VAE Repo ID",
"vaeRepoIDValidationMsg": "Repository online del file VAE",
"width": "Larghezza",
"widthValidationMsg": "Larghezza predefinita del modello.",
"height": "Altezza",
@ -49,5 +59,9 @@
"deleteModel": "Elimina modello",
"deleteConfig": "Elimina configurazione",
"deleteMsg1": "Sei sicuro di voler eliminare questo modello da InvokeAI?",
"deleteMsg2": "Questo non eliminerà il file Checkpoint del modello dal tuo disco. Puoi aggiungerlo nuovamente se lo desideri."
"deleteMsg2": "Questo non eliminerà il file Checkpoint del modello dal tuo disco. Puoi aggiungerlo nuovamente se lo desideri.",
"formMessageDiffusersModelLocation": "Ubicazione modelli diffusori",
"formMessageDiffusersModelLocationDesc": "Inseriscine almeno uno.",
"formMessageDiffusersVAELocation": "Ubicazione file VAE",
"formMessageDiffusersVAELocationDesc": "Se non fornito, InvokeAI cercherà il file VAE all'interno dell'ubicazione del modello sopra indicata."
}

View File

@ -0,0 +1,53 @@
{
"modelManager": "Менеджер моделей",
"model": "Модель",
"modelAdded": "Модель додана",
"modelUpdated": "Модель оновлена",
"modelEntryDeleted": "Запис про модель видалено",
"cannotUseSpaces": "Не можна використовувати пробіли",
"addNew": "Додати нову",
"addNewModel": "Додати нову модель",
"addManually": "Додати вручну",
"manual": "Ручне",
"name": "Назва",
"nameValidationMsg": "Введіть назву моделі",
"description": "Опис",
"descriptionValidationMsg": "Введіть опис моделі",
"config": "Файл конфігурації",
"configValidationMsg": "Шлях до файлу конфігурації",
"modelLocation": "Розташування моделі",
"modelLocationValidationMsg": "Шлях до файлу з моделлю",
"vaeLocation": "Розтышування VAE",
"vaeLocationValidationMsg": "Шлях до VAE",
"width": "Ширина",
"widthValidationMsg": "Початкова ширина зображень",
"height": "Висота",
"heightValidationMsg": "Початкова висота зображень",
"addModel": "Додати модель",
"updateModel": "Оновити модель",
"availableModels": "Доступні моделі",
"search": "Шукати",
"load": "Завантажити",
"active": "активна",
"notLoaded": "не завантажена",
"cached": "кешована",
"checkpointFolder": "Папка з моделями",
"clearCheckpointFolder": "Очистити папку з моделями",
"findModels": "Знайти моделі",
"scanAgain": "Сканувати знову",
"modelsFound": "Знайдені моделі",
"selectFolder": "Обрати папку",
"selected": "Обрані",
"selectAll": "Обрати всі",
"deselectAll": "Зняти выділення",
"showExisting": "Показувати додані",
"addSelected": "Додати обрані",
"modelExists": "Модель вже додана",
"selectAndAdd": "Оберіть і додайте моделі із списку",
"noModelsFound": "Моделі не знайдені",
"delete": "Видалити",
"deleteModel": "Видалити модель",
"deleteConfig": "Видалити конфігурацію",
"deleteMsg1": "Ви точно хочете видалити модель із InvokeAI?",
"deleteMsg2": "Це не призведе до видалення файлу моделі з диску. Позніше ви можете додати його знову."
}

View File

@ -24,6 +24,7 @@
"otherOptions": "Other Options",
"seamlessTiling": "Seamless Tiling",
"hiresOptim": "High Res Optimization",
"hiresStrength": "High Res Strength",
"imageFit": "Fit Initial Image To Output Size",
"codeformerFidelity": "Fidelity",
"seamSize": "Seam Size",

View File

@ -24,6 +24,7 @@
"otherOptions": "Other Options",
"seamlessTiling": "Seamless Tiling",
"hiresOptim": "High Res Optimization",
"hiresStrength": "High Res Strength",
"imageFit": "Fit Initial Image To Output Size",
"codeformerFidelity": "Fidelity",
"seamSize": "Seam Size",
@ -43,6 +44,7 @@
"invoke": "Invoke",
"cancel": "Cancel",
"promptPlaceholder": "Type prompt here. [negative tokens], (upweight)++, (downweight)--, swap and blend are available (see docs)",
"negativePrompts": "Negative Prompts",
"sendTo": "Send to",
"sendToImg2Img": "Send to Image to Image",
"sendToUnifiedCanvas": "Send To Unified Canvas",

View File

@ -40,7 +40,7 @@
"infillScalingHeader": "Riempimento e ridimensionamento",
"img2imgStrength": "Forza da Immagine a Immagine",
"toggleLoopback": "Attiva/disattiva elaborazione ricorsiva",
"invoke": "Invoca",
"invoke": "Invoke",
"cancel": "Annulla",
"promptPlaceholder": "Digita qui il prompt usando termini in lingua inglese. [token negativi], (aumenta il peso)++, (diminuisci il peso)--, scambia e fondi sono disponibili (consulta la documentazione)",
"sendTo": "Invia a",

View File

@ -0,0 +1,62 @@
{
"images": "Зображення",
"steps": "Кроки",
"cfgScale": "Рівень CFG",
"width": "Ширина",
"height": "Висота",
"sampler": "Семплер",
"seed": "Сід",
"randomizeSeed": "Випадковий сид",
"shuffle": "Оновити",
"noiseThreshold": "Поріг шуму",
"perlinNoise": "Шум Перліна",
"variations": "Варіації",
"variationAmount": "Кількість варіацій",
"seedWeights": "Вага сіду",
"faceRestoration": "Відновлення облич",
"restoreFaces": "Відновити обличчя",
"type": "Тип",
"strength": "Сила",
"upscaling": "Збільшення",
"upscale": "Збільшити",
"upscaleImage": "Збільшити зображення",
"scale": "Масштаб",
"otherOptions": "інші параметри",
"seamlessTiling": "Безшовний узор",
"hiresOptim": "Висока роздільна здатність",
"imageFit": "Вмістити зображення",
"codeformerFidelity": "Точність",
"seamSize": "Размір шву",
"seamBlur": "Розмиття шву",
"seamStrength": "Сила шву",
"seamSteps": "Кроки шву",
"inpaintReplace": "Inpaint-заміна",
"scaleBeforeProcessing": "Масштабувати",
"scaledWidth": "Масштаб Ш",
"scaledHeight": "Масштаб В",
"infillMethod": "Засіб заповнення",
"tileSize": "Розмір області",
"boundingBoxHeader": "Обмежуюча рамка",
"seamCorrectionHeader": "Налаштування шву",
"infillScalingHeader": "Заповнення і масштабування",
"img2imgStrength": "Сила обробки img2img",
"toggleLoopback": "Зациклити обробку",
"invoke": "Викликати",
"cancel": "Скасувати",
"promptPlaceholder": "Введіть запит тут (англійською). [видалені токени], (більш вагомі)++, (менш вагомі)--, swap и blend також доступні (дивіться Github)",
"sendTo": "Надіслати",
"sendToImg2Img": "Надіслати у img2img",
"sendToUnifiedCanvas": "Надіслати на полотно",
"copyImageToLink": "Скопіювати посилання",
"downloadImage": "Завантажити",
"openInViewer": "Відкрити у переглядачі",
"closeViewer": "Закрити переглядач",
"usePrompt": "Використати запит",
"useSeed": "Використати сід",
"useAll": "Використати все",
"useInitImg": "Використати як початкове",
"info": "Метадані",
"deleteImage": "Видалити зображення",
"initialImage": "Початкове зображення",
"showOptionsPanel": "Показати панель налаштувань"
}

View File

@ -0,0 +1,13 @@
{
"models": "Моделі",
"displayInProgress": "Показувати процес генерації",
"saveSteps": "Зберігати кожні n кроків",
"confirmOnDelete": "Підтверджувати видалення",
"displayHelpIcons": "Показувати значки підказок",
"useCanvasBeta": "Показувати інструменты зліва (Beta UI)",
"enableImageDebugging": "Увімкнути налагодження",
"resetWebUI": "Повернути початкові",
"resetWebUIDesc1": "Скидання настройок веб-інтерфейсу видаляє лише локальний кеш браузера з вашими зображеннями та налаштуваннями. Це не призводить до видалення зображень з диску.",
"resetWebUIDesc2": "Якщо зображення не відображаються в галереї або не працює ще щось, спробуйте скинути налаштування, перш ніж повідомляти про проблему на GitHub.",
"resetComplete": "Інтерфейс скинуто. Оновіть цю сторінку."
}

View File

@ -0,0 +1,32 @@
{
"tempFoldersEmptied": "Тимчасова папка очищена",
"uploadFailed": "Не вдалося завантажити",
"uploadFailedMultipleImagesDesc": "Можна вставити лише одне зображення (ви спробували вставити декілька)",
"uploadFailedUnableToLoadDesc": "Неможливо завантажити файл",
"downloadImageStarted": "Завантаження зображення почалося",
"imageCopied": "Зображення скопійоване",
"imageLinkCopied": "Посилання на зображення скопійовано",
"imageNotLoaded": "Зображення не завантажено",
"imageNotLoadedDesc": "Не знайдено зображення для надсилання до img2img",
"imageSavedToGallery": "Зображення збережено в галерею",
"canvasMerged": "Полотно об'єднане",
"sentToImageToImage": "Надіслати до img2img",
"sentToUnifiedCanvas": "Надіслати на полотно",
"parametersSet": "Параметри задані",
"parametersNotSet": "Параметри не задані",
"parametersNotSetDesc": "Не знайдені метадані цього зображення",
"parametersFailed": "Проблема із завантаженням параметрів",
"parametersFailedDesc": "Неможливо завантажити початкове зображення",
"seedSet": "Сід заданий",
"seedNotSet": "Сід не заданий",
"seedNotSetDesc": "Не вдалося знайти сід для зображення",
"promptSet": "Запит заданий",
"promptNotSet": "Запит не заданий",
"promptNotSetDesc": "Не вдалося знайти запит для зображення",
"upscalingFailed": "Збільшення не вдалося",
"faceRestoreFailed": "Відновлення облич не вдалося",
"metadataLoadFailed": "Не вдалося завантажити метадані",
"initialImageSet": "Початкове зображення задане",
"initialImageNotSet": "Початкове зображення не задане",
"initialImageNotSetDesc": "Не вдалося завантажити початкове зображення"
}

View File

@ -0,0 +1,15 @@
{
"feature": {
"prompt": "Це поле для тексту запиту, включаючи об'єкти генерації та стилістичні терміни. У запит можна включити і коефіцієнти ваги (значущості токена), але консольні команди та параметри не працюватимуть.",
"gallery": "Тут відображаються генерації з папки outputs у міру їх появи.",
"other": "Ці опції включають альтернативні режими обробки для Invoke. 'Безшовний узор' створить на виході узори, що повторюються. 'Висока роздільна здатність' - це генерація у два етапи за допомогою img2img: використовуйте це налаштування, коли хочете отримати цільне зображення більшого розміру без артефактів.",
"seed": "Значення сіду впливає на початковий шум, з якого сформується зображення. Можна використовувати вже наявний сід із попередніх зображень. 'Поріг шуму' використовується для пом'якшення артефактів при високих значеннях CFG (спробуйте в діапазоні 0-10), а 'Перлін' - для додавання шуму Перліна в процесі генерації: обидва параметри служать для більшої варіативності результатів.",
"variations": "Спробуйте варіацію зі значенням від 0.1 до 1.0, щоб змінити результат для заданого сиду. Цікаві варіації сиду знаходяться між 0.1 і 0.3.",
"upscale": "Використовуйте ESRGAN, щоб збільшити зображення відразу після генерації.",
"faceCorrection": "Корекція облич за допомогою GFPGAN або Codeformer: алгоритм визначає обличчя у готовому зображенні та виправляє будь-які дефекти. Високі значення сили змінюють зображення сильніше, в результаті обличчя будуть виглядати привабливіше. У Codeformer більш висока точність збереже вихідне зображення на шкоду корекції обличчя.",
"imageToImage": "'Зображення до зображення' завантажує будь-яке зображення, яке потім використовується для генерації разом із запитом. Чим більше значення, тим сильніше зміниться зображення в результаті. Можливі значення від 0 до 1, рекомендується діапазон 0.25-0.75",
"boundingBox": "'Обмежуюча рамка' аналогічна налаштуванням 'Ширина' і 'Висота' для 'Зображення з тексту' або 'Зображення до зображення'. Буде оброблена тільки область у рамці.",
"seamCorrection": "Керування обробкою видимих швів, що виникають між зображеннями на полотні.",
"infillAndScaling": "Керування методами заповнення (використовується для масок або стертих частин полотна) та масштабування (корисно для малих розмірів обмежуючої рамки)."
}
}

View File

@ -1,5 +1,5 @@
{
"layer": "Layer",
"layer": "Livello",
"base": "Base",
"mask": "Maschera",
"maskingOptions": "Opzioni di mascheramento",

View File

@ -0,0 +1,59 @@
{
"layer": "Шар",
"base": "Базовий",
"mask": "Маска",
"maskingOptions": "Параметри маски",
"enableMask": "Увiмкнути маску",
"preserveMaskedArea": "Зберiгати замасковану область",
"clearMask": "Очистити маску",
"brush": "Пензель",
"eraser": "Гумка",
"fillBoundingBox": "Заповнити обмежуючу рамку",
"eraseBoundingBox": "Стерти обмежуючу рамку",
"colorPicker": "Пiпетка",
"brushOptions": "Параметри пензля",
"brushSize": "Розмiр",
"move": еремiстити",
"resetView": "Скинути вигляд",
"mergeVisible": "Об'єднати видимi",
"saveToGallery": "Зберегти до галереї",
"copyToClipboard": "Копiювати до буферу обмiну",
"downloadAsImage": "Завантажити як зображення",
"undo": "Вiдмiнити",
"redo": "Повторити",
"clearCanvas": "Очистити полотно",
"canvasSettings": "Налаштування полотна",
"showIntermediates": "Показувати процес",
"showGrid": "Показувати сiтку",
"snapToGrid": "Прив'язати до сітки",
"darkenOutsideSelection": "Затемнити полотно зовні",
"autoSaveToGallery": "Автозбереження до галереї",
"saveBoxRegionOnly": "Зберiгати тiльки видiлення",
"limitStrokesToBox": "Обмежити штрихи виділенням",
"showCanvasDebugInfo": "Показати налаштування полотна",
"clearCanvasHistory": "Очистити iсторiю полотна",
"clearHistory": "Очистити iсторiю",
"clearCanvasHistoryMessage": "Очищення історії полотна залишає поточне полотно незайманим, але видаляє історію скасування та повтору",
"clearCanvasHistoryConfirm": "Ви впевнені, що хочете очистити історію полотна?",
"emptyTempImageFolder": "Очистити тимчасову папку",
"emptyFolder": "Очистити папку",
"emptyTempImagesFolderMessage": "Очищення папки тимчасових зображень також повністю скидає полотно, включаючи всю історію скасування/повтору, зображення та базовий шар полотна, що розміщуються.",
"emptyTempImagesFolderConfirm": "Ви впевнені, що хочете очистити тимчасову папку?",
"activeLayer": "Активний шар",
"canvasScale": "Масштаб полотна",
"boundingBox": "Обмежуюча рамка",
"scaledBoundingBox": "Масштабування рамки",
"boundingBoxPosition": "Позиція обмежуючої рамки",
"canvasDimensions": "Разміри полотна",
"canvasPosition": "Розташування полотна",
"cursorPosition": "Розташування курсора",
"previous": "Попереднє",
"next": "Наступне",
"принять": "Приняти",
"showHide": "Показати/Сховати",
"discardAll": "Відмінити все",
"betaClear": "Очистити",
"betaDarkenOutside": "Затемнити зовні",
"betaLimitToBox": "Обмежити виділенням",
"betaPreserveMasked": "Зберiгати замасковану область"
}

View File

@ -21,6 +21,7 @@
"langSpanish": "Spanish",
"langJapanese": "Japanese",
"langDutch": "Dutch",
"langUkranian": "Ukranian",
"text2img": "Text To Image",
"img2img": "Image To Image",
"unifiedCanvas": "Unified Canvas",

View File

@ -21,6 +21,7 @@
"langSpanish": "Spanish",
"langJapanese": "Japanese",
"langDutch": "Dutch",
"langUkranian": "Ukranian",
"text2img": "Text To Image",
"img2img": "Image To Image",
"unifiedCanvas": "Unified Canvas",

View File

@ -19,6 +19,8 @@
"langPolish": "Polacco",
"langSimplifiedChinese": "Cinese semplificato",
"langSpanish": "Spagnolo",
"langJapanese": "Giapponese",
"langDutch": "Olandese",
"text2img": "Testo a Immagine",
"img2img": "Immagine a Immagine",
"unifiedCanvas": "Tela unificata",
@ -34,6 +36,7 @@
"upload": "Caricamento",
"close": "Chiudi",
"load": "Carica",
"back": "Indietro",
"statusConnected": "Collegato",
"statusDisconnected": "Disconnesso",
"statusError": "Errore",

View File

@ -0,0 +1,53 @@
{
"hotkeysLabel": арячi клавіші",
"themeLabel": "Тема",
"languagePickerLabel": "Мова",
"reportBugLabel": "Повідомити про помилку",
"githubLabel": "Github",
"discordLabel": "Discord",
"settingsLabel": "Налаштування",
"darkTheme": "Темна",
"lightTheme": "Світла",
"greenTheme": "Зелена",
"langEnglish": "Англійська",
"langRussian": "Російська",
"langItalian": "Iталійська",
"langPortuguese": "Португальська",
"langFrench": "Французька",
"text2img": "Зображення із тексту (text2img)",
"img2img": "Зображення із зображення (img2img)",
"unifiedCanvas": "Універсальне полотно",
"nodes": "Вузли",
"nodesDesc": "Система генерації зображень на основі нодів (вузлів) вже розробляється. Слідкуйте за новинами про цю чудову функцію.",
"postProcessing": "Постобробка",
"postProcessDesc1": "Invoke AI пропонує широкий спектр функцій постобробки. Збільшення зображення (upscale) та відновлення облич вже доступні в інтерфейсі. Отримайте доступ до них з меню 'Додаткові параметри' на вкладках 'Зображення із тексту' та 'Зображення із зображення'. Обробляйте зображення безпосередньо, використовуючи кнопки дій із зображеннями над поточним зображенням або в режимі перегляду.",
"postProcessDesc2": "Найближчим часом буде випущено спеціальний інтерфейс для більш сучасних процесів постобробки.",
"postProcessDesc3": "Інтерфейс командного рядка Invoke AI пропонує різні інші функції, включаючи збільшення Embiggen",
"training": "Навчання",
"trainingDesc1": "Спеціальний інтерфейс для навчання власних моделей з використанням Textual Inversion та Dreambooth",
"trainingDesc2": "InvokeAI вже підтримує навчання моделей за допомогою TI, через інтерфейс командного рядка.",
"upload": "Завантажити",
"close": "Закрити",
"load": "Завантажити",
"statusConnected": "Підключено",
"statusDisconnected": "Відключено",
"statusError": "Помилка",
"statusPreparing": "Підготування",
"statusProcessingCanceled": "Обробка перервана",
"statusProcessingComplete": "Обробка завершена",
"statusGenerating": "Генерація",
"statusGeneratingTextToImage": "Генерація зображення із тексту",
"statusGeneratingImageToImage": "Генерація зображення із зображення",
"statusGeneratingInpainting": "Домальовка всередині",
"statusGeneratingOutpainting": "Домальовка зовні",
"statusGenerationComplete": "Генерація завершена",
"statusIterationComplete": "Iтерація завершена",
"statusSavingImage": "Збереження зображення",
"statusRestoringFaces": "Відновлення облич",
"statusRestoringFacesGFPGAN": "Відновлення облич (GFPGAN)",
"statusRestoringFacesCodeFormer": "Відновлення облич (CodeFormer)",
"statusUpscaling": "Збільшення",
"statusUpscalingESRGAN": "Збільшення (ESRGAN)",
"statusLoadingModel": "Завантаження моделі",
"statusModelChanged": "Модель змінено"
}

View File

@ -0,0 +1,16 @@
{
"generations": "Генерації",
"showGenerations": "Показувати генерації",
"uploads": "Завантаження",
"showUploads": "Показувати завантаження",
"galleryImageSize": "Розмір зображень",
"galleryImageResetSize": "Аатоматичний розмір",
"gallerySettings": "Налаштування галереї",
"maintainAspectRatio": "Зберігати пропорції",
"autoSwitchNewImages": "Автоматично вибирати нові",
"singleColumnLayout": "Одна колонка",
"pinGallery": "Закріпити галерею",
"allImagesLoaded": "Всі зображення завантажені",
"loadMore": "Завантажити більше",
"noImagesInGallery": "Зображень немає"
}

View File

@ -0,0 +1,207 @@
{
"keyboardShortcuts": "Клавіатурні скорочення",
"appHotkeys": "Гарячі клавіші програми",
"generalHotkeys": "Загальні гарячі клавіші",
"galleryHotkeys": "Гарячі клавіші галереї",
"unifiedCanvasHotkeys": "Гарячі клавіші універсального полотна",
"invoke": {
"title": "Invoke",
"desc": "Згенерувати зображення"
},
"cancel": {
"title": "Скасувати",
"desc": "Скасувати генерацію зображення"
},
"focusPrompt": {
"title": "Переключитися на введення запиту",
"desc": "Перемикання на область введення запиту"
},
"toggleOptions": {
"title": "Показати/приховати параметри",
"desc": "Відкривати і закривати панель параметрів"
},
"pinOptions": {
"title": "Закріпити параметри",
"desc": "Закріпити панель параметрів"
},
"toggleViewer": {
"title": "Показати перегляд",
"desc": "Відкривати і закривати переглядач зображень"
},
"toggleGallery": {
"title": "Показати галерею",
"desc": "Відкривати і закривати скриньку галереї"
},
"maximizeWorkSpace": {
"title": "Максимізувати робочий простір",
"desc": "Приховати панелі і максимізувати робочу область"
},
"changeTabs": {
"title": "Переключити вкладку",
"desc": "Переключитися на іншу робочу область"
},
"consoleToggle": {
"title": "Показати консоль",
"desc": "Відкривати і закривати консоль"
},
"setPrompt": {
"title": "Використовувати запит",
"desc": "Використати запит із поточного зображення"
},
"setSeed": {
"title": "Використовувати сід",
"desc": "Використовувати сід поточного зображення"
},
"setParameters": {
"title": "Використовувати всі параметри",
"desc": "Використовувати всі параметри поточного зображення"
},
"restoreFaces": {
"title": "Відновити обличчя",
"desc": "Відновити обличчя на поточному зображенні"
},
"upscale": {
"title": "Збільшення",
"desc": "Збільшити поточне зображення"
},
"showInfo": {
"title": "Показати метадані",
"desc": "Показати метадані з поточного зображення"
},
"sendToImageToImage": {
"title": "Відправити в img2img",
"desc": "Надіслати поточне зображення в Image To Image"
},
"deleteImage": {
"title": "Видалити зображення",
"desc": "Видалити поточне зображення"
},
"closePanels": {
"title": "Закрити панелі",
"desc": "Закриває відкриті панелі"
},
"previousImage": {
"title": "Попереднє зображення",
"desc": "Відображати попереднє зображення в галереї"
},
"nextImage": {
"title": "Наступне зображення",
"desc": "Відображення наступного зображення в галереї"
},
"toggleGalleryPin": {
"title": "Закріпити галерею",
"desc": "Закріплює і відкріплює галерею"
},
"increaseGalleryThumbSize": {
"title": "Збільшити розмір мініатюр галереї",
"desc": "Збільшує розмір мініатюр галереї"
},
"reduceGalleryThumbSize": {
"title": "Зменшує розмір мініатюр галереї",
"desc": "Зменшує розмір мініатюр галереї"
},
"selectBrush": {
"title": "Вибрати пензель",
"desc": "Вибирає пензель для полотна"
},
"selectEraser": {
"title": "Вибрати ластик",
"desc": "Вибирає ластик для полотна"
},
"reduceBrushSize": {
"title": "Зменшити розмір пензля",
"desc": "Зменшує розмір пензля/ластика полотна"
},
"increaseBrushSize": {
"title": "Збільшити розмір пензля",
"desc": "Збільшує розмір пензля/ластика полотна"
},
"reduceBrushOpacity": {
"title": "Зменшити непрозорість пензля",
"desc": "Зменшує непрозорість пензля полотна"
},
"increaseBrushOpacity": {
"title": "Збільшити непрозорість пензля",
"desc": "Збільшує непрозорість пензля полотна"
},
"moveTool": {
"title": "Інструмент переміщення",
"desc": "Дозволяє переміщатися по полотну"
},
"fillBoundingBox": {
"title": "Заповнити обмежувальну рамку",
"desc": "Заповнює обмежувальну рамку кольором пензля"
},
"eraseBoundingBox": {
"title": "Стерти обмежувальну рамку",
"desc": "Стирає область обмежувальної рамки"
},
"colorPicker": {
"title": "Вибрати колір",
"desc": "Вибирає засіб вибору кольору полотна"
},
"toggleSnap": {
"title": "Увімкнути прив'язку",
"desc": "Вмикає/вимикає прив'язку до сітки"
},
"quickToggleMove": {
"title": "Швидке перемикання переміщення",
"desc": "Тимчасово перемикає режим переміщення"
},
"toggleLayer": {
"title": "Переключити шар",
"desc": "Перемикання маски/базового шару"
},
"clearMask": {
"title": "Очистити маску",
"desc": "Очистити всю маску"
},
"hideMask": {
"title": "Приховати маску",
"desc": "Приховує/показує маску"
},
"showHideBoundingBox": {
"title": "Показати/приховати обмежувальну рамку",
"desc": "Переключити видимість обмежувальної рамки"
},
"mergeVisible": {
"title": "Об'єднати видимі",
"desc": "Об'єднати всі видимі шари полотна"
},
"saveToGallery": {
"title": "Зберегти в галерею",
"desc": "Зберегти поточне полотно в галерею"
},
"copyToClipboard": {
"title": "Копіювати в буфер обміну",
"desc": "Копіювати поточне полотно в буфер обміну"
},
"downloadImage": {
"title": "Завантажити зображення",
"desc": "Завантажити вміст полотна"
},
"undoStroke": {
"title": "Скасувати пензель",
"desc": "Скасувати мазок пензля"
},
"redoStroke": {
"title": "Повторити мазок пензля",
"desc": "Повторити мазок пензля"
},
"resetView": {
"title": "Вид за замовчуванням",
"desc": "Скинути вид полотна"
},
"previousStagingImage": {
"title": "Попереднє зображення",
"desc": "Попереднє зображення"
},
"nextStagingImage": {
"title": "Наступне зображення",
"desc": "Наступне зображення"
},
"acceptStagingImage": {
"title": "Прийняти зображення",
"desc": "Прийняти поточне зображення"
}
}

View File

@ -1,24 +1,34 @@
{
"modelManager": "Gestione Modelli",
"model": "Modello",
"allModels": "Tutti i Modelli",
"checkpointModels": "Checkpoint",
"diffusersModels": "Diffusori",
"safetensorModels": "SafeTensor",
"modelAdded": "Modello Aggiunto",
"modelUpdated": "Modello Aggiornato",
"modelEntryDeleted": "Modello Rimosso",
"cannotUseSpaces": "Impossibile utilizzare gli spazi",
"addNew": "Aggiungi nuovo",
"addNewModel": "Aggiungi nuovo Modello",
"addCheckpointModel": "Aggiungi modello Checkpoint / Safetensor",
"addDiffuserModel": "Aggiungi Diffusori",
"addManually": "Aggiungi manualmente",
"manual": "Manuale",
"name": "Nome",
"nameValidationMsg": "Inserisci un nome per il modello",
"description": "Descrizione",
"descriptionValidationMsg": "Aggiungi una descrizione per il modello",
"config": "Config",
"config": "Configurazione",
"configValidationMsg": "Percorso del file di configurazione del modello.",
"modelLocation": "Posizione del modello",
"modelLocationValidationMsg": "Percorso dove si trova il modello.",
"repo_id": "Repo ID",
"repoIDValidationMsg": "Repository online del modello",
"vaeLocation": "Posizione file VAE",
"vaeLocationValidationMsg": "Percorso dove si trova il file VAE.",
"vaeRepoID": "VAE Repo ID",
"vaeRepoIDValidationMsg": "Repository online del file VAE",
"width": "Larghezza",
"widthValidationMsg": "Larghezza predefinita del modello.",
"height": "Altezza",
@ -49,5 +59,9 @@
"deleteModel": "Elimina modello",
"deleteConfig": "Elimina configurazione",
"deleteMsg1": "Sei sicuro di voler eliminare questo modello da InvokeAI?",
"deleteMsg2": "Questo non eliminerà il file Checkpoint del modello dal tuo disco. Puoi aggiungerlo nuovamente se lo desideri."
"deleteMsg2": "Questo non eliminerà il file Checkpoint del modello dal tuo disco. Puoi aggiungerlo nuovamente se lo desideri.",
"formMessageDiffusersModelLocation": "Ubicazione modelli diffusori",
"formMessageDiffusersModelLocationDesc": "Inseriscine almeno uno.",
"formMessageDiffusersVAELocation": "Ubicazione file VAE",
"formMessageDiffusersVAELocationDesc": "Se non fornito, InvokeAI cercherà il file VAE all'interno dell'ubicazione del modello sopra indicata."
}

View File

@ -0,0 +1,53 @@
{
"modelManager": "Менеджер моделей",
"model": "Модель",
"modelAdded": "Модель додана",
"modelUpdated": "Модель оновлена",
"modelEntryDeleted": "Запис про модель видалено",
"cannotUseSpaces": "Не можна використовувати пробіли",
"addNew": "Додати нову",
"addNewModel": "Додати нову модель",
"addManually": "Додати вручну",
"manual": "Ручне",
"name": "Назва",
"nameValidationMsg": "Введіть назву моделі",
"description": "Опис",
"descriptionValidationMsg": "Введіть опис моделі",
"config": "Файл конфігурації",
"configValidationMsg": "Шлях до файлу конфігурації",
"modelLocation": "Розташування моделі",
"modelLocationValidationMsg": "Шлях до файлу з моделлю",
"vaeLocation": "Розтышування VAE",
"vaeLocationValidationMsg": "Шлях до VAE",
"width": "Ширина",
"widthValidationMsg": "Початкова ширина зображень",
"height": "Висота",
"heightValidationMsg": "Початкова висота зображень",
"addModel": "Додати модель",
"updateModel": "Оновити модель",
"availableModels": "Доступні моделі",
"search": "Шукати",
"load": "Завантажити",
"active": "активна",
"notLoaded": "не завантажена",
"cached": "кешована",
"checkpointFolder": "Папка з моделями",
"clearCheckpointFolder": "Очистити папку з моделями",
"findModels": "Знайти моделі",
"scanAgain": "Сканувати знову",
"modelsFound": "Знайдені моделі",
"selectFolder": "Обрати папку",
"selected": "Обрані",
"selectAll": "Обрати всі",
"deselectAll": "Зняти выділення",
"showExisting": "Показувати додані",
"addSelected": "Додати обрані",
"modelExists": "Модель вже додана",
"selectAndAdd": "Оберіть і додайте моделі із списку",
"noModelsFound": "Моделі не знайдені",
"delete": "Видалити",
"deleteModel": "Видалити модель",
"deleteConfig": "Видалити конфігурацію",
"deleteMsg1": "Ви точно хочете видалити модель із InvokeAI?",
"deleteMsg2": "Це не призведе до видалення файлу моделі з диску. Позніше ви можете додати його знову."
}

View File

@ -24,6 +24,7 @@
"otherOptions": "Other Options",
"seamlessTiling": "Seamless Tiling",
"hiresOptim": "High Res Optimization",
"hiresStrength": "High Res Strength",
"imageFit": "Fit Initial Image To Output Size",
"codeformerFidelity": "Fidelity",
"seamSize": "Seam Size",

View File

@ -24,6 +24,7 @@
"otherOptions": "Other Options",
"seamlessTiling": "Seamless Tiling",
"hiresOptim": "High Res Optimization",
"hiresStrength": "High Res Strength",
"imageFit": "Fit Initial Image To Output Size",
"codeformerFidelity": "Fidelity",
"seamSize": "Seam Size",
@ -43,6 +44,7 @@
"invoke": "Invoke",
"cancel": "Cancel",
"promptPlaceholder": "Type prompt here. [negative tokens], (upweight)++, (downweight)--, swap and blend are available (see docs)",
"negativePrompts": "Negative Prompts",
"sendTo": "Send to",
"sendToImg2Img": "Send to Image to Image",
"sendToUnifiedCanvas": "Send To Unified Canvas",

View File

@ -40,7 +40,7 @@
"infillScalingHeader": "Riempimento e ridimensionamento",
"img2imgStrength": "Forza da Immagine a Immagine",
"toggleLoopback": "Attiva/disattiva elaborazione ricorsiva",
"invoke": "Invoca",
"invoke": "Invoke",
"cancel": "Annulla",
"promptPlaceholder": "Digita qui il prompt usando termini in lingua inglese. [token negativi], (aumenta il peso)++, (diminuisci il peso)--, scambia e fondi sono disponibili (consulta la documentazione)",
"sendTo": "Invia a",

View File

@ -0,0 +1,62 @@
{
"images": "Зображення",
"steps": "Кроки",
"cfgScale": "Рівень CFG",
"width": "Ширина",
"height": "Висота",
"sampler": "Семплер",
"seed": "Сід",
"randomizeSeed": "Випадковий сид",
"shuffle": "Оновити",
"noiseThreshold": "Поріг шуму",
"perlinNoise": "Шум Перліна",
"variations": "Варіації",
"variationAmount": "Кількість варіацій",
"seedWeights": "Вага сіду",
"faceRestoration": "Відновлення облич",
"restoreFaces": "Відновити обличчя",
"type": "Тип",
"strength": "Сила",
"upscaling": "Збільшення",
"upscale": "Збільшити",
"upscaleImage": "Збільшити зображення",
"scale": "Масштаб",
"otherOptions": "інші параметри",
"seamlessTiling": "Безшовний узор",
"hiresOptim": "Висока роздільна здатність",
"imageFit": "Вмістити зображення",
"codeformerFidelity": "Точність",
"seamSize": "Размір шву",
"seamBlur": "Розмиття шву",
"seamStrength": "Сила шву",
"seamSteps": "Кроки шву",
"inpaintReplace": "Inpaint-заміна",
"scaleBeforeProcessing": "Масштабувати",
"scaledWidth": "Масштаб Ш",
"scaledHeight": "Масштаб В",
"infillMethod": "Засіб заповнення",
"tileSize": "Розмір області",
"boundingBoxHeader": "Обмежуюча рамка",
"seamCorrectionHeader": "Налаштування шву",
"infillScalingHeader": "Заповнення і масштабування",
"img2imgStrength": "Сила обробки img2img",
"toggleLoopback": "Зациклити обробку",
"invoke": "Викликати",
"cancel": "Скасувати",
"promptPlaceholder": "Введіть запит тут (англійською). [видалені токени], (більш вагомі)++, (менш вагомі)--, swap и blend також доступні (дивіться Github)",
"sendTo": "Надіслати",
"sendToImg2Img": "Надіслати у img2img",
"sendToUnifiedCanvas": "Надіслати на полотно",
"copyImageToLink": "Скопіювати посилання",
"downloadImage": "Завантажити",
"openInViewer": "Відкрити у переглядачі",
"closeViewer": "Закрити переглядач",
"usePrompt": "Використати запит",
"useSeed": "Використати сід",
"useAll": "Використати все",
"useInitImg": "Використати як початкове",
"info": "Метадані",
"deleteImage": "Видалити зображення",
"initialImage": "Початкове зображення",
"showOptionsPanel": "Показати панель налаштувань"
}

View File

@ -0,0 +1,13 @@
{
"models": "Моделі",
"displayInProgress": "Показувати процес генерації",
"saveSteps": "Зберігати кожні n кроків",
"confirmOnDelete": "Підтверджувати видалення",
"displayHelpIcons": "Показувати значки підказок",
"useCanvasBeta": "Показувати інструменты зліва (Beta UI)",
"enableImageDebugging": "Увімкнути налагодження",
"resetWebUI": "Повернути початкові",
"resetWebUIDesc1": "Скидання настройок веб-інтерфейсу видаляє лише локальний кеш браузера з вашими зображеннями та налаштуваннями. Це не призводить до видалення зображень з диску.",
"resetWebUIDesc2": "Якщо зображення не відображаються в галереї або не працює ще щось, спробуйте скинути налаштування, перш ніж повідомляти про проблему на GitHub.",
"resetComplete": "Інтерфейс скинуто. Оновіть цю сторінку."
}

View File

@ -0,0 +1,32 @@
{
"tempFoldersEmptied": "Тимчасова папка очищена",
"uploadFailed": "Не вдалося завантажити",
"uploadFailedMultipleImagesDesc": "Можна вставити лише одне зображення (ви спробували вставити декілька)",
"uploadFailedUnableToLoadDesc": "Неможливо завантажити файл",
"downloadImageStarted": "Завантаження зображення почалося",
"imageCopied": "Зображення скопійоване",
"imageLinkCopied": "Посилання на зображення скопійовано",
"imageNotLoaded": "Зображення не завантажено",
"imageNotLoadedDesc": "Не знайдено зображення для надсилання до img2img",
"imageSavedToGallery": "Зображення збережено в галерею",
"canvasMerged": "Полотно об'єднане",
"sentToImageToImage": "Надіслати до img2img",
"sentToUnifiedCanvas": "Надіслати на полотно",
"parametersSet": "Параметри задані",
"parametersNotSet": "Параметри не задані",
"parametersNotSetDesc": "Не знайдені метадані цього зображення",
"parametersFailed": "Проблема із завантаженням параметрів",
"parametersFailedDesc": "Неможливо завантажити початкове зображення",
"seedSet": "Сід заданий",
"seedNotSet": "Сід не заданий",
"seedNotSetDesc": "Не вдалося знайти сід для зображення",
"promptSet": "Запит заданий",
"promptNotSet": "Запит не заданий",
"promptNotSetDesc": "Не вдалося знайти запит для зображення",
"upscalingFailed": "Збільшення не вдалося",
"faceRestoreFailed": "Відновлення облич не вдалося",
"metadataLoadFailed": "Не вдалося завантажити метадані",
"initialImageSet": "Початкове зображення задане",
"initialImageNotSet": "Початкове зображення не задане",
"initialImageNotSetDesc": "Не вдалося завантажити початкове зображення"
}

View File

@ -0,0 +1,15 @@
{
"feature": {
"prompt": "Це поле для тексту запиту, включаючи об'єкти генерації та стилістичні терміни. У запит можна включити і коефіцієнти ваги (значущості токена), але консольні команди та параметри не працюватимуть.",
"gallery": "Тут відображаються генерації з папки outputs у міру їх появи.",
"other": "Ці опції включають альтернативні режими обробки для Invoke. 'Безшовний узор' створить на виході узори, що повторюються. 'Висока роздільна здатність' - це генерація у два етапи за допомогою img2img: використовуйте це налаштування, коли хочете отримати цільне зображення більшого розміру без артефактів.",
"seed": "Значення сіду впливає на початковий шум, з якого сформується зображення. Можна використовувати вже наявний сід із попередніх зображень. 'Поріг шуму' використовується для пом'якшення артефактів при високих значеннях CFG (спробуйте в діапазоні 0-10), а 'Перлін' - для додавання шуму Перліна в процесі генерації: обидва параметри служать для більшої варіативності результатів.",
"variations": "Спробуйте варіацію зі значенням від 0.1 до 1.0, щоб змінити результат для заданого сиду. Цікаві варіації сиду знаходяться між 0.1 і 0.3.",
"upscale": "Використовуйте ESRGAN, щоб збільшити зображення відразу після генерації.",
"faceCorrection": "Корекція облич за допомогою GFPGAN або Codeformer: алгоритм визначає обличчя у готовому зображенні та виправляє будь-які дефекти. Високі значення сили змінюють зображення сильніше, в результаті обличчя будуть виглядати привабливіше. У Codeformer більш висока точність збереже вихідне зображення на шкоду корекції обличчя.",
"imageToImage": "'Зображення до зображення' завантажує будь-яке зображення, яке потім використовується для генерації разом із запитом. Чим більше значення, тим сильніше зміниться зображення в результаті. Можливі значення від 0 до 1, рекомендується діапазон 0.25-0.75",
"boundingBox": "'Обмежуюча рамка' аналогічна налаштуванням 'Ширина' і 'Висота' для 'Зображення з тексту' або 'Зображення до зображення'. Буде оброблена тільки область у рамці.",
"seamCorrection": "Керування обробкою видимих швів, що виникають між зображеннями на полотні.",
"infillAndScaling": "Керування методами заповнення (використовується для масок або стертих частин полотна) та масштабування (корисно для малих розмірів обмежуючої рамки)."
}
}

View File

@ -1,5 +1,5 @@
{
"layer": "Layer",
"layer": "Livello",
"base": "Base",
"mask": "Maschera",
"maskingOptions": "Opzioni di mascheramento",

View File

@ -0,0 +1,59 @@
{
"layer": "Шар",
"base": "Базовий",
"mask": "Маска",
"maskingOptions": "Параметри маски",
"enableMask": "Увiмкнути маску",
"preserveMaskedArea": "Зберiгати замасковану область",
"clearMask": "Очистити маску",
"brush": "Пензель",
"eraser": "Гумка",
"fillBoundingBox": "Заповнити обмежуючу рамку",
"eraseBoundingBox": "Стерти обмежуючу рамку",
"colorPicker": "Пiпетка",
"brushOptions": "Параметри пензля",
"brushSize": "Розмiр",
"move": еремiстити",
"resetView": "Скинути вигляд",
"mergeVisible": "Об'єднати видимi",
"saveToGallery": "Зберегти до галереї",
"copyToClipboard": "Копiювати до буферу обмiну",
"downloadAsImage": "Завантажити як зображення",
"undo": "Вiдмiнити",
"redo": "Повторити",
"clearCanvas": "Очистити полотно",
"canvasSettings": "Налаштування полотна",
"showIntermediates": "Показувати процес",
"showGrid": "Показувати сiтку",
"snapToGrid": "Прив'язати до сітки",
"darkenOutsideSelection": "Затемнити полотно зовні",
"autoSaveToGallery": "Автозбереження до галереї",
"saveBoxRegionOnly": "Зберiгати тiльки видiлення",
"limitStrokesToBox": "Обмежити штрихи виділенням",
"showCanvasDebugInfo": "Показати налаштування полотна",
"clearCanvasHistory": "Очистити iсторiю полотна",
"clearHistory": "Очистити iсторiю",
"clearCanvasHistoryMessage": "Очищення історії полотна залишає поточне полотно незайманим, але видаляє історію скасування та повтору",
"clearCanvasHistoryConfirm": "Ви впевнені, що хочете очистити історію полотна?",
"emptyTempImageFolder": "Очистити тимчасову папку",
"emptyFolder": "Очистити папку",
"emptyTempImagesFolderMessage": "Очищення папки тимчасових зображень також повністю скидає полотно, включаючи всю історію скасування/повтору, зображення та базовий шар полотна, що розміщуються.",
"emptyTempImagesFolderConfirm": "Ви впевнені, що хочете очистити тимчасову папку?",
"activeLayer": "Активний шар",
"canvasScale": "Масштаб полотна",
"boundingBox": "Обмежуюча рамка",
"scaledBoundingBox": "Масштабування рамки",
"boundingBoxPosition": "Позиція обмежуючої рамки",
"canvasDimensions": "Разміри полотна",
"canvasPosition": "Розташування полотна",
"cursorPosition": "Розташування курсора",
"previous": "Попереднє",
"next": "Наступне",
"принять": "Приняти",
"showHide": "Показати/Сховати",
"discardAll": "Відмінити все",
"betaClear": "Очистити",
"betaDarkenOutside": "Затемнити зовні",
"betaLimitToBox": "Обмежити виділенням",
"betaPreserveMasked": "Зберiгати замасковану область"
}

View File

@ -11,7 +11,6 @@ const useClickOutsideWatcher = () => {
function handleClickOutside(e: MouseEvent) {
watchers.forEach(({ ref, enable, callback }) => {
if (enable && ref.current && !ref.current.contains(e.target as Node)) {
console.log('callback');
callback();
}
});

View File

@ -0,0 +1,20 @@
import * as InvokeAI from 'app/invokeai';
import promptToString from './promptToString';
export function getPromptAndNegative(input_prompt: InvokeAI.Prompt) {
let prompt: string = promptToString(input_prompt);
let negativePrompt: string | null = null;
const negativePromptRegExp = new RegExp(/(?<=\[)[^\][]*(?=])/, 'gi');
const negativePromptMatches = [...prompt.matchAll(negativePromptRegExp)];
if (negativePromptMatches && negativePromptMatches.length > 0) {
negativePrompt = negativePromptMatches.join(', ');
prompt = prompt
.replaceAll(negativePromptRegExp, '')
.replaceAll('[]', '')
.trim();
}
return [prompt, negativePrompt];
}

View File

@ -100,12 +100,14 @@ export const frontendToBackendParameters = (
facetoolType,
height,
hiresFix,
hiresStrength,
img2imgStrength,
infillMethod,
initialImage,
iterations,
perlin,
prompt,
negativePrompt,
sampler,
seamBlur,
seamless,
@ -155,6 +157,10 @@ export const frontendToBackendParameters = (
let esrganParameters: false | BackendEsrGanParameters = false;
let facetoolParameters: false | BackendFacetoolParameters = false;
if (negativePrompt !== '') {
generationParameters.prompt = `${prompt} [${negativePrompt}]`;
}
generationParameters.seed = shouldRandomizeSeed
? randomInt(NUMPY_RAND_MIN, NUMPY_RAND_MAX)
: seed;
@ -164,6 +170,8 @@ export const frontendToBackendParameters = (
generationParameters.seamless = seamless;
generationParameters.hires_fix = hiresFix;
if (hiresFix) generationParameters.strength = hiresStrength;
if (shouldRunESRGAN) {
esrganParameters = {
level: upscalingLevel,

View File

@ -9,6 +9,7 @@ import {
setAllParameters,
setInitialImage,
setIsLightBoxOpen,
setNegativePrompt,
setPrompt,
setSeed,
setShouldShowImageDetails,
@ -44,6 +45,7 @@ import { GalleryState } from 'features/gallery/store/gallerySlice';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import IAIPopover from 'common/components/IAIPopover';
import { useTranslation } from 'react-i18next';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
const systemSelector = createSelector(
[
@ -241,9 +243,18 @@ const CurrentImageButtons = () => {
[currentImage]
);
const handleClickUsePrompt = () =>
currentImage?.metadata?.image?.prompt &&
dispatch(setPrompt(currentImage.metadata.image.prompt));
const handleClickUsePrompt = () => {
if (currentImage?.metadata?.image?.prompt) {
const [prompt, negativePrompt] = getPromptAndNegative(
currentImage?.metadata?.image?.prompt
);
prompt && dispatch(setPrompt(prompt));
negativePrompt
? dispatch(setNegativePrompt(negativePrompt))
: dispatch(setNegativePrompt(''));
}
};
useHotkeys(
'p',

View File

@ -10,9 +10,10 @@ import { DragEvent, memo, useState } from 'react';
import {
setActiveTab,
setAllImageToImageParameters,
setAllTextToImageParameters,
setAllParameters,
setInitialImage,
setIsLightBoxOpen,
setNegativePrompt,
setPrompt,
setSeed,
} from 'features/options/store/optionsSlice';
@ -24,6 +25,7 @@ import {
} from 'features/canvas/store/canvasSlice';
import { hoverableImageSelector } from 'features/gallery/store/gallerySliceSelectors';
import { useTranslation } from 'react-i18next';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
interface HoverableImageProps {
image: InvokeAI.Image;
@ -62,7 +64,17 @@ const HoverableImage = memo((props: HoverableImageProps) => {
const handleMouseOut = () => setIsHovered(false);
const handleUsePrompt = () => {
image.metadata && dispatch(setPrompt(image.metadata.image.prompt));
if (image.metadata) {
const [prompt, negativePrompt] = getPromptAndNegative(
image.metadata?.image?.prompt
);
prompt && dispatch(setPrompt(prompt));
negativePrompt
? dispatch(setNegativePrompt(negativePrompt))
: dispatch(setNegativePrompt(''));
}
toast({
title: t('toast:promptSet'),
status: 'success',
@ -115,7 +127,7 @@ const HoverableImage = memo((props: HoverableImageProps) => {
};
const handleUseAllParameters = () => {
metadata && dispatch(setAllTextToImageParameters(metadata));
metadata && dispatch(setAllParameters(metadata));
toast({
title: t('toast:parametersSet'),
status: 'success',

View File

@ -38,7 +38,6 @@ export const uploadImage =
});
const image = (await response.json()) as InvokeAI.ImageUploadResponse;
console.log(image);
const newImage: InvokeAI.Image = {
uuid: uuidv4(),
category: 'user',

View File

@ -1,10 +1,53 @@
import { Flex } from '@chakra-ui/react';
import { ChangeEvent } from 'react';
import { RootState } from 'app/store';
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import IAISwitch from 'common/components/IAISwitch';
import { setHiresFix } from 'features/options/store/optionsSlice';
import {
setHiresFix,
setHiresStrength,
} from 'features/options/store/optionsSlice';
import { useTranslation } from 'react-i18next';
import IAISlider from 'common/components/IAISlider';
function HighResStrength() {
const hiresFix = useAppSelector((state: RootState) => state.options.hiresFix);
const hiresStrength = useAppSelector(
(state: RootState) => state.options.hiresStrength
);
const dispatch = useAppDispatch();
const { t } = useTranslation();
const handleHiresStrength = (v: number) => {
dispatch(setHiresStrength(v));
};
const handleHiResStrengthReset = () => {
dispatch(setHiresStrength(0.75));
};
return (
<IAISlider
label={t('options:hiresStrength')}
step={0.01}
min={0.01}
max={0.99}
onChange={handleHiresStrength}
value={hiresStrength}
isInteger={false}
withInput
withSliderMarks
inputWidth={'5.5rem'}
withReset
handleReset={handleHiResStrengthReset}
isSliderDisabled={!hiresFix}
isInputDisabled={!hiresFix}
isResetDisabled={!hiresFix}
/>
);
}
/**
* Hires Fix Toggle
@ -27,6 +70,7 @@ const HiresOptions = () => {
isChecked={hiresFix}
onChange={handleChangeHiresFix}
/>
<HighResStrength />
</Flex>
);
};

View File

@ -0,0 +1,38 @@
import { FormControl, Textarea } from '@chakra-ui/react';
import type { RootState } from 'app/store';
import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import { setNegativePrompt } from 'features/options/store/optionsSlice';
import { useTranslation } from 'react-i18next';
export function NegativePromptInput() {
const negativePrompt = useAppSelector(
(state: RootState) => state.options.negativePrompt
);
const dispatch = useAppDispatch();
const { t } = useTranslation();
return (
<FormControl>
<Textarea
id="negativePrompt"
name="negativePrompt"
value={negativePrompt}
onChange={(e) => dispatch(setNegativePrompt(e.target.value))}
background="var(--prompt-bg-color)"
placeholder={t('options:negativePrompts')}
_placeholder={{ fontSize: '0.8rem' }}
borderColor="var(--border-color)"
_hover={{
borderColor: 'var(--border-color-light)',
}}
_focusVisible={{
borderColor: 'var(--border-color-invalid)',
boxShadow: '0 0 10px var(--box-shadow-color-invalid)',
}}
fontSize="0.9rem"
color="var(--text-color-secondary)"
/>
</FormControl>
);
}

View File

@ -5,6 +5,7 @@ import promptToString from 'common/util/promptToString';
import { seedWeightsToString } from 'common/util/seedWeightPairs';
import { FACETOOL_TYPES } from 'app/constants';
import { InvokeTabName, tabMap } from 'features/tabs/tabMap';
import { getPromptAndNegative } from 'common/util/getPromptAndNegative';
export type UpscalingLevel = 2 | 4;
@ -19,6 +20,7 @@ export interface OptionsState {
facetoolType: FacetoolType;
height: number;
hiresFix: boolean;
hiresStrength: number;
img2imgStrength: number;
infillMethod: string;
initialImage?: InvokeAI.Image | string; // can be an Image or url
@ -28,6 +30,7 @@ export interface OptionsState {
optionsPanelScrollPosition: number;
perlin: number;
prompt: string;
negativePrompt: string;
sampler: string;
seamBlur: number;
seamless: boolean;
@ -69,6 +72,7 @@ const initialOptionsState: OptionsState = {
facetoolType: 'gfpgan',
height: 512,
hiresFix: false,
hiresStrength: 0.75,
img2imgStrength: 0.75,
infillMethod: 'patchmatch',
isLightBoxOpen: false,
@ -77,6 +81,7 @@ const initialOptionsState: OptionsState = {
optionsPanelScrollPosition: 0,
perlin: 0,
prompt: '',
negativePrompt: '',
sampler: 'k_lms',
seamBlur: 16,
seamless: false,
@ -123,6 +128,17 @@ export const optionsSlice = createSlice({
state.prompt = promptToString(newPrompt);
}
},
setNegativePrompt: (
state,
action: PayloadAction<string | InvokeAI.Prompt>
) => {
const newPrompt = action.payload;
if (typeof newPrompt === 'string') {
state.negativePrompt = newPrompt;
} else {
state.negativePrompt = promptToString(newPrompt);
}
},
setIterations: (state, action: PayloadAction<number>) => {
state.iterations = action.payload;
},
@ -175,6 +191,9 @@ export const optionsSlice = createSlice({
setHiresFix: (state, action: PayloadAction<boolean>) => {
state.hiresFix = action.payload;
},
setHiresStrength: (state, action: PayloadAction<number>) => {
state.hiresStrength = action.payload;
},
setShouldFitToWidthHeight: (state, action: PayloadAction<boolean>) => {
state.shouldFitToWidthHeight = action.payload;
},
@ -307,7 +326,14 @@ export const optionsSlice = createSlice({
state.shouldRandomizeSeed = false;
}
if (prompt) state.prompt = promptToString(prompt);
if (prompt) {
const [promptOnly, negativePrompt] = getPromptAndNegative(prompt);
if (promptOnly) state.prompt = promptOnly;
negativePrompt
? (state.negativePrompt = negativePrompt)
: (state.negativePrompt = '');
}
if (sampler) state.sampler = sampler;
if (steps) state.steps = steps;
if (cfg_scale) state.cfgScale = cfg_scale;
@ -438,6 +464,7 @@ export const {
setFacetoolType,
setHeight,
setHiresFix,
setHiresStrength,
setImg2imgStrength,
setInfillMethod,
setInitialImage,
@ -448,6 +475,7 @@ export const {
setParameter,
setPerlin,
setPrompt,
setNegativePrompt,
setSampler,
setSeamBlur,
setSeamless,

View File

@ -13,15 +13,17 @@ export default function LanguagePicker() {
const LANGUAGES = {
en: t('common:langEnglish'),
ru: t('common:langRussian'),
it: t('common:langItalian'),
pt_br: t('common:langBrPortuguese'),
nl: t('common:langDutch'),
fr: t('common:langFrench'),
de: t('common:langGerman'),
it: t('common:langItalian'),
ja: t('common:langJapanese'),
pl: t('common:langPolish'),
pt_br: t('common:langBrPortuguese'),
ru: t('common:langRussian'),
zh_cn: t('common:langSimplifiedChinese'),
es: t('common:langSpanish'),
ja: t('common:langJapanese'),
nl: t('common:langDutch'),
ua: t('common:langUkranian'),
};
const renderLanguagePicker = () => {

View File

@ -316,7 +316,6 @@ export default function CheckpointModelEdit() {
) : (
<Flex
width="100%"
height="250px"
justifyContent="center"
alignItems="center"
backgroundColor="var(--background-color)"

View File

@ -271,7 +271,6 @@ export default function DiffusersModelEdit() {
) : (
<Flex
width="100%"
height="250px"
justifyContent="center"
alignItems="center"
backgroundColor="var(--background-color)"

View File

@ -19,6 +19,8 @@ import { useAppDispatch, useAppSelector } from 'app/storeHooks';
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
import { activeTabNameSelector } from 'features/options/store/optionsSelectors';
import { useTranslation } from 'react-i18next';
import { Flex } from '@chakra-ui/react';
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
export default function ImageToImagePanel() {
const { t } = useTranslation();
@ -67,7 +69,10 @@ export default function ImageToImagePanel() {
return (
<InvokeOptionsPanel>
<PromptInput />
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainOptions />
<ImageToImageStrength

View File

@ -24,8 +24,8 @@
}
svg {
width: 26px;
height: 26px;
width: 24px;
height: 24px;
}
&[aria-selected='true'] {

View File

@ -1,3 +1,4 @@
import { Flex } from '@chakra-ui/react';
import { Feature } from 'app/features';
import FaceRestoreOptions from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreOptions';
import FaceRestoreToggle from 'features/options/components/AdvancedOptions/FaceRestore/FaceRestoreToggle';
@ -10,6 +11,7 @@ import VariationsOptions from 'features/options/components/AdvancedOptions/Varia
import MainOptions from 'features/options/components/MainOptions/MainOptions';
import OptionsAccordion from 'features/options/components/OptionsAccordion';
import ProcessButtons from 'features/options/components/ProcessButtons/ProcessButtons';
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
import PromptInput from 'features/options/components/PromptInput/PromptInput';
import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
import { useTranslation } from 'react-i18next';
@ -50,7 +52,10 @@ export default function TextToImagePanel() {
return (
<InvokeOptionsPanel>
<PromptInput />
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainOptions />
<OptionsAccordion accordionInfo={textToImageAccordions} />

View File

@ -13,6 +13,8 @@ import InvokeOptionsPanel from 'features/tabs/components/InvokeOptionsPanel';
import BoundingBoxSettings from 'features/options/components/AdvancedOptions/Canvas/BoundingBoxSettings/BoundingBoxSettings';
import InfillAndScalingOptions from 'features/options/components/AdvancedOptions/Canvas/InfillAndScalingOptions';
import { useTranslation } from 'react-i18next';
import { Flex } from '@chakra-ui/react';
import { NegativePromptInput } from 'features/options/components/PromptInput/NegativePromptInput';
export default function UnifiedCanvasPanel() {
const { t } = useTranslation();
@ -48,7 +50,10 @@ export default function UnifiedCanvasPanel() {
return (
<InvokeOptionsPanel>
<PromptInput />
<Flex flexDir="column" rowGap="0.5rem">
<PromptInput />
<NegativePromptInput />
</Flex>
<ProcessButtons />
<MainOptions />
<ImageToImageStrength

View File

@ -211,7 +211,7 @@ class Generate:
print('>> xformers memory-efficient attention is available but disabled')
else:
print('>> xformers not installed')
# model caching system for fast switching
self.model_manager = ModelManager(mconfig,self.device,self.precision,max_loaded_models=max_loaded_models)
# don't accept invalid models
@ -344,6 +344,7 @@ class Generate:
**args,
): # eat up additional cruft
self.clear_cuda_stats()
"""
ldm.generate.prompt2image() is the common entry point for txt2img() and img2img()
It takes the following arguments:
@ -548,6 +549,7 @@ class Generate:
inpaint_width = inpaint_width,
enable_image_debugging = enable_image_debugging,
free_gpu_mem=self.free_gpu_mem,
clear_cuda_cache=self.clear_cuda_cache
)
if init_color:
@ -565,36 +567,62 @@ class Generate:
image_callback = image_callback)
except KeyboardInterrupt:
# Clear the CUDA cache on an exception
self.clear_cuda_cache()
if catch_interrupts:
print('**Interrupted** Partial results will be returned.')
else:
raise KeyboardInterrupt
except RuntimeError:
# Clear the CUDA cache on an exception
self.clear_cuda_cache()
print(traceback.format_exc(), file=sys.stderr)
print('>> Could not generate image.')
toc = time.time()
print('>> Usage stats:')
print('\n>> Usage stats:')
print(
f'>> {len(results)} image(s) generated in', '%4.2fs' % (
toc - tic)
)
self.print_cuda_stats()
return results
def clear_cuda_cache(self):
if self._has_cuda():
self.max_memory_allocated = max(
self.max_memory_allocated,
torch.cuda.max_memory_allocated()
)
self.memory_allocated = max(
self.memory_allocated,
torch.cuda.memory_allocated()
)
self.session_peakmem = max(
self.session_peakmem,
torch.cuda.max_memory_allocated()
)
torch.cuda.empty_cache()
def clear_cuda_stats(self):
self.max_memory_allocated = 0
self.memory_allocated = 0
def print_cuda_stats(self):
if self._has_cuda():
print(
'>> Max VRAM used for this generation:',
'%4.2fG.' % (torch.cuda.max_memory_allocated() / 1e9),
'%4.2fG.' % (self.max_memory_allocated / 1e9),
'Current VRAM utilization:',
'%4.2fG' % (torch.cuda.memory_allocated() / 1e9),
'%4.2fG' % (self.memory_allocated / 1e9),
)
self.session_peakmem = max(
self.session_peakmem, torch.cuda.max_memory_allocated()
)
print(
'>> Max VRAM used since script start: ',
'%4.2fG' % (self.session_peakmem / 1e9),
)
return results
# this needs to be generalized to all sorts of postprocessors, which should be wrapped
# in a nice harmonized call signature. For now we have a bunch of if/elses!
@ -847,7 +875,7 @@ class Generate:
# the model cache does the loading and offloading
cache = self.model_manager
if not cache.valid_model(model_name):
raise KeyError('** "{model_name}" is not a known model name. Cannot change.')
raise KeyError(f'** "{model_name}" is not a known model name. Cannot change.')
cache.print_vram_usage()

View File

@ -4,6 +4,13 @@ import sys
import shlex
import traceback
from argparse import Namespace
from pathlib import Path
from typing import Optional, Union
if sys.platform == "darwin":
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
from ldm.invoke.globals import Globals
from ldm.generate import Generate
from ldm.invoke.prompt_parser import PromptParser
@ -13,17 +20,14 @@ from ldm.invoke.pngwriter import PngWriter, retrieve_metadata, write_metadata
from ldm.invoke.image_util import make_grid
from ldm.invoke.log import write_log
from ldm.invoke.model_manager import ModelManager
from pathlib import Path
from argparse import Namespace
import pyparsing
import click # type: ignore
import ldm.invoke
import pyparsing # type: ignore
# global used in multiple functions (fix)
infile = None
if sys.platform == 'darwin':
os.environ["PYTORCH_ENABLE_MPS_FALLBACK"] = "1"
def main():
"""Initialize command-line parsers and the diffusion model"""
global infile
@ -50,7 +54,7 @@ def main():
Globals.internet_available = args.internet_available and check_internet()
Globals.disable_xformers = not args.xformers
Globals.ckpt_convert = args.ckpt_convert
print(f'>> Internet connectivity is {Globals.internet_available}')
if not args.conf:
@ -69,8 +73,10 @@ def main():
# these two lines prevent a horrible warning message from appearing
# when the frozen CLIP tokenizer is imported
import transformers
import transformers # type: ignore
transformers.logging.set_verbosity_error()
import diffusers
diffusers.logging.set_verbosity_error()
# Loading Face Restoration and ESRGAN Modules
gfpgan,codeformer,esrgan = load_face_restoration(opt)
@ -570,7 +576,7 @@ def set_default_output_dir(opt:Args, completer:Completer):
completer.set_default_dir(opt.outdir)
def import_model(model_path:str, gen, opt, completer):
def import_model(model_path: str, gen, opt, completer):
'''
model_path can be (1) a URL to a .ckpt file; (2) a local .ckpt file path; or
(3) a huggingface repository id
@ -579,12 +585,28 @@ def import_model(model_path:str, gen, opt, completer):
if model_path.startswith(('http:','https:','ftp:')):
model_name = import_ckpt_model(model_path, gen, opt, completer)
elif os.path.exists(model_path) and model_path.endswith(('.ckpt','.safetensors')) and os.path.isfile(model_path):
model_name = import_ckpt_model(model_path, gen, opt, completer)
elif re.match('^[\w.+-]+/[\w.+-]+$',model_path):
model_name = import_diffuser_model(model_path, gen, opt, completer)
elif os.path.isdir(model_path):
model_name = import_diffuser_model(Path(model_path), gen, opt, completer)
# Allow for a directory containing multiple models.
models = list(Path(model_path).rglob('*.ckpt')) + list(Path(model_path).rglob('*.safetensors'))
if models:
# Only the last model name will be used below.
for model in sorted(models):
if click.confirm(f'Import {model.stem} ?', default=True):
model_name = import_ckpt_model(model, gen, opt, completer)
print()
else:
model_name = import_diffuser_model(Path(model_path), gen, opt, completer)
elif re.match(r'^[\w.+-]+/[\w.+-]+$', model_path):
model_name = import_diffuser_model(model_path, gen, opt, completer)
else:
print(f'** {model_path} is neither the path to a .ckpt file nor a diffusers repository id. Can\'t import.')
@ -602,7 +624,7 @@ def import_model(model_path:str, gen, opt, completer):
completer.update_models(gen.model_manager.list_models())
print(f'>> {model_name} successfully installed')
def import_diffuser_model(path_or_repo:str, gen, opt, completer)->str:
def import_diffuser_model(path_or_repo: Union[Path, str], gen, _, completer) -> Optional[str]:
manager = gen.model_manager
default_name = Path(path_or_repo).stem
default_description = f'Imported model {default_name}'
@ -625,7 +647,7 @@ def import_diffuser_model(path_or_repo:str, gen, opt, completer)->str:
return None
return model_name
def import_ckpt_model(path_or_url:str, gen, opt, completer)->str:
def import_ckpt_model(path_or_url: Union[Path, str], gen, opt, completer) -> Optional[str]:
manager = gen.model_manager
default_name = Path(path_or_url).stem
default_description = f'Imported model {default_name}'
@ -1111,9 +1133,13 @@ def write_commands(opt, file_path:str, outfilepath:str):
def report_model_error(opt:Namespace, e:Exception):
print(f'** An error occurred while attempting to initialize the model: "{str(e)}"')
print('** This can be caused by a missing or corrupted models file, and can sometimes be fixed by (re)installing the models.')
response = input('Do you want to run invokeai-configure script to select and/or reinstall models? [y] ')
if response.startswith(('n','N')):
return
yes_to_all = os.environ.get('INVOKE_MODEL_RECONFIGURE')
if yes_to_all:
print('** Reconfiguration is being forced by environment variable INVOKE_MODEL_RECONFIGURE')
else:
response = input('Do you want to run invokeai-configure script to select and/or reinstall models? [y] ')
if response.startswith(('n', 'N')):
return
print('invokeai-configure is launching....\n')
@ -1121,16 +1147,16 @@ def report_model_error(opt:Namespace, e:Exception):
# only the arguments accepted by the configuration script are parsed
root_dir = ["--root", opt.root_dir] if opt.root_dir is not None else []
config = ["--config", opt.conf] if opt.conf is not None else []
yes_to_all = os.environ.get('INVOKE_MODEL_RECONFIGURE')
previous_args = sys.argv
sys.argv = [ 'invokeai-configure' ]
sys.argv.extend(root_dir)
sys.argv.extend(config)
if yes_to_all is not None:
sys.argv.append(yes_to_all)
for arg in yes_to_all.split():
sys.argv.append(arg)
from ldm.invoke.config import configure_invokeai
configure_invokeai.main()
from ldm.invoke.config import invokeai_configure
invokeai_configure.main()
print('** InvokeAI will now restart')
sys.argv = previous_args
main() # would rather do a os.exec(), but doesn't exist?

View File

@ -1 +1 @@
__version__='2.3.0-rc2'
__version__='2.3.0-rc4'

View File

@ -196,6 +196,7 @@ class Args(object):
elif os.path.exists(legacyinit):
print(f'>> WARNING: Old initialization file found at {legacyinit}. This location is deprecated. Please move it to {Globals.root}/invokeai.init.')
sysargs.insert(0,f'@{legacyinit}')
Globals.log_tokenization = self._arg_parser.parse_args(sysargs).log_tokenization
self._arg_switches = self._arg_parser.parse_args(sysargs)
return self._arg_switches
@ -599,6 +600,12 @@ class Args(object):
help=f'Set the default sampler. Supported samplers: {", ".join(SAMPLER_CHOICES)}',
default='k_lms',
)
render_group.add_argument(
'--log_tokenization',
'-t',
action='store_true',
help='shows how the prompt is split into tokens'
)
render_group.add_argument(
'-f',
'--strength',
@ -744,7 +751,7 @@ class Args(object):
invoke> !fetch 0000015.8929913.png
invoke> a fantastic alien landscape -W 576 -H 512 -s 60 -A plms -C 7.5
invoke> !fetch /path/to/images/*.png prompts.txt
!replay /path/to/prompts.txt
Replays all the prompts contained in the file prompts.txt.
@ -756,6 +763,7 @@ class Args(object):
!models -- list models in configs/models.yaml
!switch <model_name> -- switch to model named <model_name>
!import_model /path/to/weights/file.ckpt -- adds a .ckpt model to your config
!import_model /path/to/weights/ -- interactively import models from a directory
!import_model http://path_to_model.ckpt -- downloads and adds a .ckpt model to your config
!import_model hakurei/waifu-diffusion -- downloads and adds a diffusers model to your config
!optimize_model <model_name> -- converts a .ckpt model to a diffusers model

View File

@ -20,6 +20,7 @@
import os
import re
import torch
import warnings
from pathlib import Path
from ldm.invoke.globals import Globals, global_cache_dir
from safetensors.torch import load_file
@ -44,6 +45,7 @@ from diffusers import (
PNDMScheduler,
StableDiffusionPipeline,
UNet2DConditionModel,
logging as dlogging,
)
from diffusers.pipelines.latent_diffusion.pipeline_latent_diffusion import LDMBertConfig, LDMBertModel
from diffusers.pipelines.paint_by_example import PaintByExampleImageEncoder, PaintByExamplePipeline
@ -795,8 +797,9 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
prediction_type:str=None,
extract_ema:bool=True,
upcast_attn:bool=False,
vae:AutoencoderKL=None
)->StableDiffusionGeneratorPipeline:
vae:AutoencoderKL=None,
return_generator_pipeline:bool=False,
)->Union[StableDiffusionPipeline,StableDiffusionGeneratorPipeline]:
'''
Load a Stable Diffusion pipeline object from a CompVis-style `.ckpt`/`.safetensors` file and (ideally) a `.yaml`
config file.
@ -823,166 +826,173 @@ def load_pipeline_from_original_stable_diffusion_ckpt(
:param upcast_attention: Whether the attention computation should always be upcasted. This is necessary when
running stable diffusion 2.1.
'''
with warnings.catch_warnings():
warnings.simplefilter('ignore')
verbosity = dlogging.get_verbosity()
dlogging.set_verbosity_error()
checkpoint = load_file(checkpoint_path) if Path(checkpoint_path).suffix == '.safetensors' else torch.load(checkpoint_path)
cache_dir = global_cache_dir('hub')
checkpoint = load_file(checkpoint_path) if Path(checkpoint_path).suffix == '.safetensors' else torch.load(checkpoint_path)
cache_dir = global_cache_dir('hub')
pipeline_class = StableDiffusionGeneratorPipeline if return_generator_pipeline else StableDiffusionPipeline
# Sometimes models don't have the global_step item
if "global_step" in checkpoint:
global_step = checkpoint["global_step"]
else:
print(" | global_step key not found in model")
global_step = None
# sometimes there is a state_dict key and sometimes not
if 'state_dict' in checkpoint:
checkpoint = checkpoint["state_dict"]
upcast_attention = False
if original_config_file is None:
key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight"
if key_name in checkpoint and checkpoint[key_name].shape[-1] == 1024:
original_config_file = os.path.join(Globals.root,'configs','stable-diffusion','v2-inference-v.yaml')
if global_step == 110000:
# v2.1 needs to upcast attention
upcast_attention = True
# Sometimes models don't have the global_step item
if "global_step" in checkpoint:
global_step = checkpoint["global_step"]
else:
original_config_file = os.path.join(Globals.root,'configs','stable-diffusion','v1-inference.yaml')
print(" | global_step key not found in model")
global_step = None
original_config = OmegaConf.load(original_config_file)
# sometimes there is a state_dict key and sometimes not
if 'state_dict' in checkpoint:
checkpoint = checkpoint["state_dict"]
if num_in_channels is not None:
original_config["model"]["params"]["unet_config"]["params"]["in_channels"] = num_in_channels
upcast_attention = False
if original_config_file is None:
key_name = "model.diffusion_model.input_blocks.2.1.transformer_blocks.0.attn2.to_k.weight"
if (
"parameterization" in original_config["model"]["params"]
and original_config["model"]["params"]["parameterization"] == "v"
):
if prediction_type is None:
# NOTE: For stable diffusion 2 base it is recommended to pass `prediction_type=="epsilon"`
# as it relies on a brittle global step parameter here
prediction_type = "epsilon" if global_step == 875000 else "v_prediction"
if image_size is None:
# NOTE: For stable diffusion 2 base one has to pass `image_size==512`
# as it relies on a brittle global step parameter here
image_size = 512 if global_step == 875000 else 768
else:
if prediction_type is None:
prediction_type = "epsilon"
if image_size is None:
image_size = 512
if key_name in checkpoint and checkpoint[key_name].shape[-1] == 1024:
original_config_file = os.path.join(Globals.root,'configs','stable-diffusion','v2-inference-v.yaml')
num_train_timesteps = original_config.model.params.timesteps
beta_start = original_config.model.params.linear_start
beta_end = original_config.model.params.linear_end
if global_step == 110000:
# v2.1 needs to upcast attention
upcast_attention = True
else:
original_config_file = os.path.join(Globals.root,'configs','stable-diffusion','v1-inference.yaml')
scheduler = DDIMScheduler(
beta_end=beta_end,
beta_schedule="scaled_linear",
beta_start=beta_start,
num_train_timesteps=num_train_timesteps,
steps_offset=1,
clip_sample=False,
set_alpha_to_one=False,
prediction_type=prediction_type,
)
# make sure scheduler works correctly with DDIM
scheduler.register_to_config(clip_sample=False)
original_config = OmegaConf.load(original_config_file)
if scheduler_type == "pndm":
config = dict(scheduler.config)
config["skip_prk_steps"] = True
scheduler = PNDMScheduler.from_config(config)
elif scheduler_type == "lms":
scheduler = LMSDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "heun":
scheduler = HeunDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "euler":
scheduler = EulerDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "euler-ancestral":
scheduler = EulerAncestralDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "dpm":
scheduler = DPMSolverMultistepScheduler.from_config(scheduler.config)
elif scheduler_type == "ddim":
scheduler = scheduler
else:
raise ValueError(f"Scheduler of type {scheduler_type} doesn't exist!")
if num_in_channels is not None:
original_config["model"]["params"]["unet_config"]["params"]["in_channels"] = num_in_channels
# Convert the UNet2DConditionModel model.
unet_config = create_unet_diffusers_config(original_config, image_size=image_size)
unet_config["upcast_attention"] = upcast_attention
unet = UNet2DConditionModel(**unet_config)
if (
"parameterization" in original_config["model"]["params"]
and original_config["model"]["params"]["parameterization"] == "v"
):
if prediction_type is None:
# NOTE: For stable diffusion 2 base it is recommended to pass `prediction_type=="epsilon"`
# as it relies on a brittle global step parameter here
prediction_type = "epsilon" if global_step == 875000 else "v_prediction"
if image_size is None:
# NOTE: For stable diffusion 2 base one has to pass `image_size==512`
# as it relies on a brittle global step parameter here
image_size = 512 if global_step == 875000 else 768
else:
if prediction_type is None:
prediction_type = "epsilon"
if image_size is None:
image_size = 512
converted_unet_checkpoint = convert_ldm_unet_checkpoint(
checkpoint, unet_config, path=checkpoint_path, extract_ema=extract_ema
)
num_train_timesteps = original_config.model.params.timesteps
beta_start = original_config.model.params.linear_start
beta_end = original_config.model.params.linear_end
unet.load_state_dict(converted_unet_checkpoint)
# Convert the VAE model, or use the one passed
if not vae:
print(f' | Using checkpoint model\'s original VAE')
vae_config = create_vae_diffusers_config(original_config, image_size=image_size)
converted_vae_checkpoint = convert_ldm_vae_checkpoint(checkpoint, vae_config)
vae = AutoencoderKL(**vae_config)
vae.load_state_dict(converted_vae_checkpoint)
else:
print(f' | Using external VAE specified in config')
# Convert the text model.
model_type = pipeline_type
if model_type is None:
model_type = original_config.model.params.cond_stage_config.target.split(".")[-1]
if model_type == "FrozenOpenCLIPEmbedder":
text_model = convert_open_clip_checkpoint(checkpoint)
tokenizer = CLIPTokenizer.from_pretrained("stabilityai/stable-diffusion-2",
subfolder="tokenizer",
cache_dir=global_cache_dir('diffusers')
)
pipe = StableDiffusionGeneratorPipeline(
vae=vae,
text_encoder=text_model,
tokenizer=tokenizer,
unet=unet,
scheduler=scheduler,
safety_checker=None,
feature_extractor=None,
requires_safety_checker=False,
scheduler = DDIMScheduler(
beta_end=beta_end,
beta_schedule="scaled_linear",
beta_start=beta_start,
num_train_timesteps=num_train_timesteps,
steps_offset=1,
clip_sample=False,
set_alpha_to_one=False,
prediction_type=prediction_type,
)
elif model_type == "PaintByExample":
vision_model = convert_paint_by_example_checkpoint(checkpoint)
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir)
feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir)
pipe = PaintByExamplePipeline(
vae=vae,
image_encoder=vision_model,
unet=unet,
scheduler=scheduler,
safety_checker=None,
feature_extractor=feature_extractor,
# make sure scheduler works correctly with DDIM
scheduler.register_to_config(clip_sample=False)
if scheduler_type == "pndm":
config = dict(scheduler.config)
config["skip_prk_steps"] = True
scheduler = PNDMScheduler.from_config(config)
elif scheduler_type == "lms":
scheduler = LMSDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "heun":
scheduler = HeunDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "euler":
scheduler = EulerDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "euler-ancestral":
scheduler = EulerAncestralDiscreteScheduler.from_config(scheduler.config)
elif scheduler_type == "dpm":
scheduler = DPMSolverMultistepScheduler.from_config(scheduler.config)
elif scheduler_type == "ddim":
scheduler = scheduler
else:
raise ValueError(f"Scheduler of type {scheduler_type} doesn't exist!")
# Convert the UNet2DConditionModel model.
unet_config = create_unet_diffusers_config(original_config, image_size=image_size)
unet_config["upcast_attention"] = upcast_attention
unet = UNet2DConditionModel(**unet_config)
converted_unet_checkpoint = convert_ldm_unet_checkpoint(
checkpoint, unet_config, path=checkpoint_path, extract_ema=extract_ema
)
elif model_type in ['FrozenCLIPEmbedder','WeightedFrozenCLIPEmbedder']:
text_model = convert_ldm_clip_checkpoint(checkpoint)
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir)
feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir)
pipe = StableDiffusionGeneratorPipeline(
vae=vae,
text_encoder=text_model,
tokenizer=tokenizer,
unet=unet,
scheduler=scheduler,
safety_checker=None,
feature_extractor=feature_extractor,
)
else:
text_config = create_ldm_bert_config(original_config)
text_model = convert_ldm_bert_checkpoint(checkpoint, text_config)
tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased",cache_dir=cache_dir)
pipe = LDMTextToImagePipeline(vqvae=vae, bert=text_model, tokenizer=tokenizer, unet=unet, scheduler=scheduler)
unet.load_state_dict(converted_unet_checkpoint)
# Convert the VAE model, or use the one passed
if not vae:
print(' | Using checkpoint model\'s original VAE')
vae_config = create_vae_diffusers_config(original_config, image_size=image_size)
converted_vae_checkpoint = convert_ldm_vae_checkpoint(checkpoint, vae_config)
vae = AutoencoderKL(**vae_config)
vae.load_state_dict(converted_vae_checkpoint)
else:
print(' | Using external VAE specified in config')
# Convert the text model.
model_type = pipeline_type
if model_type is None:
model_type = original_config.model.params.cond_stage_config.target.split(".")[-1]
if model_type == "FrozenOpenCLIPEmbedder":
text_model = convert_open_clip_checkpoint(checkpoint)
tokenizer = CLIPTokenizer.from_pretrained("stabilityai/stable-diffusion-2",
subfolder="tokenizer",
cache_dir=global_cache_dir('diffusers')
)
pipe = pipeline_class(
vae=vae,
text_encoder=text_model,
tokenizer=tokenizer,
unet=unet,
scheduler=scheduler,
safety_checker=None,
feature_extractor=None,
requires_safety_checker=False,
)
elif model_type == "PaintByExample":
vision_model = convert_paint_by_example_checkpoint(checkpoint)
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir)
feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir)
pipe = PaintByExamplePipeline(
vae=vae,
image_encoder=vision_model,
unet=unet,
scheduler=scheduler,
safety_checker=None,
feature_extractor=feature_extractor,
)
elif model_type in ['FrozenCLIPEmbedder','WeightedFrozenCLIPEmbedder']:
text_model = convert_ldm_clip_checkpoint(checkpoint)
tokenizer = CLIPTokenizer.from_pretrained("openai/clip-vit-large-patch14",cache_dir=cache_dir)
feature_extractor = AutoFeatureExtractor.from_pretrained("CompVis/stable-diffusion-safety-checker",cache_dir=cache_dir)
pipe = pipeline_class(
vae=vae,
text_encoder=text_model,
tokenizer=tokenizer,
unet=unet,
scheduler=scheduler,
safety_checker=None,
feature_extractor=feature_extractor,
)
else:
text_config = create_ldm_bert_config(original_config)
text_model = convert_ldm_bert_checkpoint(checkpoint, text_config)
tokenizer = BertTokenizerFast.from_pretrained("bert-base-uncased",cache_dir=cache_dir)
pipe = LDMTextToImagePipeline(vqvae=vae, bert=text_model, tokenizer=tokenizer, unet=unet, scheduler=scheduler)
dlogging.set_verbosity(verbosity)
return pipe
@ -1000,6 +1010,7 @@ def convert_ckpt_to_diffuser(
checkpoint_path,
**kwargs
)
pipe.save_pretrained(
dump_path,
safe_serialization=is_safetensors_available(),

View File

@ -17,6 +17,7 @@ from ..models.diffusion import cross_attention_control
from ..models.diffusion.shared_invokeai_diffusion import InvokeAIDiffuserComponent
from ..modules.encoders.modules import WeightedFrozenCLIPEmbedder
from ..modules.prompt_to_embeddings_converter import WeightedPromptFragmentsToEmbeddingsConverter
from ldm.invoke.globals import Globals
def get_uc_and_c_and_ec(prompt_string, model, log_tokens=False, skip_normalize_legacy_blend=False):
@ -92,9 +93,9 @@ def _get_conditioning_for_prompt(parsed_prompt: Union[Blend, FlattenedPrompt], p
Process prompt structure and tokens, and return (conditioning, unconditioning, extra_conditioning_info)
"""
if log_tokens:
print(f">> Parsed prompt to {parsed_prompt}")
print(f">> Parsed negative prompt to {parsed_negative_prompt}")
if log_tokens or Globals.log_tokenization:
print(f"\n>> [TOKENLOG] Parsed Prompt: {parsed_prompt}")
print(f"\n>> [TOKENLOG] Parsed Negative Prompt: {parsed_negative_prompt}")
conditioning = None
cac_args: cross_attention_control.Arguments = None
@ -235,7 +236,7 @@ def _get_embeddings_and_tokens_for_prompt(model, flattened_prompt: FlattenedProm
fragments = [x.text for x in flattened_prompt.children]
weights = [x.weight for x in flattened_prompt.children]
embeddings, tokens = model.get_learned_conditioning([fragments], return_tokens=True, fragment_weights=[weights])
if log_tokens:
if log_tokens or Globals.log_tokenization:
text = " ".join(fragments)
log_tokenization(text, model, display_label=log_display_label)
@ -273,12 +274,12 @@ def log_tokenization(text, model, display_label=None):
# usually tokens have '</w>' to indicate end-of-word,
# but for readability it has been replaced with ' '
"""
tokens = model.cond_stage_model.tokenizer.tokenize(text)
tokenized = ""
discarded = ""
usedTokens = 0
totalTokens = len(tokens)
for i in range(0, totalTokens):
token = tokens[i].replace('</w>', ' ')
# alternate color
@ -288,8 +289,11 @@ def log_tokenization(text, model, display_label=None):
usedTokens += 1
else: # over max token length
discarded = discarded + f"\x1b[0;3{s};40m{token}"
print(f"\n>> Tokens {display_label or ''} ({usedTokens}):\n{tokenized}\x1b[0m")
if usedTokens > 0:
print(f'\n>> [TOKENLOG] Tokens {display_label or ""} ({usedTokens}):')
print(f'{tokenized}\x1b[0m')
if discarded != "":
print(
f">> Tokens Discarded ({totalTokens - usedTokens}):\n{discarded}\x1b[0m"
)
print(f'\n>> [TOKENLOG] Tokens Discarded ({totalTokens - usedTokens}):')
print(f'{discarded}\x1b[0m')

View File

@ -127,8 +127,8 @@ script do it for you. Manual installation is described at:
https://invoke-ai.github.io/InvokeAI/installation/020_INSTALL_MANUAL/
You may download the recommended models (about 10GB total), select a customized set, or
completely skip this step.
You may download the recommended models (about 15GB total), install all models (40 GB!!)
select a customized set, or completely skip this step.
"""
)
completer.set_options(["recommended", "customized", "skip"])
@ -320,6 +320,8 @@ You may re-run the configuration script again in the future if you do not wish t
while again:
try:
access_token = getpass_asterisk.getpass_asterisk(prompt="HF Token ")
if access_token is None or len(access_token)==0:
raise EOFError
HfLogin(access_token)
access_token = HfFolder.get_token()
again = False
@ -433,9 +435,7 @@ def _download_diffusion_weights(
)
except OSError as e:
if str(e).startswith("fp16 is not a valid"):
print(
f"Could not fetch half-precision version of model {repo_id}; fetching full-precision instead"
)
pass
else:
print(f"An unexpected error occurred while downloading the model: {e})")
if path:
@ -866,7 +866,7 @@ def initialize_rootdir(root: str, yes_to_all: bool = False):
):
os.makedirs(os.path.join(root, name), exist_ok=True)
configs_src = Path(configs.__path__[-1])
configs_src = Path(configs.__path__[0])
configs_dest = Path(root) / "configs"
if not os.path.samefile(configs_src, configs_dest):
shutil.copytree(configs_src, configs_dest, dirs_exist_ok=True)

View File

@ -122,6 +122,11 @@ class Generator:
seed = self.new_seed()
# Free up memory from the last generation.
clear_cuda_cache = kwargs['clear_cuda_cache'] or None
if clear_cuda_cache is not None:
clear_cuda_cache()
return results
def sample_to_image(self,samples)->Image.Image:
@ -240,7 +245,12 @@ class Generator:
def get_perlin_noise(self,width,height):
fixdevice = 'cpu' if (self.model.device.type == 'mps') else self.model.device
noise = torch.stack([rand_perlin_2d((height, width), (8, 8), device = self.model.device).to(fixdevice) for _ in range(self.latent_channels)], dim=0).to(self.model.device)
# limit noise to only the diffusion image channels, not the mask channels
input_channels = min(self.latent_channels, 4)
noise = torch.stack([
rand_perlin_2d((height, width),
(8, 8),
device = self.model.device).to(fixdevice) for _ in range(input_channels)], dim=0).to(self.model.device)
return noise
def new_seed(self):
@ -341,3 +351,27 @@ class Generator:
def torch_dtype(self)->torch.dtype:
return torch.float16 if self.precision == 'float16' else torch.float32
# returns a tensor filled with random numbers from a normal distribution
def get_noise(self,width,height):
device = self.model.device
# limit noise to only the diffusion image channels, not the mask channels
input_channels = min(self.latent_channels, 4)
if self.use_mps_noise or device.type == 'mps':
x = torch.randn([1,
input_channels,
height // self.downsampling_factor,
width // self.downsampling_factor],
dtype=self.torch_dtype(),
device='cpu').to(device)
else:
x = torch.randn([1,
input_channels,
height // self.downsampling_factor,
width // self.downsampling_factor],
dtype=self.torch_dtype(),
device=device)
if self.perlin > 0.0:
perlin_noise = self.get_perlin_noise(width // self.downsampling_factor, height // self.downsampling_factor)
x = (1-self.perlin)*x + self.perlin*perlin_noise
return x

View File

@ -4,7 +4,6 @@ import dataclasses
import inspect
import secrets
import sys
import warnings
from dataclasses import dataclass, field
from typing import List, Optional, Union, Callable, Type, TypeVar, Generic, Any
@ -641,7 +640,6 @@ class StableDiffusionGeneratorPipeline(StableDiffusionPipeline):
@property
def cond_stage_model(self):
warnings.warn("legacy compatibility layer", DeprecationWarning)
return self.prompt_fragments_to_embeddings_converter
@torch.inference_mode()

View File

@ -63,22 +63,3 @@ class Img2Img(Generator):
shape = like.shape
x = (1-self.perlin)*x + self.perlin*self.get_perlin_noise(shape[3], shape[2])
return x
def get_noise(self,width,height):
# copy of the Txt2Img.get_noise
device = self.model.device
if self.use_mps_noise or device.type == 'mps':
x = torch.randn([1,
self.latent_channels,
height // self.downsampling_factor,
width // self.downsampling_factor],
device='cpu').to(device)
else:
x = torch.randn([1,
self.latent_channels,
height // self.downsampling_factor,
width // self.downsampling_factor],
device=device)
if self.perlin > 0.0:
x = (1-self.perlin)*x + self.perlin*self.get_perlin_noise(width // self.downsampling_factor, height // self.downsampling_factor)
return x

View File

@ -194,7 +194,8 @@ class Inpaint(Img2Img):
"""
self.enable_image_debugging = enable_image_debugging
self.infill_method = infill_method or infill_methods()[0], # The infill method to use
infill_method = infill_method or infill_methods()[0]
self.infill_method = infill_method
self.inpaint_width = inpaint_width
self.inpaint_height = inpaint_height

View File

@ -51,26 +51,4 @@ class Txt2Img(Generator):
return make_image
# returns a tensor filled with random numbers from a normal distribution
def get_noise(self,width,height):
device = self.model.device
# limit noise to only the diffusion image channels, not the mask channels
input_channels = min(self.latent_channels, 4)
if self.use_mps_noise or device.type == 'mps':
x = torch.randn([1,
input_channels,
height // self.downsampling_factor,
width // self.downsampling_factor],
dtype=self.torch_dtype(),
device='cpu').to(device)
else:
x = torch.randn([1,
input_channels,
height // self.downsampling_factor,
width // self.downsampling_factor],
dtype=self.torch_dtype(),
device=device)
if self.perlin > 0.0:
x = (1-self.perlin)*x + self.perlin*self.get_perlin_noise(width // self.downsampling_factor, height // self.downsampling_factor)
return x

View File

@ -65,6 +65,11 @@ class Txt2Img2Img(Generator):
mode="bilinear"
)
# Free up memory from the last generation.
clear_cuda_cache = kwargs['clear_cuda_cache'] or None
if clear_cuda_cache is not None:
clear_cuda_cache()
second_pass_noise = self.get_noise_like(resized_latents)
verbosity = get_verbosity()

View File

@ -8,26 +8,25 @@ import argparse
import curses
import os
import sys
import traceback
import warnings
from argparse import Namespace
from pathlib import Path
from typing import List, Union
import npyscreen
import warnings
from diffusers import DiffusionPipeline
from diffusers import logging as dlogging
from npyscreen import widget
from omegaconf import OmegaConf
from ldm.invoke.globals import (
Globals,
global_cache_dir,
global_config_file,
global_models_dir,
global_set_root,
)
from ldm.invoke.globals import (Globals, global_cache_dir, global_config_file,
global_models_dir, global_set_root)
from ldm.invoke.model_manager import ModelManager
DEST_MERGED_MODEL_DIR = "merged_models"
def merge_diffusion_models(
model_ids_or_paths: List[Union[str, Path]],
alpha: float = 0.5,
@ -46,18 +45,24 @@ def merge_diffusion_models(
**kwargs - the default DiffusionPipeline.get_config_dict kwargs:
cache_dir, resume_download, force_download, proxies, local_files_only, use_auth_token, revision, torch_dtype, device_map
"""
pipe = DiffusionPipeline.from_pretrained(
model_ids_or_paths[0],
cache_dir=kwargs.get("cache_dir", global_cache_dir()),
custom_pipeline="checkpoint_merger",
)
merged_pipe = pipe.merge(
pretrained_model_name_or_path_list=model_ids_or_paths,
alpha=alpha,
interp=interp,
force=force,
**kwargs,
)
with warnings.catch_warnings():
warnings.simplefilter("ignore")
verbosity = dlogging.get_verbosity()
dlogging.set_verbosity_error()
pipe = DiffusionPipeline.from_pretrained(
model_ids_or_paths[0],
cache_dir=kwargs.get("cache_dir", global_cache_dir()),
custom_pipeline="checkpoint_merger",
)
merged_pipe = pipe.merge(
pretrained_model_name_or_path_list=model_ids_or_paths,
alpha=alpha,
interp=interp,
force=force,
**kwargs,
)
dlogging.set_verbosity(verbosity)
return merged_pipe
@ -181,13 +186,12 @@ class FloatTitleSlider(npyscreen.TitleText):
class mergeModelsForm(npyscreen.FormMultiPageAction):
interpolations = ["weighted_sum", "sigmoid", "inv_sigmoid", "add_difference"]
def __init__(self, parentApp, name):
self.parentApp = parentApp
self.ALLOW_RESIZE=True
self.FIX_MINIMUM_SIZE_WHEN_CREATED=False
self.ALLOW_RESIZE = True
self.FIX_MINIMUM_SIZE_WHEN_CREATED = False
super().__init__(parentApp, name)
@property
@ -198,29 +202,29 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
self.parentApp.setNextForm(None)
def create(self):
window_height,window_width=curses.initscr().getmaxyx()
window_height, window_width = curses.initscr().getmaxyx()
self.model_names = self.get_model_names()
max_width = max([len(x) for x in self.model_names])
max_width += 6
horizontal_layout = max_width*3 < window_width
horizontal_layout = max_width * 3 < window_width
self.add_widget_intelligent(
npyscreen.FixedText,
color='CONTROL',
color="CONTROL",
value=f"Select two models to merge and optionally a third.",
editable=False,
)
self.add_widget_intelligent(
npyscreen.FixedText,
color='CONTROL',
color="CONTROL",
value=f"Use up and down arrows to move, <space> to select an item, <tab> and <shift-tab> to move from one field to the next.",
editable=False,
)
self.add_widget_intelligent(
npyscreen.FixedText,
value='MODEL 1',
color='GOOD',
value="MODEL 1",
color="GOOD",
editable=False,
rely=4 if horizontal_layout else None,
)
@ -235,57 +239,57 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
)
self.add_widget_intelligent(
npyscreen.FixedText,
value='MODEL 2',
color='GOOD',
value="MODEL 2",
color="GOOD",
editable=False,
relx=max_width+3 if horizontal_layout else None,
relx=max_width + 3 if horizontal_layout else None,
rely=4 if horizontal_layout else None,
)
self.model2 = self.add_widget_intelligent(
npyscreen.SelectOne,
name='(2)',
name="(2)",
values=self.model_names,
value=1,
max_height=len(self.model_names),
max_width=max_width,
relx=max_width+3 if horizontal_layout else None,
relx=max_width + 3 if horizontal_layout else None,
rely=5 if horizontal_layout else None,
scroll_exit=True,
)
self.add_widget_intelligent(
npyscreen.FixedText,
value='MODEL 3',
color='GOOD',
value="MODEL 3",
color="GOOD",
editable=False,
relx=max_width*2+3 if horizontal_layout else None,
relx=max_width * 2 + 3 if horizontal_layout else None,
rely=4 if horizontal_layout else None,
)
models_plus_none = self.model_names.copy()
models_plus_none.insert(0,'None')
models_plus_none.insert(0, "None")
self.model3 = self.add_widget_intelligent(
npyscreen.SelectOne,
name='(3)',
name="(3)",
values=models_plus_none,
value=0,
max_height=len(self.model_names)+1,
max_height=len(self.model_names) + 1,
max_width=max_width,
scroll_exit=True,
relx=max_width*2+3 if horizontal_layout else None,
relx=max_width * 2 + 3 if horizontal_layout else None,
rely=5 if horizontal_layout else None,
)
for m in [self.model1,self.model2,self.model3]:
for m in [self.model1, self.model2, self.model3]:
m.when_value_edited = self.models_changed
self.merged_model_name = self.add_widget_intelligent(
npyscreen.TitleText,
name="Name for merged model:",
labelColor='CONTROL',
labelColor="CONTROL",
value="",
scroll_exit=True,
)
self.force = self.add_widget_intelligent(
npyscreen.Checkbox,
name="Force merge of incompatible models",
labelColor='CONTROL',
labelColor="CONTROL",
value=False,
scroll_exit=True,
)
@ -294,7 +298,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
name="Merge Method:",
values=self.interpolations,
value=0,
labelColor='CONTROL',
labelColor="CONTROL",
max_height=len(self.interpolations) + 1,
scroll_exit=True,
)
@ -305,7 +309,7 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
step=0.05,
lowest=0,
value=0.5,
labelColor='CONTROL',
labelColor="CONTROL",
scroll_exit=True,
)
self.model1.editing = True
@ -315,43 +319,43 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
selected_model1 = self.model1.value[0]
selected_model2 = self.model2.value[0]
selected_model3 = self.model3.value[0]
merged_model_name = f'{models[selected_model1]}+{models[selected_model2]}'
merged_model_name = f"{models[selected_model1]}+{models[selected_model2]}"
self.merged_model_name.value = merged_model_name
if selected_model3 > 0:
self.merge_method.values=['add_difference'],
self.merged_model_name.value += f'+{models[selected_model3]}'
self.merge_method.values = (["add_difference"],)
self.merged_model_name.value += f"+{models[selected_model3]}"
else:
self.merge_method.values=self.interpolations
self.merge_method.value=0
self.merge_method.values = self.interpolations
self.merge_method.value = 0
def on_ok(self):
if self.validate_field_values() and self.check_for_overwrite():
self.parentApp.setNextForm(None)
self.editing = False
self.parentApp.merge_arguments = self.marshall_arguments()
npyscreen.notify('Starting the merge...')
npyscreen.notify("Starting the merge...")
else:
self.editing = True
def on_cancel(self):
sys.exit(0)
def marshall_arguments(self)->dict:
def marshall_arguments(self) -> dict:
model_names = self.model_names
models = [
model_names[self.model1.value[0]],
model_names[self.model2.value[0]],
]
]
if self.model3.value[0] > 0:
models.append(model_names[self.model3.value[0]-1])
models.append(model_names[self.model3.value[0] - 1])
args = dict(
models=models,
alpha = self.alpha.value,
interp = self.interpolations[self.merge_method.value[0]],
force = self.force.value,
merged_model_name = self.merged_model_name.value,
alpha=self.alpha.value,
interp=self.interpolations[self.merge_method.value[0]],
force=self.force.value,
merged_model_name=self.merged_model_name.value,
)
return args
@ -364,18 +368,22 @@ class mergeModelsForm(npyscreen.FormMultiPageAction):
f"The chosen merged model destination, {model_out}, is already in use. Overwrite?"
)
def validate_field_values(self)->bool:
def validate_field_values(self) -> bool:
bad_fields = []
model_names = self.model_names
selected_models = set((model_names[self.model1.value[0]],model_names[self.model2.value[0]]))
selected_models = set(
(model_names[self.model1.value[0]], model_names[self.model2.value[0]])
)
if self.model3.value[0] > 0:
selected_models.add(model_names[self.model3.value[0]-1])
selected_models.add(model_names[self.model3.value[0] - 1])
if len(selected_models) < 2:
bad_fields.append(f'Please select two or three DIFFERENT models to compare. You selected {selected_models}')
bad_fields.append(
f"Please select two or three DIFFERENT models to compare. You selected {selected_models}"
)
if len(bad_fields) > 0:
message = 'The following problems were detected and must be corrected:'
message = "The following problems were detected and must be corrected:"
for problem in bad_fields:
message += f'\n* {problem}'
message += f"\n* {problem}"
npyscreen.notify_confirm(message)
return False
else:
@ -403,6 +411,7 @@ class Mergeapp(npyscreen.NPSAppManaged):
npyscreen.setTheme(npyscreen.Themes.ElegantTheme)
self.main = self.addForm("MAIN", mergeModelsForm, name="Merge Models Settings")
def run_gui(args: Namespace):
mergeapp = Mergeapp()
mergeapp.run()
@ -443,22 +452,27 @@ def main():
] = cache_dir # because not clear the merge pipeline is honoring cache_dir
args.cache_dir = cache_dir
with warnings.catch_warnings():
warnings.simplefilter('ignore')
try:
if args.front_end:
run_gui(args)
else:
run_cli(args)
print(f'>> Conversion successful.')
except Exception as e:
if str(e).startswith('Not enough space'):
print('** Not enough horizontal space! Try making the window wider, or relaunch with a smaller starting size.')
else:
print(f"** An error occurred while merging the pipelines: {str(e)}")
sys.exit(-1)
except KeyboardInterrupt:
sys.exit(-1)
try:
if args.front_end:
run_gui(args)
else:
run_cli(args)
print(f">> Conversion successful. New model is named {args.merged_model_name}")
except widget.NotEnoughSpaceForWidget as e:
if str(e).startswith("Height of 1 allocated"):
print(
"** You need to have at least two diffusers models defined in models.yaml in order to merge"
)
else:
print(f"** A layout error has occurred: {str(e)}")
sys.exit(-1)
except Exception as e:
print(">> An error occurred:")
traceback.print_exc()
sys.exit(-1)
except KeyboardInterrupt:
sys.exit(-1)
if __name__ == "__main__":
main()

View File

@ -18,7 +18,7 @@ import warnings
import safetensors.torch
from pathlib import Path
from shutil import move, rmtree
from typing import Union, Any
from typing import Any, Optional, Union
from huggingface_hub import scan_cache_dir
from ldm.util import download_with_progress_bar
@ -356,6 +356,7 @@ class ModelManager(object):
checkpoint_path = weights,
original_config_file = config,
vae = vae,
return_generator_pipeline=True,
)
return (
pipeline.to(self.device).to(torch.float16 if self.precision == 'float16' else torch.float32),
@ -483,12 +484,11 @@ class ModelManager(object):
**pipeline_args,
**fp_args,
)
except OSError as e:
if str(e).startswith('fp16 is not a valid'):
print(f'Could not fetch half-precision version of model {name_or_path}; fetching full-precision instead')
pass
else:
print(f'An unexpected error occurred while downloading the model: {e})')
print(f'** An unexpected error occurred while downloading the model: {e})')
if pipeline:
break
@ -753,7 +753,7 @@ class ModelManager(object):
return search_folder, found_models
def _choose_diffusers_vae(self, model_name:str, vae:str=None)->Union[dict,str]:
# In the event that the original entry is using a custom ckpt VAE, we try to
# map that VAE onto a diffuser VAE using a hard-coded dictionary.
# I would prefer to do this differently: We load the ckpt model into memory, swap the
@ -880,14 +880,14 @@ class ModelManager(object):
print('** Migration is done. Continuing...')
def _resolve_path(self, source:Union[str,Path], dest_directory:str)->Path:
def _resolve_path(self, source: Union[str, Path], dest_directory: str) -> Optional[Path]:
resolved_path = None
if source.startswith(('http:','https:','ftp:')):
if str(source).startswith(('http:','https:','ftp:')):
basename = os.path.basename(source)
if not os.path.isabs(dest_directory):
dest_directory = os.path.join(Globals.root,dest_directory)
dest = os.path.join(dest_directory,basename)
if download_with_progress_bar(source,dest):
if download_with_progress_bar(str(source), Path(dest)):
resolved_path = Path(dest)
else:
if not os.path.isabs(source):
@ -954,7 +954,7 @@ class ModelManager(object):
def _has_cuda(self) -> bool:
return self.device.type == 'cuda'
def _diffuser_sha256(self,name_or_path:Union[str, Path])->Union[str,bytes]:
def _diffuser_sha256(self,name_or_path:Union[str, Path],chunksize=4096)->Union[str,bytes]:
path = None
if isinstance(name_or_path,Path):
path = name_or_path
@ -976,7 +976,8 @@ class ModelManager(object):
for name in files:
count += 1
with open(os.path.join(root,name),'rb') as f:
sha.update(f.read())
while chunk := f.read(chunksize):
sha.update(chunk)
hash = sha.hexdigest()
toc = time.time()
print(f' | sha256 = {hash} ({count} files hashed in','%4.2fs)' % (toc - tic))
@ -1039,7 +1040,7 @@ class ModelManager(object):
vae = AutoencoderKL.from_pretrained(name_or_path, **vae_args, **fp_args)
except OSError as e:
if str(e).startswith('fp16 is not a valid'):
print(' | Half-precision version of model not available; fetching full-precision instead')
pass
else:
deferred_error = e
if vae:

View File

@ -17,6 +17,7 @@ from pathlib import Path
from typing import List, Tuple
import npyscreen
from npyscreen import widget
from omegaconf import OmegaConf
from ldm.invoke.globals import Globals, global_set_root
@ -295,7 +296,8 @@ class textualInversionForm(npyscreen.FormMultiPageAction):
for idx in range(len(model_names))
if "default" in conf[model_names[idx]]
]
return (model_names, defaults[0])
default = defaults[0] if len(defaults) > 0 else 0
return (model_names, default)
def marshall_arguments(self) -> dict:
args = dict()
@ -437,11 +439,20 @@ def main():
do_front_end(args)
else:
do_textual_inversion_training(**vars(args))
except widget.NotEnoughSpaceForWidget as e:
if str(e).startswith("Height of 1 allocated"):
print(
"** You need to have at least one diffusers models defined in models.yaml in order to train"
)
else:
print(f"** A layout error has occurred: {str(e)}")
sys.exit(-1)
except AssertionError as e:
print(str(e))
sys.exit(-1)
except KeyboardInterrupt:
pass
if __name__ == "__main__":
main()

View File

@ -101,10 +101,9 @@ class Txt2Mask(object):
image = ImageOps.exif_transpose(image)
img = self._scale_and_crop(image)
img = transform(img).unsqueeze(0)
inputs = self.processor(text=[prompt],
images=[image],
images=[img],
padding=True,
return_tensors='pt')
outputs = self.model(**inputs)

View File

@ -284,9 +284,9 @@ class ProgressBar():
def download_with_progress_bar(url:str, dest:Path)->bool:
try:
if not os.path.exists(dest):
os.makedirs((os.path.dirname(dest) or '.'), exist_ok=True)
request.urlretrieve(url,dest,ProgressBar(os.path.basename(dest)))
if not dest.exists():
dest.parent.mkdir(parents=True, exist_ok=True)
request.urlretrieve(url,dest,ProgressBar(dest.stem))
return True
else:
return True

View File

@ -36,6 +36,7 @@ classifiers = [
dependencies = [
"accelerate",
"albumentations",
"click",
"clip_anytorch", # replacing "clip @ https://github.com/openai/CLIP/archive/eaa22acb90a5876642d0507623e859909230a52d.zip",
"datasets",
"diffusers[torch]~=0.11",
@ -98,13 +99,13 @@ dependencies = [
# legacy entrypoints; provided for backwards compatibility
"invoke.py" = "ldm.invoke.CLI:main"
"configure_invokeai.py" = "ldm.invoke.config.configure_invokeai:main"
"configure_invokeai.py" = "ldm.invoke.config.invokeai_configure:main"
"textual_inversion.py" = "ldm.invoke.training.textual_inversion:main"
"merge_embeddings.py" = "ldm.invoke.merge_diffusers:main"
# modern entrypoints
"invokeai" = "ldm.invoke.CLI:main"
"invokeai-configure" = "ldm.invoke.config.configure_invokeai:main"
"invokeai-configure" = "ldm.invoke.config.invokeai_configure:main"
"invokeai-merge" = "ldm.invoke.merge_diffusers:main" # note name munging
"invokeai-ti" = "ldm.invoke.training.textual_inversion:main"

View File

@ -2,8 +2,8 @@
# Copyright (c) 2022 Lincoln D. Stein (https://github.com/lstein)
import warnings
from ldm.invoke.config import configure_invokeai
from ldm.invoke.config import invokeai_configure
if __name__ == '__main__':
warnings.warn("configire_invokeai.py is deprecated, please run 'invoke'", DeprecationWarning)
warnings.warn("configure_invokeai.py is deprecated, please run 'invokai-configure'", DeprecationWarning)
configure_invokeai.main()

View File

@ -1,7 +1,4 @@
#!/usr/bin/env python
import sys
import os
import ldm.invoke.CLI
ldm.invoke.CLI.main()

27
scripts/pypi_helper.py Normal file
View File

@ -0,0 +1,27 @@
import requests
from ldm.invoke import __app_name__, __version__
local_version = str(__version__).replace("-", "")
package_name = str(__app_name__)
def get_pypi_versions(package_name=package_name) -> list[str]:
"""Get the versions of the package from PyPI"""
url = f"https://pypi.org/pypi/{package_name}/json"
response = requests.get(url).json()
versions: list[str] = list(response["releases"].keys())
return versions
def local_on_pypi(package_name=package_name, local_version=local_version) -> bool:
"""Compare the versions of the package from PyPI and the local package"""
pypi_versions = get_pypi_versions(package_name)
return local_version in pypi_versions
if __name__ == "__main__":
if local_on_pypi():
print(f"Package {package_name} is up to date")
else:
print(f"Package {package_name} is not up to date")