diff --git a/dsAiSupport/src/main/java/com/dsideal/aiSupport/Util/Liblib/ImageToImageUltra.java b/dsAiSupport/src/main/java/com/dsideal/aiSupport/Util/Liblib/ImageToImageUltra.java new file mode 100644 index 00000000..3b596a13 --- /dev/null +++ b/dsAiSupport/src/main/java/com/dsideal/aiSupport/Util/Liblib/ImageToImageUltra.java @@ -0,0 +1,236 @@ +package com.dsideal.aiSupport.Util.Liblib; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONArray; +import com.alibaba.fastjson.JSONObject; +import com.dsideal.aiSupport.Plugin.YamlProp; +import com.dsideal.aiSupport.Util.Liblib.Kit.SignUtil; +import com.jfinal.kit.Prop; +import okhttp3.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +import static com.dsideal.aiSupport.AiSupportApplication.getEnvPrefix; + +/** + * LibLib 图生图API工具类 + * 使用ControlNet技术将一个源图像作为参考来生成新图像 + */ +public class ImageToImageUltra { + private static final Logger log = LoggerFactory.getLogger(ImageToImageUltra.class); + + // API基础URL + private static final String API_BASE_URL = "https://openapi.liblibai.cloud"; + // 图生图API路径 + private static final String IMG_TO_IMG_PATH = "/api/generate/webui/img2img/ultra"; + + // API访问凭证 + protected static String accessKey; + protected static String secretKey; + public static Prop PropKit; + + static { + //加载配置文件 + String configFile = "application_{?}.yaml".replace("{?}", getEnvPrefix()); + PropKit = new YamlProp(configFile); + accessKey = PropKit.get("LIBLIB.accessKey"); + secretKey = PropKit.get("LIBLIB.secretKey"); + } + + /** + * 创建OkHttpClient实例 + * @return OkHttpClient实例 + */ + private static OkHttpClient createHttpClient() { + return new OkHttpClient.Builder() + .connectTimeout(30, TimeUnit.SECONDS) + .readTimeout(60, TimeUnit.SECONDS) + .writeTimeout(60, TimeUnit.SECONDS) + .build(); + } + + /** + * 提交图生图任务 + * @param templateUuid 模板UUID + * @param prompt 提示词 + * @param sourceImageUrl 源图片URL + * @param width 宽度 + * @param height 高度 + * @param steps 步数 + * @param cfgScale 提示词引导系数 + * @param seed 随机种子,-1表示随机 + * @param controlNetModel ControlNet模型ID + * @param controlWeight 控制权重 + * @return 生成任务UUID + * @throws IOException 异常信息 + */ + public static String submitImageToImageTask( + String templateUuid, + String prompt, + String sourceImageUrl, + int width, + int height, + int steps, + double cfgScale, + long seed, + String controlNetModel, + double controlWeight) throws IOException { + + // 创建OkHttpClient + OkHttpClient client = createHttpClient(); + + // 构建请求体 + JSONObject requestBody = new JSONObject(); + + // 添加模板UUID + requestBody.put("templateUuid", templateUuid); + + // 构建生成参数 + JSONObject generateParams = new JSONObject(); + generateParams.put("prompt", prompt); + generateParams.put("width", width); + generateParams.put("height", height); + generateParams.put("imgCount", 1); + generateParams.put("cfgScale", cfgScale); + generateParams.put("randnSource", 0); + generateParams.put("seed", seed); + generateParams.put("clipSkip", 2); + generateParams.put("sampler", 1); + generateParams.put("steps", steps); + generateParams.put("restoreFaces", 0); + + // 构建ControlNet参数 + JSONArray controlNetArray = new JSONArray(); + JSONObject controlNet = new JSONObject(); + controlNet.put("unitOrder", 0); + controlNet.put("sourceImage", sourceImageUrl); + controlNet.put("width", width); + controlNet.put("height", height); + controlNet.put("preprocessor", 68); + + // 构建注释参数 + JSONObject annotationParameters = new JSONObject(); + annotationParameters.put("entityControl", new JSONObject()); + controlNet.put("annotationParameters", annotationParameters); + + // 设置ControlNet模型和权重 + controlNet.put("model", controlNetModel); + controlNet.put("controlWeight", controlWeight); + controlNet.put("startingControlStep", 0); + controlNet.put("endingControlStep", 1); + controlNet.put("pixelPerfect", 1); + controlNet.put("controlMode", 0); + controlNet.put("resizeMode", 1); + + // 添加ControlNet到数组 + controlNetArray.add(controlNet); + generateParams.put("controlNet", controlNetArray); + + // 将生成参数添加到请求体 + requestBody.put("generateParams", generateParams); + + // 获取API路径 + String uri = IMG_TO_IMG_PATH; + + // 生成签名信息 + SignUtil.SignatureInfo signInfo = SignUtil.makeSignInfo(uri); + + // 构建带签名的URL + HttpUrl.Builder urlBuilder = HttpUrl.parse(API_BASE_URL + uri).newBuilder() + .addQueryParameter("AccessKey", accessKey) + .addQueryParameter("Signature", signInfo.getSignature()) + .addQueryParameter("Timestamp", String.valueOf(signInfo.getTimestamp())) + .addQueryParameter("SignatureNonce", signInfo.getSignatureNonce()); + + // 创建请求 + MediaType mediaType = MediaType.parse("application/json"); + RequestBody body = RequestBody.create(mediaType, requestBody.toJSONString()); + Request request = new Request.Builder() + .url(urlBuilder.build()) + .method("POST", body) + .addHeader("Content-Type", "application/json") + .build(); + + // 执行请求 + log.info("提交图生图任务: {}", requestBody.toJSONString()); + log.info("请求URL: {}", urlBuilder.build()); + Response response = client.newCall(request).execute(); + + // 处理响应 + if (!response.isSuccessful()) { + String errorMsg = "图生图任务提交失败,状态码: " + response.code(); + log.error(errorMsg); + throw new IOException(errorMsg); + } + + // 解析响应 + String responseBody = response.body().string(); + log.info("图生图任务提交响应: {}", responseBody); + + JSONObject responseJson = JSON.parseObject(responseBody); + int code = responseJson.getIntValue("code"); + + if (code != 0) { + String errorMsg = "图生图任务提交失败,错误码: " + code + ", 错误信息: " + responseJson.getString("msg"); + log.error(errorMsg); + throw new IOException(errorMsg); + } + + // 获取生成任务UUID + String generateUuid = responseJson.getJSONObject("data").getString("generateUuid"); + log.info("图生图任务已提交,任务UUID: {}", generateUuid); + + return generateUuid; + } + + /** + * 使用示例 + */ + public static void main(String[] args) { + try { + // 模板UUID + String templateUuid = "07e00af4fc464c7ab55ff906f8acf1b7"; + // 提示词 + String prompt = "focus on the cat,there is a cat holding a bag of mcdonald, product advertisement,"; + // 源图片URL + String sourceImageUrl = "https://liblibai-online.liblib.cloud/img/081e9f07d9bd4c2ba090efde163518f9/5fae2d9099c208487bc97867bece2bf3d904068e307c7bd30c646c9f3059af33.png"; + // 图片尺寸 + int width = 768; + int height = 1024; + // 步数 + int steps = 30; + // 提示词引导系数 + double cfgScale = 3.5; + // 随机种子,-1表示随机 + long seed = -1; + // ControlNet模型ID + String controlNetModel = "6f1767b5f9eb47289525d06ae882a0e5"; + // 控制权重 + double controlWeight = 0.9; + + // 提交图生图任务 + String generateUuid = submitImageToImageTask( + templateUuid, + prompt, + sourceImageUrl, + width, + height, + steps, + cfgScale, + seed, + controlNetModel, + controlWeight + ); + + // 输出生成任务UUID + log.info("图生图任务已提交,任务UUID: {}", generateUuid); + log.info("请使用此UUID查询任务进度和结果"); + + } catch (Exception e) { + log.error("图生图任务执行失败", e); + } + } +} \ No newline at end of file diff --git a/dsAiSupport/src/main/java/com/dsideal/aiSupport/Util/Liblib/Kit/SignUtil.java b/dsAiSupport/src/main/java/com/dsideal/aiSupport/Util/Liblib/Kit/SignUtil.java new file mode 100644 index 00000000..f801da3c --- /dev/null +++ b/dsAiSupport/src/main/java/com/dsideal/aiSupport/Util/Liblib/Kit/SignUtil.java @@ -0,0 +1,74 @@ +package com.dsideal.aiSupport.Util.Liblib.Kit; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import com.dsideal.aiSupport.Plugin.YamlProp; +import com.jfinal.kit.Prop; +import lombok.Getter; +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.RandomStringUtils; + +import static com.dsideal.aiSupport.AiSupportApplication.getEnvPrefix; + +public class SignUtil { + + // API访问凭证 + protected static final String ak; // Access Key + protected static final String sk; // Secret Key + public static Prop PropKit; // 配置文件工具 + + static { + //加载配置文件 + String configFile = "application_{?}.yaml".replace("{?}", getEnvPrefix()); + PropKit = new YamlProp(configFile); + ak = PropKit.get("LIBLIB.accessKey"); // 从配置文件获取Access Key + sk = PropKit.get("LIBLIB.secretKey"); // 从配置文件获取Secret Key + } + + /** + * 签名信息类,包含签名、时间戳和随机字符串 + */ + @Getter + public static class SignatureInfo { + private final String signature; + private final long timestamp; + private final String signatureNonce; + + public SignatureInfo(String signature, long timestamp, String signatureNonce) { + this.signature = signature; + this.timestamp = timestamp; + this.signatureNonce = signatureNonce; + } + + } + + /** + * 生成完整的签名信息,包括签名、时间戳和随机字符串 + * @param uri API接口的uri地址 + * @return 签名信息对象 + */ + public static SignatureInfo makeSignInfo(String uri) { + // 当前毫秒时间戳 + long timestamp = System.currentTimeMillis(); + // 随机字符串 + String signatureNonce = RandomStringUtils.randomAlphanumeric(10); + // 拼接请求数据 + String content = uri + "&" + timestamp + "&" + signatureNonce; + try { + // 生成签名 + SecretKeySpec secret = new SecretKeySpec(sk.getBytes(), "HmacSHA1"); + Mac mac = Mac.getInstance("HmacSHA1"); + mac.init(secret); + String signature = Base64.encodeBase64URLSafeString(mac.doFinal(content.getBytes())); + return new SignatureInfo(signature, timestamp, signatureNonce); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("no such algorithm"); + } catch (InvalidKeyException e) { + throw new RuntimeException(e); + } + } +} \ No newline at end of file