1. 主页 > 教程 > AIGC教程:Midjourney

我没有GPU也不懂AI,怎么做一个AI生成图片的APP?

最近AI生成图片(AIGC)很火,大家玩NovelAI和Midjourney都非常开心。 不过,AIGC还是处于一个很早期的阶段,大家还不太清楚它到底可以用在什么地方,为用户提供什么帮助,带来什么实际价值。对于广大程序员来说,哪怕大家有很多很好的想法,但是碍于AI大模型的高门槛,加上GPU计算高昂的价格,其实也很难实践。

那么我想,作为智源人工智能研究院的一名优秀的前端工程师,是时候站出来帮大家一把,为中国的AIGC事业的发展贡献出自己一点微薄的力量了!

其实说起来很简单,就是,我们可以薅一下智源的羊毛。智源开放了文生图大模型接口可以随便调用(https://github.com/FlagAI-Open/FlagAI/tree/master/examples/AltDiffusion),我们搞个前后端调用一下,就可以做一个简单的APP。在这个基础上加上点自己的创意,也许可以弄出个新的现象级应用呢!

下面我给大家介绍一下具体的步骤。其实很简单,就算是0基础的工程师,2个小时也可以搞定了。我先放个最后的APP效果视频:

一、准备阶段

1 环境要求

Node版本大于14 Java Development Kit [JDK] 11

2 开发环境搭建

本部分基于ReactNative中文文档,如遇到困难请阅读官方以获取更详细的指导。

本文以Windows作为开发平台,Android作为目标平台。

安装 Node 与 JDK

首先直接使用搜索引擎搜索下载 Node Java SE Development Kit (JDK) [11]

,注意 Node 的版本应大于等于 14。

在Node安装完成后打开cmd或PowerShell(以windows为例)并输入命令 node -v,此时若Node已经安装成功,则会显示当前node的版本,若版本小于14则需要重新安装14以上版本的Node。

在 Java Development Kit 安装完成后。你可以在命令行中输入 javac -version(请注意是 javac,不是 java)来查看你当前安装的 JDK 版本。

安装 Yarn

Yarn

是 Facebook 提供的替代 npm 的工具,可以加速 Node 模块的下载。请在确保 Node 已经安装完成后,打开cmd或PowerShell(以windows为例)并输入以下命令:npm install -g yarn。

安装完 yarn 之后就可以用 yarn 代替 npm 了,例如用yarn代替npm install命令,用yarn add 某第三方库名代替npm install 某第三方库名。

安装 Android 开发环境

安装 Android Studio

首先下载和安装 Android Studio 安装界面中选择"Custom"选项,确保选中了以下几项:

Android SDK Android SDK Platform Android Virtual Device

安装 Android SDK

你可以在 Android Studio 的欢迎界面中找到 SDK Manager。点击"Configure",然后就能看到"SDK Manager"。

在 SDK Manager 中选择"SDK Platforms"选项卡,然后在右下角勾选"Show Package Details"。展开Android 12 (S)选项,确保勾选了下面这些组件(重申你必须使用稳定的代理软件,否则可能都看不到这个界面):

Android SDK Platform 31Intel x86 Atom_64 System Image(官方模拟器镜像文件,使用非官方模拟器不需要安装此组件)

然后点击"SDK Tools"选项卡,同样勾中右下角的"Show Package Details"。展开"Android SDK Build-Tools"选项,确保选中了 React Native 所必须的31.0.0版本。你可以同时安装多个其他版本。

最后点击"Apply"来下载和安装这些组件。

配置 ANDROID_SDK_ROOT 环境变量

打开控制面板 -> 系统和安全 -> 系统 -> 高级系统设置 -> 高级 -> 环境变量 -> 新建,创建一个名为ANDROID_SDK_ROOT的环境变量(系统或用户变量均可),指向你的 Android SDK 所在的目录(具体的路径可能和下图不一致,请自行确认):

SDK 默认是安装在下面的目录:

C:\Users\你的用户名\AppData\Local\Android\Sdk

把一些工具目录添加到环境变量 Path

打开控制面板 -> 系统和安全 -> 系统 -> 高级系统设置 -> 高级 -> 环境变量,选中Path变量,然后点击编辑。点击新建然后把这些工具目录路径添加进去:platform-tools、emulator、tools、tools/bin

%ANDROID_SDK_ROOT%\platform-tools %ANDROID_SDK_ROOT%\emulator %ANDROID_SDK_ROOT%\tools %ANDROID_SDK_ROOT%\tools\bin

3 安装代码编辑工具

推荐使用 Visual Studio Code(免费)或者 WebStorm 作为开发工具。如果在使用 WebStorm 时遇到需要License的情况,可以先选择免费体验30天跳过此步。。

4 准备 Android 设备

使用 Android 真机

需用 usb 数据线连接到电脑,然后遵照在设备上运行这篇文档的说明操作。

使用 Android 模拟器

打开 Android Studio 创建一个虚拟设备。点击"Create Virtual Device",然后选择所需的设备类型并点击"Next",然后选择S API Level 31 image。

5 创建带 TypeScript 的新项目

注意事项

如果你之前全局安装过旧的react-native-cli命令行工具,请使用npm uninstall -g react-native-cli卸载掉它以避免一些冲突请不要在目录、文件名中使用中文、空格等特殊符号。请不要单独使用常见的关键字作为项目名(如 class, native, new, package 等等)。请不要使用与核心模块同名的项目名(如 react, react-native 等)。请不要在某些权限敏感的目录例如 System32 目录中 init 项目!会有各种权限限制导致不能运行!请不要使用一些移植的终端环境,例如git bash或mingw等等,这些在 windows 下可能导致找不到环境变量。请使用系统自带的命令行(CMD 或 powershell)运行。

初始化项目

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,同时 TypeScript 通过类型注解提供编译时的静态类型检查,更好的保证了代码的类型安全。

我们使用 React Native 内建的命令行工具来创建一个名为"TextToImgAPP"的新项目。这个命令行工具不需要安装,可以直接用 node 自带的npx命令来使用。我们可以通过--template来使用一些社区提供的模板,例如带有TypeScript配置。
npx react-native init TextToImgAPP --template react-native-template-typescript

如果出现以下提示,输入 y 继续

Need to install the following packages: react-native Ok to proceed? (y)

等待项目初始化完成后,进入TextToImgAPP文件夹就可以看到初始化后的项目源代码了。此时请确保已经运行了模拟器或者连接了Android真机,在项目目录中输入以下命令:

yarn android // 或者 yarn react-native run-android

此命令会对项目的原生部分进行编译,如果配置没有问题,你应该可以看到应用自动安装到设备上并开始运行。注意第一次运行时需要下载大量编译依赖,耗时可能数十分钟。如果项目成功运行,你将在模拟器或者真机中看到如下画面。

二、开发阶段

在项目初始化并成功启动后,我们便可以开始我们的开发工作了。我们将搭建一个非常简单的文生图APP,功能包含用户输入文字,选择图片生成数量与图片长宽。并向后端发送请求后接收生成的图片并进行展示。

首先我们在项目根目录下创建一个文件夹src,我们将在这个目录下编写我们的代码。在src文件夹中创建三个文件夹Components、Style、Utils分别存放我们编写的组件、样式和工具函数

开发前准备(可选)

本部分主要为代码风格与格式化设置

进入根目录/.eslintrc.js,在rules中添加以下两行代码:

react-native/no-inline-styles: off, semi: 0,

进入根目录/.prettierrc.js,在文件中添加以下代码:

printWidth: 120, semi: false, useTabs: false, tabWidth: 4,

1 编写按钮组件

在src/Components下创建两个文件BatchSizeButtonGroup.tsx和GenerateButton.tsx,我们将在这里编写设置图片数量按钮组件和生成图片按钮组件。

在BatchSizeButtonGroup.tsx中填入以下代码:
import React from react import {Text, TextStyle, TouchableHighlight, View} from react-native import {componentsStyleSheet} from ../Style/ComponentsStyleSheet type BatchSizeButtonGroupPropsType = { batchSize: number setBatchSize: React.Dispatch<React.SetStateAction<number>> } export const BatchSizeButtonGroup: React.FC<BatchSizeButtonGroupPropsType> = ({batchSize, setBatchSize}) => { return ( <View style={{display: flex, flexDirection: row, alignItems: center, marginBottom: 16}}> <Text style={{fontSize: 16, marginRight: 16}}>生成数量</Text> {[1, 2, 3, 4].map(value => ( <BatchSizeButton key={value} isCurSelected={value === batchSize} label={value.toString()} style={{marginRight: 16, borderRadius: 4}} onPress={() => { setBatchSize(value) }} /> ))} </View> ) } type StyledButtonPropsType = { label: string isCurSelected: boolean onPress: () => void style?: TextStyle } const BatchSizeButton: React.FC<StyledButtonPropsType> = ({label, isCurSelected, onPress, style}) => { return ( <TouchableHighlight onPress={onPress} underlayColor="#5E3A9E" style={{...style}}> <View style={[ componentsStyleSheet.batchSizeButton, {backgroundColor: isCurSelected ? #925FF0 : #E9DFFC}, ]}> <Text style={[componentsStyleSheet.batchSizeButtonText, {color: isCurSelected ? #FFFFFF : #784DC7}]}> {label} </Text> </View> </TouchableHighlight> ) }

在GenerateButton.tsx中填入以下代码:

import React from react import {Text, TouchableHighlight, View} from react-native import {componentsStyleSheet} from ../Style/ComponentsStyleSheet type GenerateButtonPropsType = { onPress: () => void disabled: boolean } export const GenerateButton: React.FC<GenerateButtonPropsType> = ({onPress, disabled}) => { return ( <TouchableHighlight onPress={onPress} style={{borderRadius: 40}} underlayColor={disabled ? #F4EFFE : #5E3A9E}> <View style={[componentsStyleSheet.generateButton, {backgroundColor: disabled ? #F4EFFE : #925FF0}]}> <Text style={[componentsStyleSheet.generateButtonText, {color: disabled ? #737373 : #FFFFFF}]}> 生成图片 </Text> </View> </TouchableHighlight> ) }

在src/Style下创建一个文件ComponentsStyleSheet.ts,并填入以下代码:

import {StyleSheet} from react-native export const componentsStyleSheet = StyleSheet.create({ styleTypeButton:{ display: flex, alignItems: center, justifyContent: center, height: 30, width: 50, borderRadius: 4, }, styleTypeButtonText: { fontSize: 16, fontWeight: 400, }, batchSizeButton: { display: flex, alignItems: center, justifyContent: center, height: 40, width: 40, borderRadius: 4, }, batchSizeButtonText: { fontSize: 20, fontWeight: 400, }, generateButton: { display: flex, flexDirection: row, alignItems: center, justifyContent: center, height: 40, width: 130, borderRadius: 40, }, generateButtonText:{ fontSize: 20, fontWeight: 300, } })

在src/Utils目录下创建文件StyleType.ts并填入以下代码:

export enum styleType { general = 通用, traditional = 国画, }

在src/Components目录下创建StyleTypeButtonGroup.tsx文件,并填入以下代码:

import React from react import {Text, TextStyle, TouchableHighlight, View} from react-native import {componentsStyleSheet} from ../Style/ComponentsStyleSheet import {styleType} from ../Utils/StyleType type styleButtonGroupPropsType = { imageStyle: styleType setImageStyle: React.Dispatch<React.SetStateAction<styleType>> } export const StyleTypeButtonGroup: React.FC<styleButtonGroupPropsType> = ({imageStyle, setImageStyle}) => { return ( <View style={{ display: flex, flexDirection: row, alignItems: center, marginTop: 16, marginBottom: 16, }}> <Text style={{fontSize: 16, marginRight: 16}}>生成数量</Text> <StyleTypeButton label={styleType.general} style={{marginRight: 16}} onPress={() => setImageStyle(styleType.general)} isCurSelected={imageStyle === styleType.general} /> <StyleTypeButton label={styleType.traditional} onPress={() => setImageStyle(styleType.traditional)} isCurSelected={imageStyle === styleType.traditional} /> </View> ) } type StyleTypeButtonPropsType = { label: styleType isCurSelected: boolean onPress: () => void style?: TextStyle } const StyleTypeButton: React.FC<StyleTypeButtonPropsType> = ({label, isCurSelected, onPress, style}) => { return ( <TouchableHighlight onPress={onPress} underlayColor="#5E3A9E" style={{...style}}> <View style={[ componentsStyleSheet.styleTypeButton, {backgroundColor: isCurSelected ? #925FF0 : #E9DFFC}, ]}> <Text style={[componentsStyleSheet.styleTypeButtonText, {color: isCurSelected ? #FFFFFF : #784DC7}]}> {label} </Text> </View> </TouchableHighlight> ) }

2 编写滑块组件

在这一部分我们需要引入@react-native-community/slider,切换到项目根目录,并在命令行输入以下指令yarn add @react-native-community/slider。等待依赖下载完成后,我们在src/Components目录下创建文件SizeSlider.tsx并填入以下代码

import React from react import {Text, View} from react-native import Slider from @react-native-community/slider type sizeSliderPropsType = { label: string size: number setSize: (payload: number) => void } export const SizeSlider: React.FC<sizeSliderPropsType> = ({label, size, setSize}) => { return ( <View style={{display: flex, flexDirection: row, alignItems: center, height: 40}}> <Text>{label}</Text> <Slider style={{width: 200, height: 40}} value={size} onValueChange={value => setSize(value)} minimumValue={512} maximumValue={1024} minimumTrackTintColor="#925FF0" maximumTrackTintColor="#000000" /> <Text style={{color: #925FF0}}>{size}</Text> </View> ) }

3 编写图片展示组件

在src/Components下创建ImageDisplay.tsx文件并填入以下代码:

import React from react import {ActivityIndicator, Image, View} from react-native type ImageDisplayPropsType={ isLoading:boolean imageData:Array<string> } export const ImageDisplay: React.FC<ImageDisplayPropsType> = ({isLoading,imageData}) => { return ( <View style={{display: flex, alignItems: center, paddingTop: 16, paddingBottom: 16}}> {isLoading ? <ActivityIndicator size="large" /> : null} {imageData.length > 0 && !isLoading ? imageData.map((imgData, idx) => ( <View key={idx} style={{marginTop: 8, marginBottom: 8}}> <Image resizeMethod="scale" resizeMode="contain" style={{height: 300, width: 300}} source={{ uri: imgData, }} /> </View> )) : null} </View> ) }

4 编写请求函数

在src/Utils目录下创建MessageData.ts文件并填入以下代码:

export type MessageData= { data: Array<Array<string>> is_generating: boolean duration: number average_duration: number }

在src/Utils目录下创建SendMessage.ts文件并填入以下代码:

export function sendMessage(body: any) { return new Promise((resolve, reject) => { fetch(http://81.70.134.89:8086/api/getmappedimagemessage/, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify(body), }) .then(response => { if (response.ok) { const res = response.json() resolve(res) } else { return reject(error) } }) .catch(error => reject(error)) }) }

5 修改 AndroidManifest

进入根目录/android/app/src/main/AndroidManifest.xml,找到以下代码:

android:windowSoftInputMode="adjustResize"

并将其替换为:

android:windowSoftInputMode="stateAlwaysHidden|adjustPan"

之后请确保在manifest标签内包含以下代码:

在application标签内包含属性:

android:usesCleartextTraffic="true"

6 编写主界面

在src/目录下创建文件HomeScreen.tsx,我们将在这里编写APP主界面的代码,文件创建完成后我们在文件中填入以下代码:

import React, {useState} from react import {ScrollView, Text, TextInput, View} from react-native import {sendMessage} from ./Utils/SendMessage import {mainStyleSheet} from ./Style/MainStyleSheet import {BatchSizeButtonGroup} from ./Components/BatchSizeButtonGroup import {GenerateButton} from ./Components/GenerateButton import {SizeSlider} from ./Components/SizeSlider import {StyleTypeButtonGroup} from ./Components/StyleTypeButtonGroup import {styleType} from ./Utils/StyleType import {ImageDisplay} from ./Components/ImageDisplay import {MessageData} from ./Utils/MessageData export function HomeScreen() { const [content, setContent] = useState<string>() const [imageStyle, setImageStyle] = useState<styleType>(styleType.general) const [imageHeight, setImageHeight] = useState<number>(512) const [imageWidth, setImageWidth] = useState<number>(512) const [batchSize, setBatchSize] = useState<number>(1) const [imageData, setImageData] = useState<Array<string>>([]) const [isLoading, setIsLoading] = useState<boolean>(false) const onPressGenerate = () => { if (isLoading) { return } setIsLoading(true) sendMessage({ text: content, styleType: imageStyle, num: batchSize, w: imageWidth, h: imageHeight, }) .then((response: any) => { const res = JSON.parse(response) as MessageData setIsLoading(false) setImageData(res.data[0]) }) .catch(err => { console.log(err) setIsLoading(false) }) } return ( <View style={{height: 100%, width: 100%, display: flex}}> <Text style={{width: 100%, fontSize: 32, textAlign: center}}>Text2Image</Text> <ScrollView style={{flex: 1, paddingLeft: 10%, paddingRight: 10%}}> <Text style={{fontSize: 16}}>请输入文字内容</Text> <ScrollView> <TextInput multiline numberOfLines={5} style={mainStyleSheet.textInput} onChangeText={text => setContent(text)} value={content} /> </ScrollView> <StyleTypeButtonGroup imageStyle={imageStyle} setImageStyle={setImageStyle} /> <BatchSizeButtonGroup batchSize={batchSize} setBatchSize={setBatchSize} /> <SizeSlider label={图片高度} size={imageHeight} setSize={setImageHeight} /> <SizeSlider label={图片宽度} size={imageWidth} setSize={setImageWidth} /> <ImageDisplay isLoading={isLoading} imageData={imageData} /> </ScrollView> <View style={mainStyleSheet.generateButton}> <GenerateButton onPress={onPressGenerate} disabled={isLoading} /> </View> </View> ) }

然后在src/Style目录下创建文件MainStyleSheet.ts,在其中填入以下代码

import {StyleSheet} from react-native export const mainStyleSheet = StyleSheet.create({ textInput: { width: 100%, textAlignVertical: top, marginTop: 16, background: #FFFFFF, borderColor: #DECFFB, borderWidth: 1, boxShadow: 0 4 8 rgba(151, 71, 255, 0.08), borderRadius: 8, }, generateButton: { marginTop: auto, marginBottom: 16, display: flex, alignItems: center, width: 100%, }, })

打开根目录/App.tsx文件,将文件中的代码替换为以下代码:

import React from react import {SafeAreaView, StatusBar, StyleSheet} from react-native import {HomeScreen} from ./src/HomeScreen const App = () => { return ( <SafeAreaView style={styles.mainContainer}> <StatusBar barStyle="light-content" /> <HomeScreen /> </SafeAreaView> ) } const styles = StyleSheet.create({ mainContainer: {width: 100%, height: 100%}, }) export default App

7 本地测试

在完成上述代码的编写后保存文件,按下 R 键,或是在开发者菜单中选择 Reload,将会在模拟器或真机中看到以下画面:

在文本输入框中输入对图片的描述,选择生成类型、数量与长宽后点击生成图片,等待一段时间后便可以看到生成的图片。

三、打包发布

本部分基于ReactNative官方文档,如遇到困难请阅读官方文档寻求进一步帮助。

1 生成签名密钥

Android 要求所有应用都有一个数字签名才会被允许安装在用户手机上,我们需要首先生成一个新的签名密钥,首先找到 jdk 的安装目录,在 Windows 上keytool命令放在 JDK 的 bin 目录中(比如C:\Program Files\Java\jdkx.x.x_x\bin)。打开 CMD 或者 powershell 进入该目录,然后输入以下指令:

keytool -genkeypair -v -storetype PKCS12 -keystore my-release-key.keystore -alias my-key-alias -keyalg RSA -keysize 2048 -validity 10000

该条命令会要求你设置一段密码与相关发行信息,请妥善保存你设置的密码。在运行完上述指令后,会在目录下生成一个文件my-release-key.keystore

2 设置 gradle 变量

把my-release-key.keystore文件放到你工程中的android/app文件夹下,打开项目目录/android/gradle.properties并添加如下代码:

MYAPP_RELEASE_STORE_FILE=my-release-key.keystore MYAPP_RELEASE_KEY_ALIAS=my-key-alias MYAPP_RELEASE_STORE_PASSWORD=***** MYAPP_RELEASE_KEY_PASSWORD=*****

其中*****是第一步时设置的密码

进入项目目录/android/app/build.gradle添加如下配置
... android { ... defaultConfig { ... } signingConfigs { release { if (project.hasProperty(MYAPP_RELEASE_STORE_FILE)) { storeFile file(MYAPP_RELEASE_STORE_FILE) storePassword MYAPP_RELEASE_STORE_PASSWORD keyAlias MYAPP_RELEASE_KEY_ALIAS keyPassword MYAPP_RELEASE_KEY_PASSWORD } } } buildTypes { release { ... signingConfig signingConfigs.release } } } ...

3 生成APK包

在项目根目录的终端中输入以下指令:

cd android ./gradlew assembleRelease

生成的 APK 文件位于android/app/build/outputs/apk/release/app-release.apk,它已经可以用来发布了。

4 测试发行版本

输入以下命令可以在设备上安装发行版本:

npx react-native run-android --variant=release
注意:你可能需要先将真机或虚拟机中安装的测试版APP删除,否则可能出现无法安装的情况

5 其他需要注意的问题

如果出现发行版APP安装成功但点击生成图片按钮时无法发送请求的情况,请检查项目根目录/android/app/src/main/AndroidManifest.xml文件,并确保manifest标签内包含以下代码:<uses-permission android:name="android.permission.INTERNET" />,以及在application标签内包含属性:android:usesCleartextTraffic="true",以保证APP有权限发起网络请求。

项目完整代码 :https://github.com/MX0723/TextToImageAPP

本文由某某资讯网发布,不代表某某资讯网立场,转载联系作者并注明出处:http://www.nxfun.com/index.php?m=home&c=View&a=index&aid=966

联系我们

在线咨询:点击这里给我发消息

微信号:weixin888

工作日:9:30-18:30,节假日休息