John1561's picture
Upload 47 files
791db78
package api
import (
"context"
"encoding/base64"
"errors"
"fmt"
"math"
"math/rand"
"regexp"
"strings"
parseflag "github.com/zijiren233/stable-diffusion-webui-bot/flag"
"github.com/zijiren233/stable-diffusion-webui-bot/gconfig"
"github.com/zijiren233/stable-diffusion-webui-bot/utils"
)
type drawType uint
const (
T_tag2img drawType = iota
T_img2img
T_superResolution
)
type config struct {
ctx context.Context
resoultChan chan<- *Resoult
cfg *drawConfig
rawCfg DrawConfig
api struct {
api *apiUrl
status Status
}
}
type Status struct {
Progress float64 `json:"progress"`
EtaRelative float64 `json:"eta_relative"`
}
type superResolutionCfg struct {
resoultChan chan<- *Resoult `json:"-"`
ctx context.Context
UpscalingResize int `json:"upscaling_resize"`
Upscaler1 string `json:"upscaler_1"`
ExtrasUpscaler2Visibility float64 `json:"extras_upscaler_2_visibility"`
ImageList []struct {
Data string `json:"data"`
Name string `json:"name"`
} `json:"imageList"`
}
type ctrlPhotoCfg struct {
resoultChan chan<- *Resoult `json:"-"`
ctx context.Context
ControlnetModule string `json:"controlnet_module"`
ControlnetInputImages []string `json:"controlnet_input_images"`
ControlnetProcessorRes int `json:"controlnet_processor_res"`
ControlnetThresholdA int `json:"controlnet_threshold_a"`
ControlnetThresholdB int `json:"controlnet_threshold_b"`
}
type InterrogateResoult struct {
Err error
Resoult string
}
type interrogateCfg struct {
resoultChan chan<- *InterrogateResoult `json:"-"`
ctx context.Context
Image string `json:"image"`
Model string `json:"model"`
}
type DrawConfig struct {
Tag string `json:"tag,omitempty" yaml:"tag,omitempty"`
Mode string `json:"mode,omitempty" yaml:"mode,omitempty"`
Num int `json:"num,omitempty" yaml:"num,omitempty"`
Steps int `json:"steps,omitempty" yaml:"steps,omitempty"`
Seed uint32 `json:"seed,omitempty" yaml:"seed,omitempty"`
CfgScale int `json:"scale,omitempty" yaml:"scale,omitempty"`
Width int `json:"width" yaml:"width"`
Height int `json:"height" yaml:"height"`
Model string `json:"model,omitempty" yaml:"model,omitempty"`
Uc string `json:"uc,omitempty" yaml:"uc,omitempty"`
Strength float64 `json:"strength,omitempty" yaml:"strength,omitempty"`
ControlPreprocess string `json:"control_preprocess,omitempty" yaml:"control_preprocess,omitempty"`
ControlProcess string `json:"control_process,omitempty" yaml:"control_process,omitempty"`
}
type drawConfig struct {
ResizeMode int `json:"resize_mode,omitempty"`
EnableHr bool `json:"enable_hr,omitempty"`
HrScale float64 `json:"hr_scale,omitempty"`
HrUpscaler string `json:"hr_upscaler,omitempty"`
HrSecondPassSteps int `json:"hr_second_pass_steps,omitempty"`
InitImages []string `json:"init_images,omitempty"`
DenoisingStrength float64 `json:"denoising_strength"`
Styles []string `json:"styles,omitempty"`
Prompt string `json:"prompt"`
Count int `json:"n_iter,omitempty"`
Num int `json:"batch_size,omitempty"`
Seed uint32 `json:"seed"`
Steps int `json:"steps"`
CfgScale int `json:"cfg_scale"`
Width int `json:"width"`
Height int `json:"height"`
NegativePrompt string `json:"negative_prompt"`
SamplerName string `json:"sampler_name"`
SamplerIndex string `json:"sampler_index"`
AlwaysonScripts struct {
Controlnet struct {
Args []ControlnetUnits `json:"args,omitempty"`
} `json:"controlnet,omitempty"`
} `json:"alwayson_scripts,omitempty"`
}
type ControlnetUnits struct {
InputImage string `json:"input_image,omitempty"`
Mask string `json:"mask,omitempty"`
Module string `json:"module,omitempty"`
Model string `json:"model,omitempty"`
Weight int `json:"weight,omitempty"`
ResizeMode string `json:"resize_mode,omitempty"`
Lowvram bool `json:"lowvram,omitempty"`
ProcessorRes int `json:"processor_res,omitempty"`
ThresholdA int `json:"threshold_a,omitempty"`
ThresholdB int `json:"threshold_b,omitempty"`
Guidance int `json:"guidance,omitempty"`
GuidanceStart int `json:"guidance_start,omitempty"`
GuidanceEnd int `json:"guidance_end,omitempty"`
Guessmode bool `json:"guessmode,omitempty"`
}
var allMode = [...]string{"DPM++ 2M Karras", "DPM++ 2M SDE Karras", "DPM++ SDE Karras", "Euler a", "DPM2", "DPM adaptive", "DPM2 a Karras", "DPM2 Karras", "DPM++ 2M", "DPM++ 2S a", "DPM++ 2S a Karras", "DPM++ SDE", "LMS Karras", "Euler", "DDIM", "Heun", "UniPC"}
var parseRepeat, _ = regexp.Compile(`, *?,`)
var Ucmap = map[string]string{
"low quality": "cropped, worst quality, low quality, normal quality, jpeg artifacts, signature, watermark, username, blurry",
"bad anatomy": "bad anatomy, bad hands, error, missing fingers, extra digit, fewer digits",
}
var DefaultMode = allMode[0]
const (
MaxHFSteps = 20
MaxNum = 9
DefaultCfgScale = 9
DefaultSteps = 20
DefaultNum = 1
)
var defaultUC = fmt.Sprint("lowres, text, ", Ucmap["bad anatomy"], ", ", Ucmap["low quality"])
func DefauleUC() string {
return defaultUC
}
func AllMode() [len(allMode)]string {
return allMode
}
func DefaultConfig() *DrawConfig {
return &DrawConfig{
Width: 512,
Height: 768,
Num: 1,
Strength: 0.70,
Mode: DefaultMode,
Steps: DefaultSteps,
CfgScale: DefaultCfgScale,
Uc: defaultUC,
Model: gconfig.MODELS()[0].Name,
}
}
func (cfg *DrawConfig) generateUC(translated bool) {
if translated {
cfg.Uc = utils.Translate(ReplaceString(cfg.Uc))
} else {
cfg.Uc = ReplaceString(cfg.Uc)
}
low := strings.ToLower(cfg.Uc)
if !strings.Contains(low, `lowres`) {
cfg.Uc = `lowres, ` + cfg.Uc
}
if !strings.Contains(low, `text`) {
cfg.Uc = `text, ` + cfg.Uc
}
}
func GenerateUC(uc string) string {
uc = ReplaceString(uc)
low := strings.ToLower(uc)
if !strings.Contains(low, `lowres`) {
uc = `lowres, ` + uc
}
if !strings.Contains(low, `text`) {
uc = `text, ` + uc
}
return uc
}
func (cfg *DrawConfig) generateTag(translated bool) {
if translated {
cfg.Tag = utils.Translate(ReplaceString(cfg.Tag))
} else {
cfg.Tag = ReplaceString(cfg.Tag)
}
low := strings.ToLower(cfg.Tag)
if !strings.Contains(low, "best quality") {
cfg.Tag = "best quality, " + cfg.Tag
}
if !strings.Contains(low, "masterpiece") {
cfg.Tag = "masterpiece, " + cfg.Tag
}
}
func New(cfg *DrawConfig, initPhoto, ControlPhoto []byte, CorrectCfg bool) (*config, error) {
if cfg.Tag == "" {
return nil, errors.New("tag can not be empty")
}
if CorrectCfg {
cfg.CorrectCfg(true, true, len(initPhoto) != 0, len(ControlPhoto) != 0, false, false, true)
}
dCfg := new(drawConfig)
dCfg.Prompt = cfg.Tag
dCfg.Seed = cfg.Seed
dCfg.SamplerName = cfg.Mode
dCfg.SamplerIndex = cfg.Mode
dCfg.Width = cfg.Width
dCfg.Height = cfg.Height
dCfg.CfgScale = cfg.CfgScale
dCfg.Steps = cfg.Steps
dCfg.NegativePrompt = cfg.Uc
dCfg.Num = 1
dCfg.Count = cfg.Num
c := &config{cfg: dCfg, rawCfg: *cfg}
if len(initPhoto) != 0 {
dCfg.ResizeMode = 2
c.cfg.InitImages = []string{base64.StdEncoding.EncodeToString(initPhoto)}
c.cfg.DenoisingStrength = c.rawCfg.Strength
} else {
dCfg.Width /= 2
dCfg.Height /= 2
dCfg.EnableHr = true
dCfg.DenoisingStrength = 0.55
dCfg.HrScale = 2
dCfg.HrUpscaler = "R-ESRGAN 4x+ Anime6B"
if dCfg.Steps < MaxHFSteps {
dCfg.HrSecondPassSteps = dCfg.Steps
} else {
dCfg.HrSecondPassSteps = MaxHFSteps
}
}
if len(ControlPhoto) != 0 {
var max int
if dCfg.Width > dCfg.Height {
max = dCfg.Width
} else {
max = dCfg.Height
}
ctrl := ControlnetUnits{
Lowvram: false,
InputImage: base64.StdEncoding.EncodeToString(ControlPhoto),
Module: cfg.ControlPreprocess,
Model: gconfig.Name2Process(cfg.ControlProcess).File,
ProcessorRes: max,
}
dCfg.AlwaysonScripts.Controlnet.Args = append(dCfg.AlwaysonScripts.Controlnet.Args, ctrl)
}
return c, nil
}
func NewSuperResolution(photo [][]byte, resize int) (*superResolutionCfg, error) {
if resize > 4 || resize < 1 {
return nil, errors.New("resize muse be in 1 and 4")
}
if photo == nil {
return nil, errors.New("photo is nil")
}
cfg := &superResolutionCfg{ExtrasUpscaler2Visibility: 1, UpscalingResize: resize, Upscaler1: "R-ESRGAN 4x+ Anime6B"}
for k, v := range photo {
fileType, err := utils.GetType(v)
if err != nil {
continue
}
cfg.ImageList = append(cfg.ImageList, struct {
Data string "json:\"data\""
Name string "json:\"name\""
}{Data: fmt.Sprint("data:", fileType, ";base64,", base64.StdEncoding.EncodeToString(v)), Name: fmt.Sprint(k)})
}
return cfg, nil
}
func NewSuperResolutionWithBase64(photo []string, multiplier int) (*superResolutionCfg, error) {
if multiplier > 4 || multiplier < 1 {
return nil, errors.New("multiplier muse be in 1 and 4")
}
if len(photo) == 0 {
return nil, errors.New("photo is nil")
}
cfg := &superResolutionCfg{ExtrasUpscaler2Visibility: 1, UpscalingResize: multiplier, Upscaler1: "R-ESRGAN 4x+ Anime6B"}
for k, v := range photo {
b, err := base64.StdEncoding.DecodeString(v)
if err != nil {
return nil, err
}
fileType, err := utils.GetType(b)
if err != nil {
continue
}
cfg.ImageList = append(cfg.ImageList, struct {
Data string "json:\"data\""
Name string "json:\"name\""
}{Data: fmt.Sprint("data:", fileType, ";base64,", v), Name: fmt.Sprint(k)})
}
return cfg, nil
}
func NewCtrlPhoto(photo [][]byte, Processor string, ResSize int) (*ctrlPhotoCfg, error) {
if len(photo) == 0 {
return nil, errors.New("photo is nil")
}
if ResSize < 0 {
return nil, errors.New("size is less than zero")
}
cfg := &ctrlPhotoCfg{ControlnetModule: ParsePreProcess(Processor)}
if ResSize == 0 {
width, hight, err := utils.GetPhotoSize(photo[0])
if err != nil {
return nil, err
}
if width >= hight {
cfg.ControlnetProcessorRes = width
} else {
cfg.ControlnetProcessorRes = hight
}
} else {
cfg.ControlnetProcessorRes = ResSize
}
for _, v := range photo {
cfg.ControlnetInputImages = append(cfg.ControlnetInputImages, base64.StdEncoding.EncodeToString(v))
}
return cfg, nil
}
func NewCtrlPhotoWithBash64(photo []string, Processor string, ResSize int) (*ctrlPhotoCfg, error) {
if len(photo) == 0 {
return nil, errors.New("photo is nil")
}
if ResSize < 0 {
return nil, errors.New("size is less than zero")
}
cfg := &ctrlPhotoCfg{ControlnetModule: ParsePreProcess(Processor), ControlnetInputImages: photo}
if ResSize == 0 {
b, err := base64.StdEncoding.DecodeString(photo[0])
if err != nil {
return nil, err
}
width, hight, err := utils.GetPhotoSize(b)
if err != nil {
return nil, err
}
if width >= hight {
cfg.ControlnetProcessorRes = width
} else {
cfg.ControlnetProcessorRes = hight
}
} else {
cfg.ControlnetProcessorRes = ResSize
}
return cfg, nil
}
func NewInterrogate(photo []byte) (*interrogateCfg, error) {
if photo == nil {
return nil, errors.New("photo is nil")
}
fileType, err := utils.GetType(photo)
if err != nil {
return nil, err
}
cfg := &interrogateCfg{Image: fmt.Sprint("data:", fileType, ";base64,", base64.StdEncoding.EncodeToString(photo)), Model: "deepdanbooru"}
return cfg, nil
}
func (cfg *config) GetCfg() DrawConfig {
return cfg.rawCfg
}
var replaceColonRe = regexp.MustCompile(`: *`)
func ReplaceColon(s string) string {
return replaceColonRe.ReplaceAllString(s, ":")
}
func ReplaceString(src string) string {
if src == "" {
return ""
}
src = strings.ReplaceAll(src, ",", ",")
src = strings.ReplaceAll(src, "\n", ",")
src = strings.ReplaceAll(src, "(", "(")
src = strings.ReplaceAll(src, ")", ")")
for parseRepeat.MatchString(src) {
src = parseRepeat.ReplaceAllString(src, ",")
}
src = strings.TrimLeft(strings.TrimRight(src, ", "), ", ")
return replaceColonRe.ReplaceAllString(src, ":")
}
func ParseCfgScalse(scale int) int {
if scale <= 0 || scale > 30 {
return DefaultCfgScale
}
return scale
}
func ParseSteps(steps int) int {
if steps < 15 || steps > 50 {
return DefaultSteps
}
return steps
}
func ParseNum(num int) int {
if num <= 0 || num > MaxNum {
return DefaultNum
}
return num
}
func ParsePreProcess(PreProcess string) string {
cpp := gconfig.PreProcess()
if PreProcess == "" {
return cpp[0].Name
}
if _, ok := utils.In(cpp, func(c gconfig.ControlPreProcess) bool {
return c.Name == PreProcess
}); !ok {
return cpp[0].Name
}
return PreProcess
}
func ParseProcess(Process string) string {
cp := gconfig.Process()
if Process == "" {
return cp[0].Name
}
if _, ok := utils.In(cp, func(c gconfig.ControlProcess) bool {
return c.Name == Process
}); !ok {
return cp[0].Name
}
return Process
}
func (cfg *DrawConfig) CorrectCfg(gTag, gUc, photo, ctrlPhoto, transTag, transUc, gSeed bool) {
if !ctrlPhoto {
cfg.ControlPreprocess = ""
cfg.ControlProcess = ""
} else {
cfg.ControlPreprocess = ParsePreProcess(cfg.ControlPreprocess)
cfg.ControlProcess = ParseProcess(cfg.ControlProcess)
}
if !photo {
cfg.Strength = 0
} else if cfg.Strength < 0 || cfg.Strength >= 1 {
cfg.Strength = 0.70
}
cfg.CfgScale = ParseCfgScalse(cfg.CfgScale)
if gSeed && cfg.Seed == 0 {
cfg.Seed = uint32(rand.Intn(math.MaxUint32))
}
cfg.Model = gconfig.Name2Model(cfg.Model).Name
cfg.Steps = ParseSteps(cfg.Steps)
if gUc {
cfg.generateUC(transUc)
}
if gTag {
cfg.generateTag(transTag)
}
cfg.Num = ParseNum(cfg.Num)
if cfg.Height < 64 || cfg.Width < 64 {
cfg.Width = 512
cfg.Height = 768
} else if sum := cfg.Height * cfg.Width; sum > parseflag.ImgMaxSize {
a := math.Pow(float64(sum)/float64(parseflag.ImgMaxSize), 0.5)
cfg.Width = int(float64(cfg.Width) / a)
cfg.Height = int(float64(cfg.Height) / a)
}
cfg.Width -= cfg.Width % 8
cfg.Height -= cfg.Height % 8
if _, ok := utils.InString(cfg.Mode, allMode[:]); !ok {
cfg.Mode = allMode[0]
}
}