feat: support OCR handwriting recognition feature (#65)
Co-authored-by: mengqian <cherish_a_meng@163.com> Co-authored-by: Tomsun28 <tomsun28@outlook.com>
This commit is contained in:
parent
b0f2e80a79
commit
6d58cea408
12 changed files with 354 additions and 1 deletions
|
|
@ -19,6 +19,8 @@ import ai.z.openapi.service.image.ImageService;
|
||||||
import ai.z.openapi.service.image.ImageServiceImpl;
|
import ai.z.openapi.service.image.ImageServiceImpl;
|
||||||
import ai.z.openapi.service.batches.BatchService;
|
import ai.z.openapi.service.batches.BatchService;
|
||||||
import ai.z.openapi.service.batches.BatchServiceImpl;
|
import ai.z.openapi.service.batches.BatchServiceImpl;
|
||||||
|
import ai.z.openapi.service.ocr.HandwritingOcrService;
|
||||||
|
import ai.z.openapi.service.ocr.HandwritingOcrServiceImpl;
|
||||||
import ai.z.openapi.service.web_search.WebSearchService;
|
import ai.z.openapi.service.web_search.WebSearchService;
|
||||||
import ai.z.openapi.service.web_search.WebSearchServiceImpl;
|
import ai.z.openapi.service.web_search.WebSearchServiceImpl;
|
||||||
import ai.z.openapi.service.web_reader.WebReaderService;
|
import ai.z.openapi.service.web_reader.WebReaderService;
|
||||||
|
|
@ -116,6 +118,9 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
|
||||||
/** FileParsing service for fileParsing operations */
|
/** FileParsing service for fileParsing operations */
|
||||||
private FileParsingService fileParsingService;
|
private FileParsingService fileParsingService;
|
||||||
|
|
||||||
|
/** HandWriting service for handwritingOcrService operations */
|
||||||
|
private HandwritingOcrService handwritingOcrService;
|
||||||
|
|
||||||
/** Moderation service for content safety detection */
|
/** Moderation service for content safety detection */
|
||||||
private ModerationService moderationService;
|
private ModerationService moderationService;
|
||||||
|
|
||||||
|
|
@ -295,6 +300,13 @@ public abstract class AbstractAiClient extends AbstractClientBaseService {
|
||||||
return fileParsingService;
|
return fileParsingService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized HandwritingOcrService handwriting() {
|
||||||
|
if (handwritingOcrService == null) {
|
||||||
|
this.handwritingOcrService = new HandwritingOcrServiceImpl(this);
|
||||||
|
}
|
||||||
|
return handwritingOcrService;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the moderation service for content safety detection. This service handles
|
* Returns the moderation service for content safety detection. This service handles
|
||||||
* content moderation for text, image, video, and audio inputs.
|
* content moderation for text, image, video, and audio inputs.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ai.z.openapi.api.ocr;
|
||||||
|
|
||||||
|
import ai.z.openapi.service.ocr.HandwritingOcrResult;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import retrofit2.Call;
|
||||||
|
import retrofit2.http.Body;
|
||||||
|
import retrofit2.http.POST;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OCR Handwriting Recognition API Provides functionality to upload an image for
|
||||||
|
* handwriting recognition, and retrieve the parsed result.
|
||||||
|
*/
|
||||||
|
public interface HandwritingOcrApi {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes handwriting recognition synchronously.
|
||||||
|
* @param multipartBody The multipart request body containing the image file and
|
||||||
|
* metadata.
|
||||||
|
* @return The recognition result as a HandwritingOcrResp object.
|
||||||
|
*/
|
||||||
|
@POST("files/ocr")
|
||||||
|
Call<HandwritingOcrResult> recognize(@Body MultipartBody multipartBody);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import ai.z.openapi.core.model.ClientResponse;
|
||||||
|
import ai.z.openapi.service.model.ChatError;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class HandwritingOcrResponse implements ClientResponse<HandwritingOcrResult> {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response status code.
|
||||||
|
*/
|
||||||
|
private int code;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response message.
|
||||||
|
*/
|
||||||
|
private String msg;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates whether the request was successful.
|
||||||
|
*/
|
||||||
|
private boolean success;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The HandwritingOcr result data.
|
||||||
|
*/
|
||||||
|
private HandwritingOcrResult data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error information if the request failed.
|
||||||
|
*/
|
||||||
|
private ChatError error;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class HandwritingOcrResult {
|
||||||
|
|
||||||
|
private String task_id; // Task ID or result ID
|
||||||
|
|
||||||
|
private String message; // Response message
|
||||||
|
|
||||||
|
private String status; // OCR task status (e.g., "succeeded")
|
||||||
|
|
||||||
|
private int words_result_num; // Number of recognition results
|
||||||
|
|
||||||
|
private List<WordsResult> words_result; // List of recognition results
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
public interface HandwritingOcrService {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes a synchronous handwriting recognition operation.
|
||||||
|
* @param request The OCR upload request (contains file path, tool type, language
|
||||||
|
* type)
|
||||||
|
* @return HandwritingOcrResp containing the recognition result
|
||||||
|
*/
|
||||||
|
HandwritingOcrResponse recognize(HandwritingOcrUploadReq request);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,102 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import ai.z.openapi.AbstractAiClient;
|
||||||
|
import ai.z.openapi.api.ocr.HandwritingOcrApi;
|
||||||
|
import ai.z.openapi.utils.RequestSupplier;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import io.reactivex.rxjava3.core.Single;
|
||||||
|
import okhttp3.MediaType;
|
||||||
|
import okhttp3.MultipartBody;
|
||||||
|
import okhttp3.RequestBody;
|
||||||
|
import retrofit2.Response;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* OCR handwriting recognition service implementation
|
||||||
|
*/
|
||||||
|
public class HandwritingOcrServiceImpl implements HandwritingOcrService {
|
||||||
|
|
||||||
|
private final AbstractAiClient zAiClient;
|
||||||
|
|
||||||
|
private final HandwritingOcrApi handwritingOcrApi;
|
||||||
|
|
||||||
|
public HandwritingOcrServiceImpl(AbstractAiClient zAiClient) {
|
||||||
|
this.zAiClient = zAiClient;
|
||||||
|
this.handwritingOcrApi = zAiClient.retrofit().create(HandwritingOcrApi.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public HandwritingOcrResponse recognize(HandwritingOcrUploadReq request) {
|
||||||
|
if (request == null) {
|
||||||
|
throw new IllegalArgumentException("request cannot be null");
|
||||||
|
}
|
||||||
|
if (request.getFilePath() == null) {
|
||||||
|
throw new IllegalArgumentException("filePath cannot be null");
|
||||||
|
}
|
||||||
|
if (request.getToolType() == null) {
|
||||||
|
throw new IllegalArgumentException("toolType cannot be null");
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestSupplier<HandwritingOcrUploadReq, HandwritingOcrResult> supplier = params -> {
|
||||||
|
try {
|
||||||
|
File file = new File(params.getFilePath());
|
||||||
|
if (!file.exists()) {
|
||||||
|
throw new RuntimeException("file not found at " + params.getFilePath());
|
||||||
|
}
|
||||||
|
String toolType = params.getToolType();
|
||||||
|
String languageType = params.getLanguageType();
|
||||||
|
Boolean probability = params.getProbability();
|
||||||
|
|
||||||
|
// Build multipart/form-data
|
||||||
|
MultipartBody.Part filePart = MultipartBody.Part.createFormData("file", file.getName(),
|
||||||
|
RequestBody.create(MediaType.parse("application/octet-stream"), file));
|
||||||
|
MultipartBody.Builder formBodyBuilder = new MultipartBody.Builder().setType(MultipartBody.FORM);
|
||||||
|
formBodyBuilder.addPart(filePart);
|
||||||
|
formBodyBuilder.addFormDataPart("tool_type", toolType);
|
||||||
|
if (languageType != null) {
|
||||||
|
formBodyBuilder.addFormDataPart("language_type", languageType);
|
||||||
|
}
|
||||||
|
if (probability != null) {
|
||||||
|
formBodyBuilder.addFormDataPart("probability", String.valueOf(probability));
|
||||||
|
}
|
||||||
|
|
||||||
|
MultipartBody multipartBody = formBodyBuilder.build();
|
||||||
|
|
||||||
|
// Send POST request
|
||||||
|
retrofit2.Call<HandwritingOcrResult> call = handwritingOcrApi.recognize(multipartBody);
|
||||||
|
Response<HandwritingOcrResult> response = call.execute();
|
||||||
|
if (!response.isSuccessful() || response.body() == null) {
|
||||||
|
String errorJson = "";
|
||||||
|
if (response.errorBody() != null) {
|
||||||
|
errorJson = response.errorBody().string();
|
||||||
|
}
|
||||||
|
String msg = response.message();
|
||||||
|
String msgFromBody = "";
|
||||||
|
if (errorJson != null && errorJson.trim().startsWith("{")) {
|
||||||
|
try {
|
||||||
|
ObjectMapper mapper = new ObjectMapper();
|
||||||
|
HandwritingOcrResult errorResult = mapper.readValue(errorJson, HandwritingOcrResult.class);
|
||||||
|
msgFromBody = errorResult.getMessage();
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
msgFromBody = errorJson;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new IOException("Failed to recognize, code: " + response.code() + ", msg: " + msg
|
||||||
|
+ (msgFromBody.isEmpty() ? "" : (", detail: " + msgFromBody)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Single.just(response.body());
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.zAiClient.executeRequest(request, supplier, HandwritingOcrResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import ai.z.openapi.core.model.ClientRequest;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handwriting OCR upload request object
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
public class HandwritingOcrUploadReq implements ClientRequest<HandwritingOcrUploadReq> {
|
||||||
|
|
||||||
|
private String filePath; // Path to the image file
|
||||||
|
|
||||||
|
private String toolType; // Tool type, must be "hand_write"
|
||||||
|
|
||||||
|
private String languageType; // Language type (optional)
|
||||||
|
|
||||||
|
private Boolean probability; // Confidence score for each line of text recognition
|
||||||
|
|
||||||
|
}
|
||||||
20
core/src/main/java/ai/z/openapi/service/ocr/Location.java
Normal file
20
core/src/main/java/ai/z/openapi/service/ocr/Location.java
Normal file
|
|
@ -0,0 +1,20 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Location {
|
||||||
|
|
||||||
|
private int left;
|
||||||
|
|
||||||
|
private int top;
|
||||||
|
|
||||||
|
private int width;
|
||||||
|
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
}
|
||||||
18
core/src/main/java/ai/z/openapi/service/ocr/Probability.java
Normal file
18
core/src/main/java/ai/z/openapi/service/ocr/Probability.java
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class Probability {
|
||||||
|
|
||||||
|
private Double average; // Average confidence of the line
|
||||||
|
|
||||||
|
private Double variance; // Confidence variance of the line
|
||||||
|
|
||||||
|
private Double min; // Minimum confidence of the line
|
||||||
|
|
||||||
|
}
|
||||||
18
core/src/main/java/ai/z/openapi/service/ocr/WordsResult.java
Normal file
18
core/src/main/java/ai/z/openapi/service/ocr/WordsResult.java
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
package ai.z.openapi.service.ocr;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
@NoArgsConstructor
|
||||||
|
public class WordsResult {
|
||||||
|
|
||||||
|
private Location location; // Location information for detected text
|
||||||
|
|
||||||
|
private String words; // Recognized text
|
||||||
|
|
||||||
|
private Probability probability; // Confidence score for each line of text recognition
|
||||||
|
|
||||||
|
}
|
||||||
2
pom.xml
2
pom.xml
|
|
@ -45,7 +45,7 @@
|
||||||
</scm>
|
</scm>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<revision>0.1.0</revision>
|
<revision>0.1.1</revision>
|
||||||
<java.version>8</java.version>
|
<java.version>8</java.version>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,67 @@
|
||||||
|
package ai.z.openapi.samples;
|
||||||
|
|
||||||
|
import ai.z.openapi.ZaiClient;
|
||||||
|
import ai.z.openapi.service.ocr.HandwritingOcrResponse;
|
||||||
|
import ai.z.openapi.service.ocr.HandwritingOcrResult;
|
||||||
|
import ai.z.openapi.service.ocr.HandwritingOcrUploadReq;
|
||||||
|
import ai.z.openapi.service.ocr.WordsResult;
|
||||||
|
|
||||||
|
public class HandwritingOcrExample {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
// It is recommended to set the API Key via environment variable
|
||||||
|
// export ZAI_API_KEY=your.api_key
|
||||||
|
// ZaiClient client = ZaiClient.builder().build();
|
||||||
|
|
||||||
|
// You can also set the API Key directly in the code for testing
|
||||||
|
ZaiClient client = ZaiClient.builder()
|
||||||
|
.apiKey("your-real-api-key")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
try {
|
||||||
|
System.out.println("=== Handwriting OCR Example ===");
|
||||||
|
|
||||||
|
String filePath = ""; // Change to your own image path
|
||||||
|
HandwritingOcrResponse response = syncHandwritingOcrExample(client, filePath, "hand_write", "CHN_ENG", true);
|
||||||
|
if (response != null && response.getData() != null) {
|
||||||
|
System.out.println(response.getData());
|
||||||
|
} else {
|
||||||
|
System.out.println("Recognition failed.");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("Exception occurred: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example: Upload an image and perform handwriting OCR recognition
|
||||||
|
* @param client ZaiClient instance
|
||||||
|
* @param filePath Path of the image file
|
||||||
|
* @param toolType Type of recognition tool
|
||||||
|
* @param languageType Language type (optional)
|
||||||
|
* @return OCR response object
|
||||||
|
*/
|
||||||
|
private static HandwritingOcrResponse syncHandwritingOcrExample(ZaiClient client, String filePath, String toolType,
|
||||||
|
String languageType, Boolean probability) {
|
||||||
|
if (filePath == null || filePath.trim().isEmpty()) {
|
||||||
|
System.err.println("Invalid file path.");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
HandwritingOcrUploadReq uploadReq = new HandwritingOcrUploadReq();
|
||||||
|
uploadReq.setFilePath(filePath);
|
||||||
|
uploadReq.setToolType(toolType); // Must be "hand_write"
|
||||||
|
uploadReq.setLanguageType(languageType); // Can be "CHN_ENG", "ENG", etc.
|
||||||
|
uploadReq.setProbability(probability);
|
||||||
|
System.out.println(uploadReq.toString());
|
||||||
|
System.out.println("Uploading the image and performing handwriting recognition...calling API");
|
||||||
|
return client.handwriting().recognize(uploadReq);
|
||||||
|
}
|
||||||
|
catch (Exception e) {
|
||||||
|
System.err.println("Handwriting recognition task error: " + e.getMessage());
|
||||||
|
}
|
||||||
|
// Return null indicates failure
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue