优化
This commit is contained in:
@@ -1,16 +1,22 @@
|
||||
package com.baiyue.integration.dto.ocr;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 百度 OCR 返回的文字候选及其识别框。
|
||||
*/
|
||||
public record OcrCandidate(
|
||||
String text,
|
||||
Double confidence,
|
||||
BigDecimal confidence,
|
||||
Integer left,
|
||||
Integer top,
|
||||
Integer width,
|
||||
Integer height
|
||||
) {
|
||||
public OcrCandidate {
|
||||
confidence = OcrConfidence.normalize(confidence);
|
||||
}
|
||||
|
||||
public long area() {
|
||||
if (width == null || height == null || width <= 0 || height <= 0) {
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.baiyue.integration.dto.ocr;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/** Canonical confidence representation shared by OCR response models. */
|
||||
public final class OcrConfidence {
|
||||
private static final int SCALE = 5;
|
||||
private static final BigDecimal ZERO = BigDecimal.ZERO.setScale(SCALE);
|
||||
private static final BigDecimal ONE = BigDecimal.ONE.setScale(SCALE);
|
||||
|
||||
private OcrConfidence() {
|
||||
}
|
||||
|
||||
public static BigDecimal normalize(BigDecimal value) {
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
if (value.compareTo(BigDecimal.ZERO) < 0) {
|
||||
return ZERO;
|
||||
}
|
||||
if (value.compareTo(BigDecimal.ONE) > 0) {
|
||||
return ONE;
|
||||
}
|
||||
return value.setScale(SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
+6
-1
@@ -12,10 +12,15 @@ public record WeightOcrImageResult(
|
||||
String rawResponseFile,
|
||||
Status status,
|
||||
BigDecimal weight,
|
||||
Double confidence,
|
||||
BigDecimal confidence,
|
||||
List<OcrCandidate> candidates,
|
||||
String ocrRawText,
|
||||
String error
|
||||
) {
|
||||
public WeightOcrImageResult {
|
||||
confidence = OcrConfidence.normalize(confidence);
|
||||
}
|
||||
|
||||
public enum Status {
|
||||
RECOGNIZED,
|
||||
NO_WEIGHT_FOUND,
|
||||
|
||||
+4
-3
@@ -12,6 +12,7 @@ import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.net.http.HttpClient;
|
||||
@@ -163,7 +164,7 @@ public class BaiduMeterClient {
|
||||
JsonNode location = item.path("location");
|
||||
candidates.add(new OcrCandidate(
|
||||
item.path("words").asText(),
|
||||
nullableDouble(probability.get("average")),
|
||||
nullableDecimal(probability.get("average")),
|
||||
nullableInt(location.get("left")),
|
||||
nullableInt(location.get("top")),
|
||||
nullableInt(location.get("width")),
|
||||
@@ -195,8 +196,8 @@ public class BaiduMeterClient {
|
||||
return URLEncoder.encode(value, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private static Double nullableDouble(JsonNode node) {
|
||||
return node == null || !node.isNumber() ? null : node.doubleValue();
|
||||
private static BigDecimal nullableDecimal(JsonNode node) {
|
||||
return node == null || !node.isNumber() ? null : node.decimalValue();
|
||||
}
|
||||
|
||||
private static Integer nullableInt(JsonNode node) {
|
||||
|
||||
+3
-2
@@ -25,7 +25,8 @@ public class WeightCandidateSelector {
|
||||
.flatMap(Optional::stream)
|
||||
.max(Comparator
|
||||
.comparingLong((SelectedWeight selected) -> selected.candidate().area())
|
||||
.thenComparing(selected -> selected.confidence() == null ? -1 : selected.confidence()));
|
||||
.thenComparing(selected -> selected.confidence() == null
|
||||
? BigDecimal.valueOf(-1) : selected.confidence()));
|
||||
}
|
||||
|
||||
private Optional<SelectedWeight> toSelectedWeight(OcrCandidate candidate) {
|
||||
@@ -55,6 +56,6 @@ public class WeightCandidateSelector {
|
||||
.replace('-', '-');
|
||||
}
|
||||
|
||||
public record SelectedWeight(BigDecimal weight, Double confidence, OcrCandidate candidate) {
|
||||
public record SelectedWeight(BigDecimal weight, BigDecimal confidence, OcrCandidate candidate) {
|
||||
}
|
||||
}
|
||||
|
||||
+30
-2
@@ -34,6 +34,7 @@ import java.util.stream.Stream;
|
||||
@Service
|
||||
public class WeightOcrBatchService {
|
||||
private static final Set<String> SUPPORTED_EXTENSIONS = Set.of("jpg", "jpeg", "png", "bmp");
|
||||
private static final int RAW_TEXT_MAX_LENGTH = 1000;
|
||||
|
||||
@Resource private IntegrationProperties properties;
|
||||
@Resource private WeightOcrStoragePolicy storagePolicy;
|
||||
@@ -113,7 +114,7 @@ public class WeightOcrBatchService {
|
||||
return new WeightOcrImageResult(
|
||||
source.toString(), outputRelative(paths, cropped), outputRelative(paths, rawResponse),
|
||||
Status.RECOGNIZED, selected.weight(), selected.confidence(),
|
||||
response.candidates(), null);
|
||||
response.candidates(), summarizeCandidates(response.candidates()), null);
|
||||
}
|
||||
|
||||
private WeightOcrImageResult noWeight(Path source, BatchPaths paths, Path cropped,
|
||||
@@ -121,6 +122,7 @@ public class WeightOcrBatchService {
|
||||
return new WeightOcrImageResult(
|
||||
source.toString(), outputRelative(paths, cropped), outputRelative(paths, rawResponse),
|
||||
Status.NO_WEIGHT_FOUND, null, null, response.candidates(),
|
||||
summarizeCandidates(response.candidates()),
|
||||
"百度响应中没有符合重量格式的候选值");
|
||||
}
|
||||
|
||||
@@ -129,7 +131,33 @@ public class WeightOcrBatchService {
|
||||
return new WeightOcrImageResult(
|
||||
source.toString(), Files.exists(cropped) ? outputRelative(paths, cropped) : null,
|
||||
Files.exists(rawResponse) ? outputRelative(paths, rawResponse) : null,
|
||||
Status.FAILED, null, null, List.of(), error);
|
||||
Status.FAILED, null, null, List.of(), null, error);
|
||||
}
|
||||
|
||||
private String summarizeCandidates(List<com.baiyue.integration.dto.ocr.OcrCandidate> candidates) {
|
||||
if (candidates == null || candidates.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
StringBuilder summary = new StringBuilder();
|
||||
for (var candidate : candidates) {
|
||||
if (candidate == null || candidate.text() == null) {
|
||||
continue;
|
||||
}
|
||||
String text = candidate.text().strip().replaceAll("[\\p{Cc}\\p{Cf}]+", " ");
|
||||
if (text.isBlank()) {
|
||||
continue;
|
||||
}
|
||||
int separatorLength = summary.isEmpty() ? 0 : 1;
|
||||
int remaining = RAW_TEXT_MAX_LENGTH - summary.length() - separatorLength;
|
||||
if (remaining <= 0) {
|
||||
break;
|
||||
}
|
||||
if (separatorLength > 0) {
|
||||
summary.append('\n');
|
||||
}
|
||||
summary.append(text, 0, Math.min(text.length(), remaining));
|
||||
}
|
||||
return summary.isEmpty() ? null : summary.toString();
|
||||
}
|
||||
|
||||
private WeightOcrBatchResult buildResult(String batchId, Instant startedAt, BatchPaths paths,
|
||||
|
||||
+3
-1
@@ -56,7 +56,9 @@ public class WeightOcrSingleImageService {
|
||||
if (batch.results() == null || batch.results().size() != 1) {
|
||||
throw new BizException(ResultCode.INTERNAL_ERROR, "OCR单图识别结果数量异常");
|
||||
}
|
||||
return batch.results().get(0);
|
||||
WeightOcrImageResult result = batch.results().get(0);
|
||||
return new WeightOcrImageResult(null, null, null, result.status(), result.weight(),
|
||||
result.confidence(), java.util.List.of(), result.ocrRawText(), result.error());
|
||||
} finally {
|
||||
deleteRequestDirectory(requestDirectory);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user