如何自己动手实现一个图片解答小助手

news2024/10/9 13:14:18

有一张图片如下所示:

Kimi上有一个功能,就是解析图片内容,给出回答:

image-20241008185201298

这样可以用于拍照向AI提问的场景,我自己也有这方面的需求,因此动手实践了一下。

自己动手实现的效果如下所示:

image-20241008185722470

那么自己如何实现呢?

可以通过添加一个OCR的功能来实现。中文图片文字识别也就是OCR效果比较好的是百度开源的PaddleOCR,之前介绍过PaddleOCR的.NET绑定PaddleSharp,见这篇文章:C#使用PaddleOCR进行图片文字识别。

之前使用PaddleOCR的时候,我已经在电脑上安装了一个虚拟环境,因为需求比较简单,就是将图片进行文字识别之后返回文本就行了,因此今天玩个不一样的,不用.NET绑定,直接调用Python脚本就好了。

那么现在拆解任务就是:

C#如何调用Python脚本?

那么就先来试一下,最简单的调用,调用Python脚本输出一个Hello:

print("Hello")

可以使用 System.Diagnostics.Process 类来启动一个外部进程来运行Python脚本:

 string pythonScriptPath = @"D:\学习路线\人工智能\图片文字识别\test.py"; // 替换为你的Python脚本路径
 string pythonExecutablePath = @"D:\SoftWare\Anaconda\envs\paddle_env\python.exe"; // 替换为你的Python解释器路径                                                                                     
 ProcessStartInfo start = new ProcessStartInfo();
 start.FileName = pythonExecutablePath;
 start.Arguments =$"{pythonScriptPath}";
 start.UseShellExecute = false;
 start.RedirectStandardOutput = true;
 start.RedirectStandardError = true;
 start.CreateNoWindow = true;

 using (Process process = Process.Start(start))
 {
     using (System.IO.StreamReader reader = process.StandardOutput)
     {
         string result = reader.ReadToEnd();
         MessageBox.Show(result);
     }

     using (System.IO.StreamReader errorReader = process.StandardError)
     {
         string errors = errorReader.ReadToEnd();
         if (!string.IsNullOrEmpty(errors))
         {
             MessageBox.Show("Errors: " + errors);
         }
     }
 }      

其中ProcessStartInfo各属性的解释如下:

  1. FileName
    • 含义:指定要启动的程序或文档的名称。
    • 示例:在这里,pythonExecutablePath 是 Python 解释器的路径,如 "C:\path\to\python.exe"
  2. Arguments
    • 含义:指定传递给要启动程序的命令行参数。
    • 示例:在这里,pythonScriptPath 是你要执行的 Python 脚本的路径,如 "C:\path\to\hello.py"
  3. UseShellExecute
    • 含义:指定是否使用操作系统 shell 来启动进程。如果设置为 false,则直接启动进程;如果设置为 true,则通过 shell 启动进程。
    • 示例:在这里,设置为 false,表示不使用 shell 启动进程,而是直接启动 Python 解释器。
  4. RedirectStandardOutput
    • 含义:指定是否将子进程的标准输出重定向到 Process.StandardOutput 流。
    • 示例:在这里,设置为 true,表示将 Python 脚本的输出重定向到 Process.StandardOutput,以便你可以读取它。
  5. RedirectStandardError
    • 含义:指定是否将子进程的标准错误输出重定向到 Process.StandardError 流。
    • 示例:在这里,设置为 true,表示将 Python 脚本的错误输出重定向到 Process.StandardError,以便你可以读取它。
  6. CreateNoWindow
    • 含义:指定是否在新窗口中启动进程。如果设置为 true,则不会创建新窗口;如果设置为 false,则会创建新窗口。
    • 示例:在这里,设置为 true,表示不创建新窗口,即在后台运行 Python 脚本。

现在查看一下运行效果:

image-20241008191313678

获取到了Python脚本输出的值。

那么再拆解一下任务,我们需要在命令行中传入一个参数,该如何实现呢?

import sys

# 检查是否有参数传递
if len(sys.argv) > 1:
    n = sys.argv[1]
    print(f"hello {n}")
else:
    print("请提供一个参数")

只需修改下图中,这两处地方即可:

image-20241008191639208

现在再来试下效果:

image-20241008191805671

成功在命令行中传入了一个参数。

那么现在我们的准备工作已经做好了。

PaddleOCR的使用脚本如下:

import sys
import logging
from paddleocr import PaddleOCR, draw_ocr

# Paddleocr目前支持的多语言语种可以通过修改lang参数进行切换
# 例如`ch`, `en`, `fr`, `german`, `korean`, `japan`


# 检查是否有参数传递
if len(sys.argv) > 1:
    imagePath = sys.argv[1]
else:
    print("请提供一个参数")

# 配置日志级别为 WARNING,这样 DEBUG 和 INFO 级别的日志信息将被隐藏
logging.basicConfig(level=logging.WARNING)

# 创建一个自定义的日志处理器,将日志输出到 NullHandler(不输出)
class NullHandler(logging.Handler):
    def emit(self, record):
        pass

# 获取 PaddleOCR 的日志记录器
ppocr_logger = logging.getLogger('ppocr')

# 移除所有默认的日志处理器
for handler in ppocr_logger.handlers[:]:
    ppocr_logger.removeHandler(handler)

# 添加自定义的 NullHandler
ppocr_logger.addHandler(NullHandler())

ocr = PaddleOCR(use_angle_cls=True, lang="ch")  # need to run only once to download and load model into memory
img_path = imagePath
result = ocr.ocr(img_path, cls=True)
for idx in range(len(result)):
    res = result[idx]   
    for line in res:
        print(line[1][0])

在vs code中运行效果如下所示:

image-20241008192131148

现在在WPF应用中调用结果如下:

image-20241009104512270

现在图片文字识别的部分已经搞定了。

现在就需要与大语言模型结合起来了,就是将识别出来的文字,丢给大语言模型。

可以这样写:

 public async IAsyncEnumerable<string> GetAIResponse4(string question, string imagePath)
 {
     string pythonScriptPath = @"D:\学习路线\人工智能\图片文字识别\test.py"; // 替换为你的Python脚本路径
     string pythonExecutablePath = @"D:\SoftWare\Anaconda\envs\paddle_env\python.exe"; // 替换为你的Python解释器路径                                                                                         
     string arguments = imagePath; // 替换为你要传递的参数

     ProcessStartInfo start = new ProcessStartInfo();
     start.FileName = pythonExecutablePath;
     start.Arguments = $"{pythonScriptPath} {arguments}";
     start.UseShellExecute = false;
     start.RedirectStandardOutput = true;
     start.RedirectStandardError = true;
     start.CreateNoWindow = true;

     string result = "";

     using (Process process = Process.Start(start))
     {
         using (System.IO.StreamReader reader = process.StandardOutput)
         {
             result = reader.ReadToEnd();                   
         }

         using (System.IO.StreamReader errorReader = process.StandardError)
         {
             string errors = errorReader.ReadToEnd();
             if (!string.IsNullOrEmpty(errors))
             {
                 MessageBox.Show("Errors: " + errors);
             }
         }
     }

     string skPrompt = """
                        获取到的图片内容:{{$PictureContent}}。
                        根据获取到的信息回答问题:{{$Question}}""";
     await foreach (var str in _kernel.InvokePromptStreamingAsync(skPrompt, new() { ["PictureContent"] = result, ["Question"] = question }))
     {
         yield return str.ToString();
     }
 }

就可以实现如下的效果了:

image-20241009104837084

全部代码可在https://github.com/Ming-jiayou/SimpleRAG看到。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2199101.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

websocket连接异常报错1006

目录&#xff1a; 1、问题现象2、问题原因3、解决方案 1、问题现象 WebSocket状态码的作用&#xff1a; 在WebSocket协议中&#xff0c;状态码用于表示连接状态和错误信息。通过状态码&#xff0c;我们可以快速判断连接是否成功&#xff0c;以及出现错误时的原因。常见的WebSo…

makefile常见问题记录

1 Makefile:8 *** missing separator. Stop. 可能原因1&#xff1a;makefile的命令行开头必须使用Tab键 如图1所示&#xff0c;红框内为一个命令行&#xff0c;图2的缩进由敲空格实现&#xff0c;会标红&#xff0c;报错&#xff0c;图3的缩进为按Tab键&#xff0c;语法正确&…

golang接口详解

interface(接口) 接口&#xff08;interface&#xff09;定义了一个对象的行为规范&#xff0c;只定义规范不实现&#xff0c;由具体的对象来实现规范的细节 在Go语言中接口&#xff08;interface&#xff09;是一种类型&#xff0c;一种抽象的类型。相较于之前章节中讲到的那…

揭秘破解密码的常见方法和手段

前言 今天小编就帮粉丝朋友科普一下&#xff0c;破解密码的常见方法和手段&#xff0c;希望看到本文的粉丝朋友&#xff0c;给小编点赞支持支持一波 键盘监听木马 键盘监听病毒在网吧中非常流行&#xff0c;它在启动后会监听用户的键盘输入事件&#xff0c;如果有人使用账号密…

AD画完原理图,进行编译可就是不弹出错误窗口,明明原理图有错误(AD中Error Reporting设置)

AD“工程选项”对话框用于设置大量绘图和组件配置检查&#xff0c;共有12个选项卡&#xff0c;“Error Reporting&#xff08;错误报告&#xff09;”是第一个选项卡。 “Error Reporting&#xff08;错误报告&#xff09;”选项卡包含8页错误&#xff0c;“Violations Associ…

千寻位置大气增强服务为高效农耕作业保驾护航

2024年9月14日广东湛江&#xff0c;千耘QYX农机导航用户为了抢农时&#xff0c;夜晚使用导航作业&#xff0c;但导航界面一直显示浮点解&#xff0c;无法进行作业&#xff0c;非常着急&#xff0c;联系当地经销商寻求解决办法&#xff0c;经过查看&#xff0c;是因为当时电离层…

AI改革的双刃剑:从生成式AI到推理式AI的未来之路

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

一文彻底搞懂大模型 - Dify(Agent + RAG)

**Dify 是一个用于构建 AI 应用程序的开源平台。****Dify融合了后端即服务&#xff08;Backend as Service&#xff09;和LLMOps理念。它支持多种大型语言模型&#xff0c;如Claude3、OpenAI等&#xff0c;并与多个模型供应商合作&#xff0c;确保开发者能根据需求选择最适合的…

python中计算分布的分位数及累积概率

本文讨论python中怎样计算分布的分位数及累积概率 ⭐️ 根据累计概率获取分位数 在 Python 中&#xff0c;你可以使用 scipy.stats 中的 ppf&#xff08;percent point function&#xff09;来根据累积概率获取分位数。ppf 是逆累积分布函数&#xff0c;也就是根据给定的累积…

【前端安全】逆向webpack加密算法

❤️博客主页&#xff1a; iknow181 &#x1f525;系列专栏&#xff1a; 网络安全、 Python、JavaSE、JavaWeb、CCNP &#x1f389;欢迎大家点赞&#x1f44d;收藏⭐评论✍ 0x01 webpack 简介 webpack一个静态模块打包器&#xff0c;有入口、出口、loader和插件&#xff0c;通过…

LLM RAG面试问题大全!

01 引言 RAG在通用人工智能、数据科学和人工智能的发展领域中起到了变革性的作用。RAG模型让机器能够基于事实产生更准确、连贯和一致的语言&#xff0c;它改变了人类与技术的互动方式。RAG让能够撰写独特内容、引人入胜的产品描述和新闻文章的机器人概念成为现实。尽管RAG的重…

c++primer第十四章代码重用

包含对象成员的类 包含&#xff1a;私有部分有一个类。 使用模板类&#xff0c;声明对象时必须指定具体的数据类型。 范例&#xff1a; #ifndef STUDENTC_H_ #define STUDENTC_H_ #include <iostream> #include <string> #include <valarray> class Stude…

【Sceneform-EQR】(手势控制器实现)通过手势事件实现在AR/VR等三维场景中的控制模型旋转、平移与缩放

在Sceneform-EQR中实现旋转平移缩放手势 实现在AR/VR等三维场景&#xff0c;通过手势控制模型节点的缩放、平移和旋转。 实现思路 实现模型旋转 Sceneform-EQR(filament\opengl)中采用右手坐标系。通过欧拉角进行旋转采用Z->Y->X的顺序&#xff0c;在这里&#xff0c;…

dart-sass和node-sass的区别,使用dart-sass后可能会出现的问题

前言&#xff1a; 2020 年 10 月 27 日&#xff0c;Sass 官方团队正式宣布 Libsass 将弃用&#xff0c;以及基于它的 Node Sass 和 SassC&#xff0c;并且建议用户使用 Dart Sass。如果在 vue 脚手架搭建的项目中需要使用 sass&#xff0c;建议初始化时勾选 sass 配置&#xff…

MySQL数据库备份与恢复:全面指南

MySQL数据库备份与恢复&#xff1a;全面指南 MySQL数据库是许多应用程序的核心组件&#xff0c;数据的安全和可用性至关重要。无论是由于硬件故障、软件错误&#xff0c;还是人为操作失误&#xff0c;数据丢失的风险都时刻存在。因此&#xff0c;做好数据库的备份与恢复工作是…

前端无感刷新token机制(一文说明白)

前言 用户登录之后&#xff0c;会返回一个用户的标识&#xff0c;之后带上这个标识请求别的接口&#xff0c;就能识别出该用户。 标识登录状态的方案有两种&#xff1a; session 和 jwt。这两种方案一个服务端存储&#xff0c;通过 cookie 携带标识&#xff0c;一个在客户端存…

研发中台拆分之路:深度剖析、心得总结与经验分享

背景在 21 年&#xff0c;中台拆分在 21 年&#xff0c;以下为中台拆分的过程心得&#xff0c;带有一定的主观&#xff0c;偏向于中小团队中台建设参考&#xff08;这里的中小团队指 3-100 人的团队&#xff09;&#xff0c;对于大型团队不太适用&#xff0c;毕竟大型团队人中 …

leetcode C++特性 AIDL的一些细节

leetcode细节 C的一些特性 【C基础】std::move用法介绍-CSDN博客 c thread的join和joinable的区别_thread joinable-CSDN博客 C线程介绍_std::thread 头文件-CSDN博客 https://blog.csdn.net/weixin_46645965/article/details/136259902 【C】—— 观察者模式-CSDN博客 C 迭…

LabVIEW交直流接触器动态检测系统

LabVIEW软件与霍尔传感器技术结合的交直流接触器动态检测系统通过实时数据采集和处理技术&#xff0c;有效地测量并分析交直流接触器在吸合及吸持阶段的电流和电压变化&#xff0c;以及相应的功率消耗&#xff0c;从而优化电力和配电系统的性能和可靠性。 项目背景 交直流接触…

在供应商准入时,如何规避风险、提高效率?

在进行供应商准入时&#xff0c;进行风险审核是至关重要的步骤&#xff0c;它有助于确保供应链的稳定性和企业的长期成功。通过风险审核&#xff0c;企业可以确保供应商提供的产品或服务符合质量标准&#xff0c;同时评估供应商的财务稳健性&#xff0c;以降低供应链中断的风险…