mirror of
https://github.com/invoke-ai/InvokeAI
synced 2024-08-30 20:32:17 +00:00
Merge branch 'main' of https://github.com/ParisNeo/ArtBot
This commit is contained in:
commit
89f69c2d94
@ -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
2
.github/CODEOWNERS
vendored
@ -4,4 +4,4 @@ scripts/legacy_api.py @CapableWeb
|
||||
tests/legacy_tests.sh @CapableWeb
|
||||
installer/ @ebr
|
||||
.github/workflows/ @mauwii
|
||||
docker_build/ @mauwii
|
||||
docker/ @mauwii
|
||||
|
88
.github/workflows/build-cloud-img.yml
vendored
88
.github/workflows/build-cloud-img.yml
vendored
@ -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 }}
|
37
.github/workflows/build-container.yml
vendored
37
.github/workflows/build-container.yml
vendored
@ -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
41
.github/workflows/pypi-release.yml
vendored
Normal 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/*
|
57
.github/workflows/test-invoke-pip.yml
vendored
57
.github/workflows/test-invoke-pip.yml
vendored
@ -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 }}
|
||||
|
@ -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" ]
|
@ -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"]
|
@ -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
|
@ -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
86
docker/Dockerfile
Normal 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"
|
@ -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
38
docker/env.sh
Normal 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,,}"
|
@ -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:+$@}
|
@ -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
8
installer/create_installer.sh
Executable file → Normal 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
25
installer/install.sh.in
Normal file → Executable 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 ${@}
|
||||
|
@ -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
|
||||
|
@ -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..
|
||||
|
@ -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:"
|
||||
|
@ -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
|
||||
|
@ -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
File diff suppressed because one or more lines are too long
6
invokeai/frontend/dist/index.html
vendored
6
invokeai/frontend/dist/index.html
vendored
@ -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>
|
||||
|
@ -21,6 +21,7 @@
|
||||
"langSpanish": "Spanish",
|
||||
"langJapanese": "Japanese",
|
||||
"langDutch": "Dutch",
|
||||
"langUkranian": "Ukranian",
|
||||
"text2img": "Text To Image",
|
||||
"img2img": "Image To Image",
|
||||
"unifiedCanvas": "Unified Canvas",
|
||||
|
@ -21,6 +21,7 @@
|
||||
"langSpanish": "Spanish",
|
||||
"langJapanese": "Japanese",
|
||||
"langDutch": "Dutch",
|
||||
"langUkranian": "Ukranian",
|
||||
"text2img": "Text To Image",
|
||||
"img2img": "Image To Image",
|
||||
"unifiedCanvas": "Unified Canvas",
|
||||
|
@ -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",
|
||||
|
53
invokeai/frontend/dist/locales/common/ua.json
vendored
Normal file
53
invokeai/frontend/dist/locales/common/ua.json
vendored
Normal 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": "Модель змінено"
|
||||
}
|
16
invokeai/frontend/dist/locales/gallery/ua.json
vendored
Normal file
16
invokeai/frontend/dist/locales/gallery/ua.json
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"generations": "Генерації",
|
||||
"showGenerations": "Показувати генерації",
|
||||
"uploads": "Завантаження",
|
||||
"showUploads": "Показувати завантаження",
|
||||
"galleryImageSize": "Розмір зображень",
|
||||
"galleryImageResetSize": "Аатоматичний розмір",
|
||||
"gallerySettings": "Налаштування галереї",
|
||||
"maintainAspectRatio": "Зберігати пропорції",
|
||||
"autoSwitchNewImages": "Автоматично вибирати нові",
|
||||
"singleColumnLayout": "Одна колонка",
|
||||
"pinGallery": "Закріпити галерею",
|
||||
"allImagesLoaded": "Всі зображення завантажені",
|
||||
"loadMore": "Завантажити більше",
|
||||
"noImagesInGallery": "Зображень немає"
|
||||
}
|
207
invokeai/frontend/dist/locales/hotkeys/ua.json
vendored
Normal file
207
invokeai/frontend/dist/locales/hotkeys/ua.json
vendored
Normal 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": "Прийняти поточне зображення"
|
||||
}
|
||||
}
|
@ -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."
|
||||
}
|
||||
|
53
invokeai/frontend/dist/locales/modelmanager/ua.json
vendored
Normal file
53
invokeai/frontend/dist/locales/modelmanager/ua.json
vendored
Normal 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": "Це не призведе до видалення файлу моделі з диску. Позніше ви можете додати його знову."
|
||||
}
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
62
invokeai/frontend/dist/locales/options/ua.json
vendored
Normal file
62
invokeai/frontend/dist/locales/options/ua.json
vendored
Normal 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": "Показати панель налаштувань"
|
||||
}
|
13
invokeai/frontend/dist/locales/settings/ua.json
vendored
Normal file
13
invokeai/frontend/dist/locales/settings/ua.json
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"models": "Моделі",
|
||||
"displayInProgress": "Показувати процес генерації",
|
||||
"saveSteps": "Зберігати кожні n кроків",
|
||||
"confirmOnDelete": "Підтверджувати видалення",
|
||||
"displayHelpIcons": "Показувати значки підказок",
|
||||
"useCanvasBeta": "Показувати інструменты зліва (Beta UI)",
|
||||
"enableImageDebugging": "Увімкнути налагодження",
|
||||
"resetWebUI": "Повернути початкові",
|
||||
"resetWebUIDesc1": "Скидання настройок веб-інтерфейсу видаляє лише локальний кеш браузера з вашими зображеннями та налаштуваннями. Це не призводить до видалення зображень з диску.",
|
||||
"resetWebUIDesc2": "Якщо зображення не відображаються в галереї або не працює ще щось, спробуйте скинути налаштування, перш ніж повідомляти про проблему на GitHub.",
|
||||
"resetComplete": "Інтерфейс скинуто. Оновіть цю сторінку."
|
||||
}
|
32
invokeai/frontend/dist/locales/toast/ua.json
vendored
Normal file
32
invokeai/frontend/dist/locales/toast/ua.json
vendored
Normal 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": "Не вдалося завантажити початкове зображення"
|
||||
}
|
15
invokeai/frontend/dist/locales/tooltip/ua.json
vendored
Normal file
15
invokeai/frontend/dist/locales/tooltip/ua.json
vendored
Normal 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": "Керування методами заповнення (використовується для масок або стертих частин полотна) та масштабування (корисно для малих розмірів обмежуючої рамки)."
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"layer": "Layer",
|
||||
"layer": "Livello",
|
||||
"base": "Base",
|
||||
"mask": "Maschera",
|
||||
"maskingOptions": "Opzioni di mascheramento",
|
||||
|
59
invokeai/frontend/dist/locales/unifiedcanvas/ua.json
vendored
Normal file
59
invokeai/frontend/dist/locales/unifiedcanvas/ua.json
vendored
Normal 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гати замасковану область"
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
"langSpanish": "Spanish",
|
||||
"langJapanese": "Japanese",
|
||||
"langDutch": "Dutch",
|
||||
"langUkranian": "Ukranian",
|
||||
"text2img": "Text To Image",
|
||||
"img2img": "Image To Image",
|
||||
"unifiedCanvas": "Unified Canvas",
|
||||
|
@ -21,6 +21,7 @@
|
||||
"langSpanish": "Spanish",
|
||||
"langJapanese": "Japanese",
|
||||
"langDutch": "Dutch",
|
||||
"langUkranian": "Ukranian",
|
||||
"text2img": "Text To Image",
|
||||
"img2img": "Image To Image",
|
||||
"unifiedCanvas": "Unified Canvas",
|
||||
|
@ -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",
|
||||
|
53
invokeai/frontend/public/locales/common/ua.json
Normal file
53
invokeai/frontend/public/locales/common/ua.json
Normal 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": "Модель змінено"
|
||||
}
|
16
invokeai/frontend/public/locales/gallery/ua.json
Normal file
16
invokeai/frontend/public/locales/gallery/ua.json
Normal file
@ -0,0 +1,16 @@
|
||||
{
|
||||
"generations": "Генерації",
|
||||
"showGenerations": "Показувати генерації",
|
||||
"uploads": "Завантаження",
|
||||
"showUploads": "Показувати завантаження",
|
||||
"galleryImageSize": "Розмір зображень",
|
||||
"galleryImageResetSize": "Аатоматичний розмір",
|
||||
"gallerySettings": "Налаштування галереї",
|
||||
"maintainAspectRatio": "Зберігати пропорції",
|
||||
"autoSwitchNewImages": "Автоматично вибирати нові",
|
||||
"singleColumnLayout": "Одна колонка",
|
||||
"pinGallery": "Закріпити галерею",
|
||||
"allImagesLoaded": "Всі зображення завантажені",
|
||||
"loadMore": "Завантажити більше",
|
||||
"noImagesInGallery": "Зображень немає"
|
||||
}
|
207
invokeai/frontend/public/locales/hotkeys/ua.json
Normal file
207
invokeai/frontend/public/locales/hotkeys/ua.json
Normal 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": "Прийняти поточне зображення"
|
||||
}
|
||||
}
|
@ -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."
|
||||
}
|
||||
|
53
invokeai/frontend/public/locales/modelmanager/ua.json
Normal file
53
invokeai/frontend/public/locales/modelmanager/ua.json
Normal 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": "Це не призведе до видалення файлу моделі з диску. Позніше ви можете додати його знову."
|
||||
}
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
62
invokeai/frontend/public/locales/options/ua.json
Normal file
62
invokeai/frontend/public/locales/options/ua.json
Normal 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": "Показати панель налаштувань"
|
||||
}
|
13
invokeai/frontend/public/locales/settings/ua.json
Normal file
13
invokeai/frontend/public/locales/settings/ua.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"models": "Моделі",
|
||||
"displayInProgress": "Показувати процес генерації",
|
||||
"saveSteps": "Зберігати кожні n кроків",
|
||||
"confirmOnDelete": "Підтверджувати видалення",
|
||||
"displayHelpIcons": "Показувати значки підказок",
|
||||
"useCanvasBeta": "Показувати інструменты зліва (Beta UI)",
|
||||
"enableImageDebugging": "Увімкнути налагодження",
|
||||
"resetWebUI": "Повернути початкові",
|
||||
"resetWebUIDesc1": "Скидання настройок веб-інтерфейсу видаляє лише локальний кеш браузера з вашими зображеннями та налаштуваннями. Це не призводить до видалення зображень з диску.",
|
||||
"resetWebUIDesc2": "Якщо зображення не відображаються в галереї або не працює ще щось, спробуйте скинути налаштування, перш ніж повідомляти про проблему на GitHub.",
|
||||
"resetComplete": "Інтерфейс скинуто. Оновіть цю сторінку."
|
||||
}
|
32
invokeai/frontend/public/locales/toast/ua.json
Normal file
32
invokeai/frontend/public/locales/toast/ua.json
Normal 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": "Не вдалося завантажити початкове зображення"
|
||||
}
|
15
invokeai/frontend/public/locales/tooltip/ua.json
Normal file
15
invokeai/frontend/public/locales/tooltip/ua.json
Normal 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": "Керування методами заповнення (використовується для масок або стертих частин полотна) та масштабування (корисно для малих розмірів обмежуючої рамки)."
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
"layer": "Layer",
|
||||
"layer": "Livello",
|
||||
"base": "Base",
|
||||
"mask": "Maschera",
|
||||
"maskingOptions": "Opzioni di mascheramento",
|
||||
|
59
invokeai/frontend/public/locales/unifiedcanvas/ua.json
Normal file
59
invokeai/frontend/public/locales/unifiedcanvas/ua.json
Normal 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гати замасковану область"
|
||||
}
|
@ -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();
|
||||
}
|
||||
});
|
||||
|
20
invokeai/frontend/src/common/util/getPromptAndNegative.ts
Normal file
20
invokeai/frontend/src/common/util/getPromptAndNegative.ts
Normal 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];
|
||||
}
|
@ -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,
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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',
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
@ -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>
|
||||
);
|
||||
}
|
@ -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,
|
||||
|
@ -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 = () => {
|
||||
|
@ -316,7 +316,6 @@ export default function CheckpointModelEdit() {
|
||||
) : (
|
||||
<Flex
|
||||
width="100%"
|
||||
height="250px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
backgroundColor="var(--background-color)"
|
||||
|
@ -271,7 +271,6 @@ export default function DiffusersModelEdit() {
|
||||
) : (
|
||||
<Flex
|
||||
width="100%"
|
||||
height="250px"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
backgroundColor="var(--background-color)"
|
||||
|
@ -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
|
||||
|
@ -24,8 +24,8 @@
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
&[aria-selected='true'] {
|
||||
|
@ -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} />
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
||||
|
@ -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?
|
||||
|
@ -1 +1 @@
|
||||
__version__='2.3.0-rc2'
|
||||
__version__='2.3.0-rc4'
|
||||
|
@ -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
|
||||
|
@ -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(),
|
||||
|
@ -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')
|
@ -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)
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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()
|
||||
|
@ -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:
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
27
scripts/pypi_helper.py
Normal 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")
|
Loading…
Reference in New Issue
Block a user