react native(学习笔记第三课) 英语打卡微应用(2)-从上传图片开始

文章目录

  • [react native(学习笔记第三课) 英语打卡微应用(2)-从上传图片开始](#react native(学习笔记第三课) 英语打卡微应用(2)-从上传图片开始)
    • [1. 构建手机的拍照功能](#1. 构建手机的拍照功能)
      • [1.1 使用`react native`的`react native image picker`包](#1.1 使用react nativereact native image picker包)
      • [1.2 定义`takePhoto`的函数](#1.2 定义takePhoto的函数)
    • 2.构建手机的图片选择功能
      • [2.1 使用`react native`的`react native image picker`库](#2.1 使用react nativereact native image picker库)
      • [2.2 定义`selectFromGallery`的函数](#2.2 定义selectFromGallery的函数)
    • [3. 界面运行效果](#3. 界面运行效果)
    • [4. 创建后端(`python+web`)程序](#4. 创建后端(python+web)程序)
      • [4.1 后端(`python+web`)程序的架构](#4.1 后端(python+web)程序的架构)
      • [4.2 `python + FastAPI`构建](#4.2 python + FastAPI构建)
      • [4.3 `python + FastAPI`的程序入口](#4.3 python + FastAPI的程序入口)
      • [4.4 为英文文章的`ocr`创建`routes`](#4.4 为英文文章的ocr创建routes)
      • [4.5 前端应用上传图片文件](#4.5 前端应用上传图片文件)
      • [4.5 继续后端的`ocr service`之前,首先来导入`Customize LLM`](#4.5 继续后端的ocr service之前,首先来导入Customize LLM)
      • [4.6 实现后端的`ocr service`](#4.6 实现后端的ocr service)
    • [5. 准备智谱`AI`的`api key`](#5. 准备智谱AIapi key)
      • [5.1 为什么智谱`AI`的`api key`](#5.1 为什么智谱AIapi key)
      • [5.2 在后端应用`backend`中设定智谱`AI`的`api key`](#5.2 在后端应用backend中设定智谱AIapi key)
    • [6. 启动后端(`backend`)程序](#6. 启动后端(backend)程序)
      • [6.1 进入程序的根目录](#6.1 进入程序的根目录)
      • [6.2 执行命令](#6.2 执行命令)
    • [7. 启动前端程序进行识别成文字演示](#7. 启动前端程序进行识别成文字演示)
      • [7.1 启动前端程序](#7.1 启动前端程序)
        • [7.1.1 修改后端地址](#7.1.1 修改后端地址)
        • [7.1.2 启动前端程序](#7.1.2 启动前端程序)
      • [7.2 在前端程序中操作](#7.2 在前端程序中操作)

react native(学习笔记第三课) 英语打卡微应用(2)-从上传图片开始

  • 构建手机的拍照功能
  • 构建手机的图片选择功能
  • 界面运行效果
  • 创建后端(python+web)程序
  • 准备智谱AIapi key
  • 启动后端(backend)程序
  • 使用zhipu AI识别成文字演示

1. 构建手机的拍照功能

这里,使用react native,让手机应用具备照片拍照功能。
项目代码

使用AI,可以很快构筑想要的功能。

1.1 使用react nativereact native image picker

typescript 复制代码
import {
  launchCamera,
  launchImageLibrary,
  ImagePickerResponse,
  Asset,
} from 'react-native-image-picker';

引入了这个react native的包,并且导入了launchCamera,就可以进行拍照的功能开发了。

1.2 定义takePhoto的函数

typescript 复制代码
  // 拍照
  const takePhoto = async () => {
    launchCamera(
      {
        mediaType: 'photo',
        quality: 0.8,
        saveToPhotos: true,
      },
      (response: ImagePickerResponse) => {
        if (response.didCancel) {
          console.log('用户取消了拍照');
        } else if (response.errorCode) {
          Alert.alert('错误', `拍照失败: ${response.errorMessage}`);
        } else if (response.assets && response.assets.length > 0) {
          setPhoto(response.assets[0]);
          setExtractedText(''); // 清空之前的文本
        }
      },
    );
  };

2.构建手机的图片选择功能

除了英语文章手机的拍照,可能有对相册里面的英语文章图片进行上传的需要,同样这里,提供相册选择图片的机能。

2.1 使用react nativereact native image picker

typescript 复制代码
import {
  launchCamera,
  launchImageLibrary,
  ImagePickerResponse,
  Asset,
} from 'react-native-image-picker';

引入了这个react native的包,并且导入了launchImageLibrary,就可以进行相册的图片选择功能开发了。

2.2 定义selectFromGallery的函数

typescript 复制代码
  // 从相册选择
  const selectFromGallery = () => {
    launchImageLibrary(
      {
        mediaType: 'photo',
        quality: 0.8,
      },
      (response: ImagePickerResponse) => {
        if (response.didCancel) {
          console.log('用户取消了选择');
        } else if (response.errorCode) {
          Alert.alert('错误', `选择图片失败: ${response.errorMessage}`);
        } else if (response.assets && response.assets.length > 0) {
          setPhoto(response.assets[0]);
          setExtractedText(''); // 清空之前的文本
        }
      },
    );
  };

3. 界面运行效果

这里有两个区域:

  1. 第一个区域是图片选择或者拍照区域
  2. 第二个区域是将图片从手机上上传到后端backend。在后端

4. 创建后端(python+web)程序

为什么要创建后端程序呢,因为需要将上传的图片上传给AI进行解析,之后会将解析之后的文字上传到数据库,以上传时间和主题进行database上的保存。所以开发前端frontend的同时,在开发一个backend的程序。
后端程序代码

4.1 后端(python+web)程序的架构

  • 采用python + FastAPI作为web的开发引擎。
  • 采用Zhupu AI进行图片的识别。->智谱AI的官网
  • 采用python + langchain进行AI的调用。

4.2 python + FastAPI构建

这里是整体的python + FastAPI文件夹结构,下面详细介绍每个部分。

4.3 python + FastAPI的程序入口

python 复制代码
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
from app.routes import ocr  # 稍后创建
import os
from dotenv import load_dotenv

# 加载环境变量
load_dotenv()

# 创建FastAPI应用实例
app = FastAPI(
    title="Book OCR API",
    description="将英文书图片转换为文字的API服务",
    version="1.0.0"
)

# 配置CORS(允许React Native App访问)
app.add_middleware(
    CORSMiddleware,
    allow_origins=["*"],  # 生产环境应该指定具体域名
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# 注册路由
app.include_router(ocr.router, prefix="/api/v1", tags=["english_checker"])

@app.get("/")
async def root():
    return {
        "message": "API is running",
        "docs_url": "/docs",
        "version": "1.0.0"
    }

@app.get("/health")
async def health_check():
    return {"status": "healthy"}

4.4 为英文文章的ocr创建routes

python 复制代码
from fastapi import APIRouter, UploadFile, File, HTTPException
from app.services.ocr_service import OCRService
import os
import logging
import base64

router = APIRouter()
ocr_service = OCRService()

# 配置日志
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)  # 创建 logger 实例


@router.post("/ocr/extract-text")
async def extract_text_from_image(
    file: UploadFile = File(..., description="上传英文书的图片")
):
    """
    从上传的图片中提取文字
    """
    # 验证文件类型
    if not file.content_type.startswith('image/'):
        raise HTTPException(
            status_code=400, 
            detail="文件类型必须是图片"
        )
    
    # 验证文件大小(例如限制10MB)
    file_size = 0
    content = await file.read()
    file_size = len(content)
    
    # 转换为 Base64 字符串
    base64_string = base64.b64encode(content).decode('utf-8')
    decoded_start = base64.b64decode(base64_string[:12])  # 解码前 12 个 Base64 字符
    print(f"文件头(十六进制): {decoded_start.hex()}")

    # 根据十六进制判断文件类型
    if decoded_start.startswith(b'\x89PNG\r\n\x1a\n'):
        data_url = f"data:image/png;base64,{base64_string}"
    elif decoded_start.startswith(b'\xff\xd8\xff'):
        data_url = f"data:image/jpeg;base64,{base64_string}"
    
    if file_size > 10 * 1024 * 1024:  # 10MB
        raise HTTPException(
            status_code=400,
            detail="文件大小不能超过10MB"
        )
    
    try:
        # 调用AI进行OCR解析
        extracted_text = await ocr_service.extract_text(data_url)
        
        return {
            "success": True,
            "filename": file.filename,
            "extracted_text": extracted_text,
            "word_count": len(extracted_text.split()),
            "character_count": len(extracted_text)
        }
    
    except Exception as e:
        raise HTTPException(
            status_code=500,
            detail=f"文字提取失败: {str(e)}"
        )

这里,后端程序接收到了image文件,之后就进行base64的转换,最后加上先头的data:image/jpeg;base64,,转成LLM大模型需要的数据格式。

之后,进行ocr_service.extract_text(data_url)的调用,从routes层调用到service层。

4.5 前端应用上传图片文件

在进行service层的解析之前,先看看前端的代码是如何进行进行image文件上传的。

typescript 复制代码
    try {
      // 创建 FormData
      const formData = new FormData();
      
      // 添加图片文件
      formData.append('file', {
        uri: photo.uri,
        type: photo.type || 'image/jpeg',
        name: photo.fileName || 'photo.jpg',
      });

      // 添加描述(如果有)
      if (description) {
        formData.append('description', description);
      }

      // 替换为你的后端API地址
      const response = await fetch('http://192.168.1.107:8000/api/v1/ocr/extract-text', {
        method: 'POST',
        body: formData,
      });

      const result = await response.json();

      if (response.ok) {
        // 从后端响应中提取 extracted_text

后端通过async def extract_text_from_image(file: UploadFile = File(..., description="上传英文书的图片"))进行前端上传文件的解析。

4.5 继续后端的ocr service之前,首先来导入Customize LLM

这里,需要导入智谱AI,这里使用前面的Customize LLM的文章来开发。
AI(学习笔记第十一课) 使用langchain的multimodality

在工程的文件夹app的下面,创建了ai的子文件夹,作为LLM的目录。

这里,可以看出,作为图片解析的LLM,接受三种image输入格式:

  • 本地处理路径(file://d:/test.jpeg)
  • base64的数据流(image bytes)这次使用的就是这种方式
  • 网络的url来表示的网络图片

    这里,主要是采用langchain的自定义customize大模型LLM,继承BaseChatModel这个父类,写出自己的_generate方法,来在应用程序和商业的LLM之前搭一个桥梁,让其他应用程序都能够自如的调用LLM,并能够增加一些customize处理。

4.6 实现后端的ocr service

从上面可以看到,/routes/ocr.py里面,已经在对接受的文件进行了base64的转换,并根据文件的形式不同加上data:image/png;base64或者data:image/jpeg;base64之后,直接就交给了/services/ocr_service.py这里处理。

python 复制代码
# 转换为 Base64 字符串
    base64_string = base64.b64encode(content).decode('utf-8')
    decoded_start = base64.b64decode(base64_string[:12])  # 解码前 12 个 Base64 字符
    print(f"文件头(十六进制): {decoded_start.hex()}")

    # 根据十六进制判断文件类型
    if decoded_start.startswith(b'\x89PNG\r\n\x1a\n'):
        data_url = f"data:image/png;base64,{base64_string}"
    elif decoded_start.startswith(b'\xff\xd8\xff'):
        data_url = f"data:image/jpeg;base64,{base64_string}"
    
    if file_size > 10 * 1024 * 1024:  # 10MB
        raise HTTPException(
            status_code=400,
            detail="文件大小不能超过10MB"
        )
    
    try:
        # 调用AI进行OCR解析
        extracted_text = await ocr_service.extract_text(data_url)

接下来,实现ocr_service.py的这个类。很容易想到,这里的ocr_service.py主要是调用刚才的Customize LLMGLM45V,将前端传递过来的imagebase64数据传递给LLM

pyhon 复制代码
import io
from PIL import Image
import asyncio
from functools import partial
import logging
import numpy as np
import os

from langchain_core.messages import HumanMessage
from app.ai.custom_llm_zhipu import GLM45V
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)  # 创建 logger 实例

def create_image_analyzer(api_key):
    llm = GLM45V(api_key=api_key)
    def analyze_image(image_path, prompt_text="请将这张图片英文抽取出来"):
        """分析图片内容"""
        message = HumanMessage(content=[
            {
                "type": "text",
                "text": prompt_text
            },
            {
                "type": "image_url",
                "image_url": {
                    "url": image_path
                }
            }
        ])
        result = llm.generate([[message]])
        return result.generations[0][0].text
   return analyze_image

class OCRService:
    async def extract_text(self, base64_url) -> str:
        try:
            # 在线程池中运行CPU密集型的OCR任务
            # 这样可以避免阻塞FastAPI的异步事件循环
            loop = asyncio.get_event_loop()
            api_key = os.getenv("ZHIPU_AI_API_KEY")
            logger.info(f"api key is :{api_key}")
            analyzer = create_image_analyzer(api_key)
            result = analyzer(base64_url)
            print("详细分析:", result)
            return result
        except Exception as e:
            logger.error(f"文字提取失败: {str(e)}", exc_info=True)  # exc_info=True 会记录完整的堆栈跟踪
            raise Exception(f"OCR处理失败: {str(e)}")

5. 准备智谱AIapi key

5.1 为什么智谱AIapi key

由于使用智谱AI进行一下的两个功能:

  • 对于英语的文章拍照上传,之后进行OCR文字抽取
  • 对于抽取到的文字进行转语音的操作,使用智谱AI的语音模型
    所以需要开设一个智谱AIAPI key

5.2 在后端应用backend中设定智谱AIapi key

这里,.env.example文件已经给出了设定的key,在.env中设定了这个文件就可以了。

这里尝试了一下,解析一个图片,花费不到0.1元,感觉作为开发测试还是可以的。

6. 启动后端(backend)程序

6.1 进入程序的根目录

shell 复制代码
cd D:\00_study\17_react_native\english_checker_backend

6.2 执行命令

代码库已经将启动的命令写入了start_commands.txt文件中,可以按照这个执行

shell 复制代码
# 安装python的evnv环境
python -m mvenv venv
# 激活python的venv
venv\Scripts\activate
# pip安装
pip install -r requirements.txt
# 启动backend 工程
python -m uvicorn app.main:app --reload --host=0.0.0.0 --log-level debug
  • 注意1:这里--host=0.0.0.0一定要执行,否则,只有本机的localhost或者127.0.0.1好用,手机应用上到backend的访问不要用。*
  • 注意2:这里,一定要用家里的宽带,使手机应用和backend应用在一个局域网里面。当然,等到开发完毕的时候正式环境,应该使用公共的域名服务DNS

7. 启动前端程序进行识别成文字演示

7.1 启动前端程序

7.1.1 修改后端地址

后端地址修改

这里由于是开发途中,所以暂时还是hardcoding的,最后应该问问AI,对于前端工程,后端工程的url如何进行定义(hardcoding解决方案)。

这里已经修改成了后端的地址。

但是,如果使用手机开热点,之后PC进行连接,那么这种模式下,手机不能连接上PC。必须要使用同一局域网下才可以。

7.1.2 启动前端程序

启动之后,就可以看到手机的画面,非常简单的一个画面。

7.2 在前端程序中操作

进行拍照-> 上传,等待后端对图片image进行一会的解析之后,就会在识别结果的框内出现了识别好的英文的文字。之后,继续对文字进行转成标准英文语音的功能开发。

相关推荐
北冥有鱼被烹1 小时前
【玩索】【英语武器系统之1】用塞尔达武器系统学习英语语法
学习·english
李李李勃谦2 小时前
鸿蒙PC打造电子书阅读器:支持 EPUB/PDF、书签同步、笔记管理
笔记·华为·pdf·harmonyos
开开心心就好2 小时前
整合多家平台资源的免费学习应用
人工智能·vscode·学习·游戏·音视频·语音识别·媒体
U盘失踪了2 小时前
【笔记】Cookie 请求头的写法
笔记
Brilliantwxx2 小时前
【C++】认识vector(概念+题目OJ)
开发语言·c++·笔记·算法
m0_46644103詹湛2 小时前
(一)FPGA :基础概念详解(Xilinx平台)
笔记·学习·fpga开发·verilog
zuozewei2 小时前
Agent Teams 实验笔记:让 Claude Code 三个 Agent 跑一遍 Todo Demo
笔记
_李小白2 小时前
【android opencv学习笔记】Day 5: 高效的图像扫描
android·opencv·学习
空中海2 小时前
05 React Native架构设计、主线项目与专家实践
javascript·react native·react.js