You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何提升OpenCV提取的扫描纸张签名质量?含透明背景需求

签名提取与透明背景PNG转换优化需求

我使用OpenCV Java库从扫描的白底签名纸中提取并裁剪签名,目前发现灰度版本(grayROI)的效果最优。希望优化签名外观(如提亮背景),核心需求是将签名转换为带透明背景的PNG格式。现有实现代码如下:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.apache.commons.io.FilenameUtils;
import org.opencv.core.Core;
import org.opencv.core.Mat;
import org.opencv.core.MatOfPoint;
import org.opencv.core.Point;
import org.opencv.core.Rect;
import org.opencv.core.Scalar;
import org.opencv.core.Size;
import org.opencv.imgcodecs.Imgcodecs;
import org.opencv.imgproc.Imgproc;
import org.opencv.utils.Converters;

import nu.pattern.OpenCV;


    public static synchronized void extractSignature(File signature) {

        if (signature == null)
            return;

        String fileExtension = FilenameUtils.getExtension(signature.getName());

        // check if the file extension is correct!
        if (!fileExtension.toLowerCase().equals("jpg") && !fileExtension.toLowerCase().equals("jpeg") && !fileExtension.toLowerCase().equals("png")) {
            return;
        }
        
        try {
            
            if (!isLoaded) {
                OpenCV.loadLocally();
                isLoaded = true;
            }
            
        } catch (Exception e) {
            System.out.println(e.toString());
        }

        // Load image
        Mat image = Imgcodecs.imread(signature.getPath());
        Mat imageOriginal = image.clone();

        // Convert image to HSV color space
        Mat hsv = new Mat();
        Imgproc.cvtColor(image, hsv, Imgproc.COLOR_BGR2HSV);

        // Define lower and upper bounds for color threshold
        Scalar lower = new Scalar(90, 38, 0);
        Scalar upper = new Scalar(145, 255, 255);

        // Threshold the HSV image to get only desired colors
        Mat mask = new Mat();
        Core.inRange(hsv, lower, upper, mask);

        // Find contours
        List<MatOfPoint> contours = new ArrayList<>();
        Mat hierarchy = new Mat();
        Imgproc.findContours(mask, contours, hierarchy, Imgproc.RETR_EXTERNAL, Imgproc.CHAIN_APPROX_SIMPLE);

        // Combine all contours into one and get bounding box
        MatOfPoint allContours = new MatOfPoint();
        for (MatOfPoint contour : contours) {
            List<Point> pts = contour.toList();
            allContours.push_back(new MatOfPoint(Converters.vector_Point_to_Mat(pts)));
        }

        Rect boundingBox = Imgproc.boundingRect(allContours);

        
        // Add 5 pixels to each dimension of the bounding box
        int padding = 10;
        int x = Math.max(boundingBox.x - padding, 0);
        int y = Math.max(boundingBox.y - padding, 0);
        int width = Math.min(boundingBox.width + 2 * padding, image.cols() - x);
        int height = Math.min(boundingBox.height + 2 * padding, image.rows() - y);
        Rect paddedBoundingBox = new Rect(x, y, width, height);

        
        // Extract ROI
        //Mat ROI = new Mat(imageOriginal, boundingBox);
        Mat ROI = new Mat(imageOriginal, paddedBoundingBox);
        
        
        // Convert ROI to grayscale
        Mat grayROI = new Mat();
        Imgproc.cvtColor(ROI, grayROI, Imgproc.COLOR_BGR2GRAY);
        
        // Apply histogram equalization to improve contrast
        Mat equalizedROI = new Mat();
        Imgproc.equalizeHist(grayROI, equalizedROI);
        
        // Apply adaptive thresholding to binarize the image
        Mat binaryROI = new Mat();
        Imgproc.adaptiveThreshold(grayROI, binaryROI, 255, Imgproc.ADAPTIVE_THRESH_GAUSSIAN_C, Imgproc.THRESH_BINARY, 11, 2);

        // Apply morphological transformations to improve the signature appearance
        Mat morphROI = new Mat();
        Mat kernel = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(2, 2));
        Imgproc.morphologyEx(binaryROI, morphROI, Imgproc.MORPH_CLOSE, kernel);
        Imgproc.morphologyEx(morphROI, morphROI, Imgproc.MORPH_OPEN, kernel);


        // Save and display images
        Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + signature.getName(), ROI);
        Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + "morphROI.jpg", morphROI);
        Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + "grayROI.jpg", grayROI);
        Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + "binaryROI.jpg", binaryROI);
        Imgcodecs.imwrite(FileUtils.getSignaturesFolder().getAbsolutePath() + File.separator + "equalizedROI.jpg", equalizedROI);
        
        return;
    }

参考图片

  • 原扫描图:原扫描图
  • 提取结果图:提取后的签名图

内容的提问来源于stack exchange,提问作者Paolo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 18:54:53