|  | @@ -0,0 +1,183 @@
 | 
	
		
			
				|  |  | +package org.springblade.archive.utils;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import org.apache.pdfbox.pdmodel.PDDocument;
 | 
	
		
			
				|  |  | +import org.apache.pdfbox.rendering.PDFRenderer;
 | 
	
		
			
				|  |  | +import org.bytedeco.javacpp.BytePointer;
 | 
	
		
			
				|  |  | +import org.bytedeco.javacv.Frame;
 | 
	
		
			
				|  |  | +import org.bytedeco.javacv.Java2DFrameConverter;
 | 
	
		
			
				|  |  | +import org.bytedeco.javacv.OpenCVFrameConverter;
 | 
	
		
			
				|  |  | +import org.bytedeco.opencv.opencv_core.*;
 | 
	
		
			
				|  |  | +import org.bytedeco.opencv.opencv_imgproc.*;
 | 
	
		
			
				|  |  | +import static org.bytedeco.opencv.global.opencv_core.*;
 | 
	
		
			
				|  |  | +import org.bytedeco.opencv.global.opencv_imgcodecs;
 | 
	
		
			
				|  |  | +import org.opencv.core.CvType;
 | 
	
		
			
				|  |  | +import org.springblade.common.utils.CommonUtil;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import java.awt.image.BufferedImage;
 | 
	
		
			
				|  |  | +import java.io.ByteArrayOutputStream;
 | 
	
		
			
				|  |  | +import java.io.File;
 | 
	
		
			
				|  |  | +import java.io.IOException;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +import static org.bytedeco.opencv.global.opencv_imgcodecs.*;
 | 
	
		
			
				|  |  | +import static org.bytedeco.opencv.global.opencv_imgproc.*;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +/**
 | 
	
		
			
				|  |  | + * 简单检测图片质量
 | 
	
		
			
				|  |  | + */
 | 
	
		
			
				|  |  | +public class ImageQualityDetectorUtils {
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +//    public static void main(String[] args) throws IOException {
 | 
	
		
			
				|  |  | +//        try (PDDocument document = PDDocument.load(new File("C:\\Users\\泓创02\\Downloads\\73ef17c2f24ea5b83747a1ac5c2c3f0f.pdf"));
 | 
	
		
			
				|  |  | +//             Java2DFrameConverter converter1 = new Java2DFrameConverter();
 | 
	
		
			
				|  |  | +//             OpenCVFrameConverter.ToMat converter2 = new OpenCVFrameConverter.ToMat();) {
 | 
	
		
			
				|  |  | +//            if (document.isEncrypted()) {
 | 
	
		
			
				|  |  | +//                throw new RuntimeException("PDF文件已加密,请先解密");
 | 
	
		
			
				|  |  | +//            }
 | 
	
		
			
				|  |  | +//            // 创建PDF渲染器
 | 
	
		
			
				|  |  | +//            PDFRenderer pdfRenderer = new PDFRenderer(document);
 | 
	
		
			
				|  |  | +//            // 获取总页数
 | 
	
		
			
				|  |  | +//            int pages = document.getNumberOfPages();
 | 
	
		
			
				|  |  | +//            for (int i = 0; i < pages; i++) {
 | 
	
		
			
				|  |  | +//                Frame frame = converter1.convert(pdfRenderer.renderImageWithDPI(i, 300));
 | 
	
		
			
				|  |  | +//                Mat image = converter2.convert(frame);
 | 
	
		
			
				|  |  | +//                if (image.empty()) {
 | 
	
		
			
				|  |  | +//                    System.out.println("无法加载图像");
 | 
	
		
			
				|  |  | +//                    return;
 | 
	
		
			
				|  |  | +//                }
 | 
	
		
			
				|  |  | +//                // 检测各种质量问题
 | 
	
		
			
				|  |  | +//                boolean hasStains = detectLargeStains(image);
 | 
	
		
			
				|  |  | +//                boolean isObstructed = detectTextObstruction(image);
 | 
	
		
			
				|  |  | +//                boolean hasShadows = detectShadows(image);
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +//                System.out.println("检测结果:");
 | 
	
		
			
				|  |  | +//                System.out.println("大面积污渍: " + (hasStains ? "是" : "否"));
 | 
	
		
			
				|  |  | +//                System.out.println("文字遮挡: " + (isObstructed ? "是" : "否"));
 | 
	
		
			
				|  |  | +//                System.out.println("黑影/阴影: " + (hasShadows ? "是" : "否"));
 | 
	
		
			
				|  |  | +//
 | 
	
		
			
				|  |  | +//                if (hasStains || isObstructed || hasShadows) {
 | 
	
		
			
				|  |  | +//                    System.out.println("图像质量不佳,建议重新扫描");
 | 
	
		
			
				|  |  | +//                } else {
 | 
	
		
			
				|  |  | +//                    System.out.println("图像质量良好");
 | 
	
		
			
				|  |  | +//                }
 | 
	
		
			
				|  |  | +//            }
 | 
	
		
			
				|  |  | +//        }
 | 
	
		
			
				|  |  | +//    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 检测图像中是否存在大面积污渍
 | 
	
		
			
				|  |  | +     * @param image 输入图像
 | 
	
		
			
				|  |  | +     * @return 污渍检测结果
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public static boolean detectLargeStains(Mat image) {
 | 
	
		
			
				|  |  | +        // 转换为灰度图
 | 
	
		
			
				|  |  | +        Mat gray = new Mat();
 | 
	
		
			
				|  |  | +        cvtColor(image, gray, COLOR_BGR2GRAY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 应用阈值处理,突出污渍区域
 | 
	
		
			
				|  |  | +        Mat thresh = new Mat();
 | 
	
		
			
				|  |  | +        threshold(gray, thresh, 0, 255, THRESH_BINARY_INV + THRESH_OTSU);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 形态学操作去除噪声
 | 
	
		
			
				|  |  | +        Mat kernel = getStructuringElement(MORPH_ELLIPSE, new Size(5, 5));
 | 
	
		
			
				|  |  | +        morphologyEx(thresh, thresh, MORPH_OPEN, kernel);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 查找轮廓
 | 
	
		
			
				|  |  | +        MatVector contours = new MatVector();
 | 
	
		
			
				|  |  | +        findContours(thresh, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 检查是否有大面积的污渍
 | 
	
		
			
				|  |  | +        double imageArea = image.rows() * image.cols();
 | 
	
		
			
				|  |  | +        for (int i = 0; i < contours.size(); i++) {
 | 
	
		
			
				|  |  | +            double area = contourArea(contours.get(i));
 | 
	
		
			
				|  |  | +            // 如果污渍面积超过图像面积的5%,则认为存在大面积污渍
 | 
	
		
			
				|  |  | +            if (area > imageArea * 0.05) {
 | 
	
		
			
				|  |  | +                return true;
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 检测图像中文字是否被遮挡
 | 
	
		
			
				|  |  | +     * @param image 输入图像
 | 
	
		
			
				|  |  | +     * @return 遮挡检测结果
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public static boolean detectTextObstruction(Mat image) {
 | 
	
		
			
				|  |  | +        // 转换为灰度图
 | 
	
		
			
				|  |  | +        Mat gray = new Mat();
 | 
	
		
			
				|  |  | +        cvtColor(image, gray, COLOR_BGR2GRAY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 边缘检测
 | 
	
		
			
				|  |  | +        Mat edges = new Mat();
 | 
	
		
			
				|  |  | +        Canny(gray, edges, 50, 150);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 形态学操作连接边缘
 | 
	
		
			
				|  |  | +        Mat kernel = getStructuringElement(MORPH_RECT, new Size(3, 3));
 | 
	
		
			
				|  |  | +        morphologyEx(edges, edges, MORPH_CLOSE, kernel);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 查找轮廓
 | 
	
		
			
				|  |  | +        MatVector contours = new MatVector();
 | 
	
		
			
				|  |  | +        findContours(edges, contours, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 分析轮廓特征判断是否有遮挡
 | 
	
		
			
				|  |  | +        for (int i = 0; i < contours.size(); i++) {
 | 
	
		
			
				|  |  | +            Mat contour = contours.get(i);
 | 
	
		
			
				|  |  | +            double area = contourArea(contour);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 计算轮廓的边界矩形
 | 
	
		
			
				|  |  | +            Rect boundingRect = boundingRect(contour);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +            // 如果区域较大但边缘不规则,可能是遮挡
 | 
	
		
			
				|  |  | +            if (area > 1000) {
 | 
	
		
			
				|  |  | +                double aspectRatio = (double) boundingRect.width() / boundingRect.height();
 | 
	
		
			
				|  |  | +                if (aspectRatio > 5 || aspectRatio < 0.2) {
 | 
	
		
			
				|  |  | +                    return true;
 | 
	
		
			
				|  |  | +                }
 | 
	
		
			
				|  |  | +            }
 | 
	
		
			
				|  |  | +        }
 | 
	
		
			
				|  |  | +        return false;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    /**
 | 
	
		
			
				|  |  | +     * 检测图像中是否存在黑影或阴影
 | 
	
		
			
				|  |  | +     * @param image 输入图像
 | 
	
		
			
				|  |  | +     * @return 阴影检测结果
 | 
	
		
			
				|  |  | +     */
 | 
	
		
			
				|  |  | +    public static boolean detectShadows(Mat image) {
 | 
	
		
			
				|  |  | +        // 转换为灰度图
 | 
	
		
			
				|  |  | +        Mat gray = new Mat();
 | 
	
		
			
				|  |  | +        cvtColor(image, gray, COLOR_BGR2GRAY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 应用高斯模糊减少噪声
 | 
	
		
			
				|  |  | +        Mat blurred = new Mat();
 | 
	
		
			
				|  |  | +        GaussianBlur(gray, blurred, new Size(5, 5), 0);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 计算图像的梯度
 | 
	
		
			
				|  |  | +        Mat gradX = new Mat();
 | 
	
		
			
				|  |  | +        Mat gradY = new Mat();
 | 
	
		
			
				|  |  | +        Sobel(blurred, gradX, CV_32F, 1, 0);
 | 
	
		
			
				|  |  | +        Sobel(blurred, gradY, CV_32F, 0, 1);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 计算梯度幅值
 | 
	
		
			
				|  |  | +        Mat gradient = new Mat();
 | 
	
		
			
				|  |  | +        magnitude(gradX, gradY, gradient);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 转换为8位图像
 | 
	
		
			
				|  |  | +        Mat gradient8u = new Mat();
 | 
	
		
			
				|  |  | +        convertScaleAbs(gradient, gradient8u);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 应用阈值分割
 | 
	
		
			
				|  |  | +        Mat binary = new Mat();
 | 
	
		
			
				|  |  | +        threshold(gradient8u, binary, 30, 255, THRESH_BINARY);
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 计算阴影区域占比
 | 
	
		
			
				|  |  | +        double totalPixels = binary.rows() * binary.cols();
 | 
	
		
			
				|  |  | +        double shadowPixels = totalPixels - countNonZero(binary);
 | 
	
		
			
				|  |  | +        double shadowRatio = shadowPixels / totalPixels;
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +        // 如果阴影区域超过10%,则认为存在明显阴影
 | 
	
		
			
				|  |  | +        return shadowRatio > 0.1;
 | 
	
		
			
				|  |  | +    }
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +}
 |