index.vue 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740
  1. <template>
  2. <div class="diffusion">
  3. <header_local />
  4. <h1>
  5. <img
  6. id="logo_img"
  7. :src="config.base.easyDiffusion + '/media/images/icon-512x512.png'"
  8. />
  9. <span style="vertical-align: middle">AI绘图</span>
  10. </h1>
  11. <el-row class="body">
  12. <el-col :span="8">
  13. <div class="main left">
  14. <p class="label">输入描述<small>(建议输入英文)</small></p>
  15. <el-input
  16. v-model="textarea"
  17. :rows="2"
  18. type="textarea"
  19. placeholder="请输入描述内容"
  20. />
  21. <p class="label">初始图片 (img2img) <small>(可选)</small></p>
  22. <input
  23. type="file"
  24. style="position: absolute; left: -100%"
  25. name="file"
  26. id="file"
  27. accept="image/*"
  28. @change="selectfile"
  29. />
  30. <image-ele
  31. v-if="fileInput.file"
  32. :file="fileInput"
  33. @close="closeFunc"
  34. @draw="draw"
  35. />
  36. <label
  37. for="file"
  38. class="dark"
  39. v-if="!fileInput.file"
  40. style="
  41. background-color: #202225;
  42. padding: 3px 8px;
  43. font-size: 0.9em;
  44. border-radius: 3px;
  45. "
  46. >
  47. <el-icon><FolderOpened /></el-icon>
  48. 选择
  49. </label>
  50. <span
  51. for="file"
  52. class="dark"
  53. v-if="!fileInput.file"
  54. style="
  55. background-color: #202225;
  56. padding: 3px 8px;
  57. font-size: 0.9em;
  58. border-radius: 3px;
  59. margin-left: 3px;
  60. "
  61. @click="draw"
  62. >
  63. <el-icon><EditPen /></el-icon>
  64. 绘制
  65. </span>
  66. <p class="label" v-if="Object.keys(selectObject).length">
  67. 图像修改器
  68. </p>
  69. <img-com
  70. v-for="(v, i) in selectObject || {}"
  71. :key="i + 'select'"
  72. :imgUrl="v.previews[selectType].path"
  73. :text="v.modifier"
  74. :isSelect="true"
  75. @click="() => delectSelect(i)"
  76. />
  77. <el-button
  78. type="primary"
  79. size="large"
  80. color="#4d5bff"
  81. style="display: block; width: 100%; margin-top: 1em"
  82. @click="createDr"
  83. :loading="loading"
  84. >
  85. 生成图片
  86. </el-button>
  87. <div class="line"></div>
  88. <el-collapse>
  89. <el-collapse-item>
  90. <template #title>
  91. <div class="collapseTitle">
  92. <el-icon size="11pt">
  93. <Plus />
  94. </el-icon>
  95. 图像设置
  96. </div>
  97. </template>
  98. <!-- <el-row style="margin-bottom: 5px">
  99. <el-col :span="4">
  100. <div style="text-align: right">种子:</div>
  101. </el-col>
  102. <el-col :span="20">
  103. <el-input
  104. size="small"
  105. style="width: 5em; margin-right: 3px"
  106. v-model="obj.zhongzi"
  107. :disabled="obj.zhongzisuiji"
  108. />
  109. <el-switch
  110. size="small"
  111. v-model="obj.zhongzisuiji"
  112. text=""
  113. inactive-text=""
  114. />随机
  115. </el-col>
  116. </el-row> -->
  117. <el-row style="margin-bottom: 5px">
  118. <el-col :span="4">
  119. <div style="text-align: right">图片数量:</div>
  120. </el-col>
  121. <el-col :span="8">
  122. <el-input-number
  123. class="dark"
  124. size="small"
  125. v-model="obj.imageNum"
  126. :min="1"
  127. />
  128. </el-col>
  129. <!-- <el-col :span="4">
  130. <div style="text-align: right">并行:</div>
  131. </el-col>
  132. <el-col :span="8">
  133. <el-input-number
  134. class="dark"
  135. size="small"
  136. v-model="obj.para"
  137. :min="1"
  138. />
  139. </el-col> -->
  140. </el-row>
  141. <!--
  142. <el-row style="margin-bottom: 5px">
  143. <el-col :span="4">
  144. <div style="text-align: right">模型:</div>
  145. </el-col>
  146. <el-col :span="20">
  147. <el-select
  148. size="small"
  149. v-model="obj.moxing"
  150. placeholder="Select"
  151. >
  152. <el-option label="sd-v1-4" value="sd-v1-4" />
  153. </el-select>
  154. </el-col>
  155. </el-row>
  156. <el-row style="margin-bottom: 5px">
  157. <el-col :span="4">
  158. <div style="text-align: right">定制VAE:</div>
  159. </el-col>
  160. <el-col :span="20">
  161. <el-select
  162. size="small"
  163. v-model="obj.ave"
  164. placeholder="Select"
  165. >
  166. <el-option label="None" value="None" />
  167. <el-option
  168. label="vae-ft-mse-840000-ema-pruned"
  169. value="vae-ft-mse-840000-ema-pruned"
  170. />
  171. </el-select>
  172. </el-col>
  173. </el-row>
  174. <el-row style="margin-bottom: 5px">
  175. <el-col :span="4">
  176. <div style="text-align: right">采样:</div>
  177. </el-col>
  178. <el-col :span="20">
  179. <el-select
  180. size="small"
  181. v-model="obj.caiyang"
  182. placeholder="Select"
  183. >
  184. <el-option label="PLMS" value="PLMS" />
  185. <el-option label="DDIM" value="DDIM" />
  186. <el-option label="Heun" value="Heun" />
  187. <el-option label="Euler" value="Euler" />
  188. <el-option
  189. label="Euler Ancestral"
  190. value="Euler Ancestral"
  191. />
  192. <el-option label="DPM2" value="DPM2" />
  193. <el-option label="DPM2 Ancestral" value="DPM2 Ancestral" />
  194. <el-option label="LMS" value="LMS" />
  195. <el-option
  196. label="DPM Solver (Stability AI)"
  197. value="DPM Solver (Stability AI)"
  198. />
  199. <el-option
  200. label="DPM++ 2s Ancestral (Karras)"
  201. value="DPM++ 2s Ancestral (Karras)"
  202. />
  203. <el-option
  204. label="DPM++ SDE (Karras)"
  205. value="DPM++ SDE (Karras)"
  206. />
  207. <el-option
  208. label="DPM Fast (Karras)"
  209. value="DPM Fast (Karras)"
  210. />
  211. <el-option label="UniPC SNR" value="UniPC SNR" />
  212. <el-option label="UniPC TU" value="UniPC TU" />
  213. <el-option label="UniPC SNR 2" value="UniPC SNR 2" />
  214. <el-option label="UniPC TU 2" value="UniPC TU 2" />
  215. <el-option label="UniPC TQ" value="UniPC TQ" />
  216. </el-select>
  217. </el-col>
  218. </el-row>
  219. <el-row style="margin-bottom: 5px" :gutter="3">
  220. <el-col :span="4">
  221. <div style="text-align: right">图片大小:</div>
  222. </el-col>
  223. <el-col :span="10">
  224. <el-select
  225. size="small"
  226. v-model="obj.width"
  227. placeholder="width"
  228. disabled
  229. >
  230. <el-option
  231. :label="item"
  232. :value="item"
  233. v-for="item in numlist"
  234. :key="'l' + item"
  235. />
  236. </el-select>
  237. </el-col>
  238. <el-col :span="10">
  239. <el-select
  240. size="small"
  241. v-model="obj.height"
  242. placeholder="height"
  243. disabled
  244. >
  245. <el-option
  246. :label="item"
  247. :value="item"
  248. v-for="item in numlist"
  249. :key="'r' + item"
  250. />
  251. </el-select>
  252. </el-col>
  253. </el-row>
  254. <el-row style="margin-bottom: 5px" :gutter="3">
  255. <el-col :span="4">
  256. <div style="text-align: right">步骤数:</div>
  257. </el-col>
  258. <el-col :span="20">
  259. <el-input
  260. size="small"
  261. v-model="obj.step"
  262. style="width: 8em"
  263. />
  264. </el-col>
  265. </el-row>
  266. <el-row style="margin-bottom: 5px" :gutter="3">
  267. <el-col :span="4">
  268. <div style="text-align: right">指导尺度:</div>
  269. </el-col>
  270. <el-col :span="20">
  271. <input
  272. type="range"
  273. :value="obj.chidu * 2"
  274. @input="rangechidu"
  275. />
  276. <el-input
  277. size="small"
  278. v-model="obj.chidu"
  279. style="width: 8em"
  280. />
  281. </el-col>
  282. </el-row>
  283. <el-row style="margin-bottom: 5px">
  284. <el-col :span="4">
  285. <div style="text-align: right">超网络:</div>
  286. </el-col>
  287. <el-col :span="20">
  288. <el-select
  289. size="small"
  290. v-model="obj.chaolianjie"
  291. placeholder="Select"
  292. >
  293. <el-option label="None" value="None" />
  294. </el-select>
  295. </el-col>
  296. </el-row>
  297. <el-row style="margin-bottom: 5px">
  298. <el-col :span="4">
  299. <div style="text-align: right">图片格式:</div>
  300. </el-col>
  301. <el-col :span="20">
  302. <el-select
  303. size="small"
  304. v-model="obj.imgType"
  305. placeholder="Select"
  306. >
  307. <el-option label=".jpeg" value=".jpeg" />
  308. <el-option label=".png" value=".png" />
  309. <el-option label=".webp" value=".webp" />
  310. </el-select>
  311. </el-col>
  312. </el-row>
  313. <el-row style="margin-bottom: 5px" :gutter="3">
  314. <el-col :span="4">
  315. <div style="text-align: right">图像质量:</div>
  316. </el-col>
  317. <el-col :span="20">
  318. <input type="range" v-model="obj.zhiliang" />
  319. <el-input
  320. size="small"
  321. v-model="obj.zhiliang"
  322. style="width: 8em"
  323. />
  324. </el-col>
  325. </el-row> -->
  326. </el-collapse-item>
  327. </el-collapse>
  328. <br />
  329. <el-collapse>
  330. <el-collapse-item name="1">
  331. <template #title>
  332. <div class="collapseTitle">
  333. <el-icon size="11pt">
  334. <Plus />
  335. </el-icon>
  336. 风格配置 (艺术风格、标签等)
  337. </div>
  338. </template>
  339. <el-row>
  340. <el-col :span="4">图像样式</el-col>
  341. <el-col :span="8">
  342. <el-select
  343. v-model="selectType"
  344. class="dark"
  345. popper-class="dark"
  346. size="small"
  347. placeholder="Select"
  348. >
  349. <el-option label="人脸" :value="0" />
  350. <el-option label="实景" :value="1" />
  351. </el-select>
  352. </el-col>
  353. </el-row>
  354. <el-collapse
  355. v-model="activeNamesItem"
  356. v-for="(item, index) in list"
  357. :key="index"
  358. >
  359. <el-collapse-item :name="index">
  360. <template #title>
  361. <div class="collapseTitle">
  362. <el-icon size="11pt">
  363. <Plus v-if="activeNamesItem != index" />
  364. <Minus v-else />
  365. </el-icon>
  366. {{ item.category }}
  367. </div>
  368. </template>
  369. <img-com
  370. v-for="(v, i) in item.modifiers || []"
  371. :key="index + i"
  372. :imgUrl="v.previews[selectType].path"
  373. :text="v.modifier"
  374. :isSelect="!!selectObject[index + '-' + i]"
  375. @click="() => selectTypeFunc(v, i, index)"
  376. />
  377. </el-collapse-item>
  378. </el-collapse>
  379. </el-collapse-item>
  380. </el-collapse>
  381. </div>
  382. </el-col>
  383. <el-col :span="16">
  384. <div class="main right">
  385. <listItem
  386. @setconfig="setconfig"
  387. @removeItem="removeItem"
  388. @makeimg="makeimg"
  389. @setImage="setImage"
  390. :downlist="downlist"
  391. />
  392. </div>
  393. </el-col>
  394. </el-row>
  395. <image-edit
  396. v-if="showImageEdit"
  397. :file="fileInput"
  398. @closemain="draw"
  399. @getimage="getimage"
  400. />
  401. </div>
  402. </template>
  403. <script setup>
  404. import header_local from '../allMedia/components/header.vue';
  405. import imgCom from './components/imgCom.vue';
  406. import imageEle from './components/image.vue';
  407. import imageEdit from './components/imageEdit.vue';
  408. import listItem from './components/listItem.vue';
  409. import { getPaintingStyle, createDraw, queryDraw } from '@/api/index';
  410. import config from '../../config';
  411. import { ref } from 'vue';
  412. const textarea = ref('');
  413. const fileInput = ref({});
  414. const activeNamesItem = ref(-1);
  415. const list = ref([]);
  416. const selectObject = ref({});
  417. const showImageEdit = ref(false);
  418. const selectType = ref(1);
  419. const obj = ref({
  420. imageNum: 1,
  421. para: 1,
  422. zhongzi: '',
  423. zhongzisuiji: true,
  424. moxing: 'sd-v1-4',
  425. ave: 'None',
  426. caiyang: 'DDIM',
  427. step: 25,
  428. chaolianjie: 'None',
  429. imgType: '.jpeg',
  430. chidu: 50,
  431. zhiliang: 75,
  432. width: 512,
  433. height: 512,
  434. });
  435. const loading = ref(false);
  436. const ajaxList = [];
  437. const downlist = ref([]);
  438. let l = [];
  439. // const numlist = [
  440. // 128, 192, 256, 320, 384, 448, 512, 576, 640, 704, 768, 832, 896, 960, 1024,
  441. // 1280, 1536, 1792, 2048,
  442. // ];
  443. document.title = 'AI绘图';
  444. getPaintingStyle({}).then(r => {
  445. list.value = r || [];
  446. });
  447. const createDr = () => {
  448. const active_tags = [];
  449. loading.value = true;
  450. for (const key in selectObject.value) {
  451. if (!Object.hasOwnProperty.call(selectObject.value, key)) continue;
  452. active_tags.push(selectObject.value[key].modifier);
  453. }
  454. const f = {
  455. file: fileInput.value.file,
  456. fileResult: fileInput.value.fileResult,
  457. };
  458. ajaxList.push({
  459. data: {
  460. prompt: textarea.value || undefined,
  461. init_image: f.fileResult,
  462. active_tags,
  463. },
  464. imageNum: obj.value.imageNum || 1,
  465. status: 0,
  466. });
  467. const tags = JSON.parse(JSON.stringify(selectObject.value));
  468. downlist.value.unshift({
  469. list: [],
  470. tags,
  471. prompt: textarea.value || undefined,
  472. init_image: f,
  473. ...obj.value,
  474. imageNum: obj.value.imageNum || 1,
  475. status: ajaxList[0].status,
  476. statusImg: '待定',
  477. });
  478. ajax();
  479. };
  480. const ajax = () => {
  481. if (!ajaxList.length || ajaxList[0].status > ajaxList[0].imageNum) return;
  482. createDraw({
  483. data: ajaxList[0].data,
  484. })
  485. .then(r => {
  486. let t = setInterval(() => {
  487. queryDraw({
  488. data: r.task,
  489. }).then(res => {
  490. downlist.value[0].statusImg = '等待';
  491. if (res.status !== 'succeeded') return;
  492. l.push({
  493. image: res.output[0].data,
  494. ...res.render_request,
  495. ...res.task_data,
  496. });
  497. ajaxList[0].status += 1;
  498. downlist.value[0].status = ajaxList[0].status;
  499. if (ajaxList[0].imageNum - ajaxList[0].status <= 0) {
  500. ajaxList.shift();
  501. downlist.value[0].list = l;
  502. l = [];
  503. loading.value = false;
  504. // 当前图已经生成结束
  505. }
  506. window.clearInterval(t);
  507. if (!ajaxList.length) {
  508. downlist.value[0].statusImg = '';
  509. return;
  510. }
  511. return ajax();
  512. });
  513. }, 1000);
  514. })
  515. .catch(() => {
  516. ajaxList[0].status = 0;
  517. });
  518. };
  519. const selectTypeFunc = (type, i, index) => {
  520. if (!selectObject.value[index + '-' + i])
  521. selectObject.value[index + '-' + i] = JSON.parse(JSON.stringify(type));
  522. else delete selectObject.value[index + '-' + i];
  523. };
  524. const setImage = image => {
  525. console.log(image)
  526. fileInput.value.fileResult = image;
  527. fileInput.value.file = image;
  528. }
  529. const delectSelect = index => {
  530. delete selectObject.value[index];
  531. };
  532. const selectfile = file => {
  533. const F = file.target.files[0] || undefined;
  534. if (!F) {
  535. closeFunc();
  536. return;
  537. }
  538. var reader = new FileReader();
  539. reader.readAsDataURL(F); //发起异步请求
  540. reader.onload = function () {
  541. //读取完成后,数据保存在对象的result属性中
  542. file.target.value = '';
  543. fileInput.value.fileResult = this.result;
  544. fileInput.value.file = F;
  545. };
  546. };
  547. const closeFunc = () => {
  548. fileInput.value.fileResult = undefined;
  549. fileInput.value.file = undefined;
  550. };
  551. const draw = () => {
  552. showImageEdit.value = !showImageEdit.value;
  553. };
  554. const removeItem = indexs => {
  555. const i = indexs.split('-');
  556. if (i.length === 1 || downlist.value[i[0]].list.length == 1) {
  557. downlist.value.splice(i[0], 1);
  558. } else {
  559. downlist.value[i[0]].list.splice(i[1], 1);
  560. }
  561. };
  562. const setconfig = item => {
  563. selectObject.value = item.tags;
  564. fileInput.value = item.init_image;
  565. textarea.value = item.prompt || '';
  566. obj.value.imageNum = item.imageNum || 1;
  567. };
  568. const getimage = base64 => {
  569. fileInput.value.fileResult = base64;
  570. fileInput.value.file = base64;
  571. };
  572. // const rangechidu = e => {
  573. // obj.value.chidu = e.target.value / 2;
  574. // };
  575. const makeimg = item => {
  576. const active_tags = [];
  577. loading.value = true;
  578. for (const key in item.tags || {}) {
  579. if (!Object.hasOwnProperty.call(item.tags, key)) continue;
  580. active_tags.push(item.tags[key].modifier);
  581. }
  582. const f = {
  583. file: item.init_image ? item.init_image.file : undefined,
  584. fileResult: item.init_image ? item.init_image.fileResult : undefined,
  585. };
  586. ajaxList.push({
  587. data: {
  588. prompt: item.prompt || undefined,
  589. init_image: f.fileResult,
  590. active_tags,
  591. },
  592. imageNum: item.imageNum || 1,
  593. status: 0,
  594. });
  595. downlist.value.unshift({
  596. ...item,
  597. list: [],
  598. status: ajaxList[0].status,
  599. statusImg: '待定',
  600. });
  601. ajax();
  602. };
  603. </script>
  604. <style scoped>
  605. .diffusion {
  606. background-color: #202225;
  607. color: #eee;
  608. width: 100vw;
  609. height: 100%;
  610. }
  611. .diffusion > h1 {
  612. padding: 12px 0;
  613. font-size: 2em;
  614. }
  615. .body {
  616. background-color: #36393f;
  617. width: 100%;
  618. height: 100%;
  619. overflow-y: auto;
  620. padding-bottom: 2em;
  621. }
  622. #logo_img {
  623. width: 32px;
  624. height: 32px;
  625. }
  626. .main {
  627. padding: 0 16px 16px 16px;
  628. }
  629. .label {
  630. font-size: 10pt;
  631. font-weight: 700;
  632. height: 1.5em;
  633. line-height: 1.5em;
  634. padding-left: 3px;
  635. margin-top: 12px;
  636. }
  637. .line {
  638. margin: 16px 0;
  639. height: 1px;
  640. background-color: #202225;
  641. }
  642. .collapseTitle {
  643. padding-left: 5px;
  644. font-weight: 700;
  645. font-size: 11pt;
  646. }
  647. .right {
  648. padding: 1em;
  649. width: 100%;
  650. }
  651. </style>
  652. <style>
  653. .diffusion .el-textarea__inner {
  654. background-color: #202225;
  655. border: 2px solid #2f3136;
  656. box-shadow: none;
  657. color: #fff;
  658. }
  659. .diffusion .el-textarea__inner:focus {
  660. outline: 2px solid #4d5bff;
  661. }
  662. .diffusion .el-collapse-item__header,
  663. .diffusion .el-collapse,
  664. .diffusion .el-collapse-item__wrap,
  665. .diffusion .el-popper.is-light,
  666. .diffusion .el-select__popper.el-popper .el-popper__arrow::before {
  667. background-color: #2f3136;
  668. color: #eee;
  669. border-color: #2f3136;
  670. }
  671. .dark,
  672. .dark * {
  673. background-color: #202225;
  674. color: #eee;
  675. border-color: #2f3136;
  676. }
  677. .dark .el-select-dropdown__item.hover,
  678. .dark .el-select-dropdown__item:hover {
  679. background-color: #ffffff60;
  680. }
  681. .dark .el-select-dropdown__item.hover span {
  682. background-color: #ffffff00;
  683. }
  684. .diffusion .el-popper.is-light,
  685. .diffusion .el-select__popper.el-popper .el-popper__arrow::before {
  686. box-shadow: 0 0 5px #ffffff80;
  687. }
  688. .diffusion .el-collapse-item__content {
  689. color: #eee;
  690. padding-left: 15px;
  691. }
  692. .diffusion .el-collapse {
  693. border-radius: 5px;
  694. }
  695. .diffusion .el-input__wrapper {
  696. box-shadow: 0 0 0 1px #202225 inset;
  697. background-color: #202225;
  698. }
  699. .diffusion .el-input.is-disabled .el-input__wrapper {
  700. background-color: #202225;
  701. box-shadow: 0 0 0 1px #202225 inset;
  702. }
  703. .diffusion .el-select-dropdown__item.hover {
  704. background-color: #202225 !important;
  705. }
  706. </style>