Skip to content

Commit

Permalink
[All] ImageOcr (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
pk5ls20 authored Oct 30, 2024
1 parent 94b6390 commit 54def3f
Show file tree
Hide file tree
Showing 10 changed files with 316 additions and 10 deletions.
49 changes: 49 additions & 0 deletions Lagrange.Core/Common/Entity/ImageOcrResultEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using System.Text.Json.Serialization;

namespace Lagrange.Core.Common.Entity
{
[Serializable]
public class ImageOcrResult
{
[JsonPropertyName("texts")] public List<TextDetection> Texts { get; set; }

[JsonPropertyName("language")] public string Language { get; set; }

public ImageOcrResult(List<TextDetection> texts, string language)
{
Texts = texts;
Language = language;
}
}

[Serializable]
public class TextDetection
{
[JsonPropertyName("text")] public string Text { get; set; }

[JsonPropertyName("confidence")] public int Confidence { get; set; }

[JsonPropertyName("coordinates")] public List<Coordinate> Coordinates { get; set; }

public TextDetection(string text, int confidence, List<Coordinate> coordinates)
{
Text = text;
Confidence = confidence;
Coordinates = coordinates;
}
}

[Serializable]
public class Coordinate
{
[JsonPropertyName("x")] public int X { get; set; }

[JsonPropertyName("y")] public int Y { get; set; }

public Coordinate(int x, int y)
{
X = x;
Y = y;
}
}
}
9 changes: 9 additions & 0 deletions Lagrange.Core/Common/Interface/Api/OperationExt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,4 +261,13 @@ public static Task<bool> GroupJoinEmojiChain(this BotContext bot, uint groupUin,

public static Task<bool> FriendJoinEmojiChain(this BotContext bot, uint friendUin, uint emojiId, uint targetMessageSeq)
=> bot.ContextCollection.Business.OperationLogic.FriendJoinEmojiChain(friendUin, emojiId, targetMessageSeq);

public static Task<string> UploadImage(this BotContext bot, ImageEntity entity)
=> bot.ContextCollection.Business.OperationLogic.UploadImage(entity);

public static Task<ImageOcrResult?> OcrImage(this BotContext bot, string url)
=> bot.ContextCollection.Business.OperationLogic.ImageOcr(url);

public static Task<ImageOcrResult?> OcrImage(this BotContext bot, ImageEntity entity)
=> bot.ContextCollection.Business.OperationLogic.ImageOcr(entity);
}
Original file line number Diff line number Diff line change
Expand Up @@ -717,4 +717,28 @@ public async Task<bool> FriendJoinEmojiChain(uint friendUin, uint emojiId, uint
var results = await Collection.Business.SendEvent(friendJoinEmojiChainEvent);
return results.Count != 0 && results[0].ResultCode == 0;
}

public async Task<string> UploadImage(ImageEntity image)
{
await Collection.Highway.ManualUploadEntity(image);
var msgInfo = image.MsgInfo;
if (msgInfo is null) throw new Exception();
var downloadEvent = ImageDownloadEvent.Create(Collection.Keystore.Uid ?? "", msgInfo);
var result = await Collection.Business.SendEvent(downloadEvent);
var ret = (ImageDownloadEvent)result[0];
return ret.ImageUrl;
}

public async Task<ImageOcrResult?> ImageOcr(string imageUrl)
{
var imageOcrEvent = ImageOcrEvent.Create(imageUrl);
var results = await Collection.Business.SendEvent(imageOcrEvent);
return results.Count != 0 ? ((ImageOcrEvent)results[0]).ImageOcrResult : null;
}

public async Task<ImageOcrResult?> ImageOcr(ImageEntity image)
{
var imageUrl = await UploadImage(image);
return await ImageOcr(imageUrl);
}
}
25 changes: 25 additions & 0 deletions Lagrange.Core/Internal/Event/Action/ImageOcrEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using Lagrange.Core.Common.Entity;

namespace Lagrange.Core.Internal.Event.Action;

internal class ImageOcrEvent : ProtocolEvent
{
public string Url { get; } = string.Empty;

public ImageOcrResult ImageOcrResult { get; }

private ImageOcrEvent(string url) : base(true)
{
Url = url;
ImageOcrResult = new ImageOcrResult(new List<TextDetection>(), "");
}

private ImageOcrEvent(int resultCode, ImageOcrResult result) : base(resultCode)
{
ImageOcrResult = result;
}

public static ImageOcrEvent Create(string url) => new(url);

public static ImageOcrEvent Result(int resultCode, ImageOcrResult result) => new(resultCode, result);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
using ProtoBuf;

#pragma warning disable CS8618
// ReSharper disable InconsistentNaming

namespace Lagrange.Core.Internal.Packets.Service.Oidb.Request;

[ProtoContract]
[OidbSvcTrpcTcp(0xE07, 0)]
internal class OidbSvcTrpcTcp0xE07_0
{
[ProtoMember(1)] public uint Version { get; set; }

[ProtoMember(2)] public uint Client { get; set; }

[ProtoMember(3)] public uint Entrance { get; set; }

[ProtoMember(10)] public OcrReqBody OcrReqBody { get; set; }
}

[ProtoContract]
internal class OcrReqBody
{
[ProtoMember(1)] public string ImageUrl { get; set; }

[ProtoMember(2)] public uint LanguageType { get; set; }

[ProtoMember(3)] public uint Scene { get; set; }

[ProtoMember(10)] public string OriginMd5 { get; set; }

[ProtoMember(11)] public string AfterCompressMd5 { get; set; }

[ProtoMember(12)] public string AfterCompressFileSize { get; set; }

[ProtoMember(13)] public string AfterCompressWeight { get; set; }

[ProtoMember(14)] public string AfterCompressHeight { get; set; }

[ProtoMember(15)] public bool IsCut { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using ProtoBuf;

#pragma warning disable CS8618
// ReSharper disable InconsistentNaming

namespace Lagrange.Core.Internal.Packets.Service.Oidb.Response;

[ProtoContract]
internal class OidbSvcTrpcTcp0xE07_0_Response
{
[ProtoMember(1)] public int RetCode { get; set; }

[ProtoMember(2)] public string ErrMsg { get; set; }

[ProtoMember(3)] public string Wording { get; set; }

[ProtoMember(10)] public OcrRspBody OcrRspBody { get; set; }
}

[ProtoContract]
internal class OcrRspBody
{
[ProtoMember(1)] public List<TextDetection> TextDetections { get; set; }

[ProtoMember(2)] public string Language { get; set; }

[ProtoMember(3)] public string RequestId { get; set; }

[ProtoMember(101)] public List<string> OcrLanguageList { get; set; }

[ProtoMember(102)] public List<string> DstTranslateLanguageList { get; set; }

[ProtoMember(103)] public List<Language> LanguageList { get; set; }

[ProtoMember(111)] public uint AfterCompressWeight { get; set; }

[ProtoMember(112)] public uint AfterCompressHeight { get; set; }
}

[ProtoContract]
internal class TextDetection
{
[ProtoMember(1)] public string DetectedText { get; set; }

[ProtoMember(2)] public uint Confidence { get; set; }

[ProtoMember(3)] public Polygon Polygon { get; set; }

[ProtoMember(4)] public string AdvancedInfo { get; set; }
}

[ProtoContract]
internal class Polygon
{
[ProtoMember(1)] public List<Coordinate> Coordinates { get; set; }
}

[ProtoContract]
internal class Coordinate
{
[ProtoMember(1)] public int X { get; set; }

[ProtoMember(2)] public int Y { get; set; }
}

[ProtoContract]
internal class Language
{
[ProtoMember(1)] public string LanguageCode { get; set; }

[ProtoMember(2)] public string LanguageDesc { get; set; }
}
57 changes: 57 additions & 0 deletions Lagrange.Core/Internal/Service/Action/ImageOcrService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
using Lagrange.Core.Common;
using Lagrange.Core.Common.Entity;
using Lagrange.Core.Internal.Event;
using Lagrange.Core.Internal.Event.Action;
using Lagrange.Core.Internal.Packets.Service.Oidb;
using Lagrange.Core.Internal.Packets.Service.Oidb.Request;
using Lagrange.Core.Internal.Packets.Service.Oidb.Response;
using Lagrange.Core.Utility.Extension;
using ProtoBuf;

namespace Lagrange.Core.Internal.Service.Action;

[EventSubscribe(typeof(ImageOcrEvent))]
[Service("OidbSvcTrpcTcp.0xe07_0")]
internal class ImageOcrService : BaseService<ImageOcrEvent>
{
protected override bool Build(ImageOcrEvent input, BotKeystore keystore, BotAppInfo appInfo,
BotDeviceInfo device, out Span<byte> output, out List<Memory<byte>>? extraPackets)
{
var packet = new OidbSvcTrpcTcpBase<OidbSvcTrpcTcp0xE07_0>(new OidbSvcTrpcTcp0xE07_0
{
Version = 1,
Client = 0,
Entrance = 1,
OcrReqBody = new OcrReqBody
{
ImageUrl = input.Url,
OriginMd5 = "",
AfterCompressMd5 = "",
AfterCompressFileSize = "",
AfterCompressWeight = "",
AfterCompressHeight = "",
IsCut = false
}
});
output = packet.Serialize();
extraPackets = null;
return true;
}

protected override bool Parse(Span<byte> input, BotKeystore keystore, BotAppInfo appInfo, BotDeviceInfo device,
out ImageOcrEvent output, out List<ProtocolEvent>? extraEvents)
{
var packet = Serializer.Deserialize<OidbSvcTrpcTcpBase<OidbSvcTrpcTcp0xE07_0_Response>>(input);
var response = new ImageOcrResult(
packet?.Body.OcrRspBody.TextDetections.Select(d => new Common.Entity.TextDetection(
d.DetectedText,
(int)d.Confidence,
d.Polygon.Coordinates.Select(c => new Common.Entity.Coordinate(c.X, c.Y)).ToList()
)).ToList() ?? new List<Common.Entity.TextDetection>(),
packet?.Body.OcrRspBody.Language ?? string.Empty
);
output = ImageOcrEvent.Result(packet?.Body.RetCode ?? -1, response);
extraEvents = null;
return true;
}
}
9 changes: 9 additions & 0 deletions Lagrange.OneBot/Core/Entity/Action/OneBotOcrImage.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Text.Json.Serialization;

namespace Lagrange.OneBot.Core.Entity.Action;

[Serializable]
public class OneBotOcrImage
{
[JsonPropertyName("image")] public string Image { get; set; } = string.Empty;
}
13 changes: 3 additions & 10 deletions Lagrange.OneBot/Core/Operation/Ability/UploadImageOperation.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
using System.Text.Json.Nodes;
using Lagrange.Core;
using Lagrange.Core.Internal.Event.Message;
using Lagrange.Core.Common.Interface.Api;
using Lagrange.Core.Message.Entity;
using Lagrange.OneBot.Core.Entity.Action;
using Lagrange.OneBot.Message.Entity;
Expand All @@ -15,15 +15,8 @@ public async Task<OneBotResult> HandleOperation(BotContext context, JsonNode? pa
if (payload?["file"]?.ToString() is { } file && CommonResolver.ResolveStream(file) is { } stream)
{
var entity = new ImageEntity(stream);
await context.ContextCollection.Highway.ManualUploadEntity(entity);
var msgInfo = entity.MsgInfo;
if (msgInfo is null) throw new Exception();

var downloadEvent = ImageDownloadEvent.Create(context.ContextCollection.Keystore.Uid ?? "", msgInfo);
var result = await context.ContextCollection.Business.SendEvent(downloadEvent);
var ret = (ImageDownloadEvent)result[0];

return new OneBotResult(ret.ImageUrl, 0, "ok");
var url = await context.UploadImage(entity);
return new OneBotResult(url, 0, "ok");
}

throw new Exception();
Expand Down
27 changes: 27 additions & 0 deletions Lagrange.OneBot/Core/Operation/Generic/OcrImageOperation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Text.Json;
using System.Text.Json.Nodes;
using Lagrange.Core;
using Lagrange.Core.Message.Entity;
using Lagrange.Core.Common.Interface.Api;
using Lagrange.OneBot.Core.Entity.Action;
using Lagrange.OneBot.Core.Operation.Converters;
using Lagrange.OneBot.Message.Entity;

namespace Lagrange.OneBot.Core.Operation.Generic;


[Operation(".ocr_image")]
[Operation("ocr_image")]
public class OcrImageOperation() : IOperation
{
public async Task<OneBotResult> HandleOperation(BotContext context, JsonNode? payload)
{
if (payload.Deserialize<OneBotOcrImage>(SerializerOptions.DefaultOptions) is { } data && CommonResolver.ResolveStream(data.Image) is { } stream)
{
var entity = new ImageEntity(stream);
var res = await context.OcrImage(entity);
return new OneBotResult(res, 0, "ok");
}
throw new Exception();
}
}

0 comments on commit 54def3f

Please sign in to comment.