Java OpenCV删除扫描瑕疵并旋转内容

Java OpenCV删除扫描瑕疵并旋转内容,java,opencv,Java,Opencv,我能够本地化以下图像的内容: 这是当前的Java代码: Mat image = Imgcodecs.imread("test.png"); Mat gray = new Mat(); Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY); Core.absdiff(gray, new Scalar(255), gray); Imgproc.threshold(gray, gray, 5, 255,

我能够本地化以下图像的内容:

这是当前的Java代码:

    Mat image = Imgcodecs.imread("test.png");

    Mat gray = new Mat();
    Imgproc.cvtColor(image, gray, Imgproc.COLOR_BGR2GRAY);
    Core.absdiff(gray, new Scalar(255), gray);

    Imgproc.threshold(gray, gray, 5, 255, Imgproc.THRESH_TOZERO);

    Mat kernel1 = Imgproc.getStructuringElement(Imgproc.MORPH_ELLIPSE, new Size(11, 11));
    Mat kernel2 = Mat.ones(3, 3, CvType.CV_8U);

    Mat erosion = new Mat();
    Imgproc.erode(gray, erosion, kernel2, new Point(-1, -1), 1);
    Mat dilation = new Mat();
    Imgproc.dilate(erosion, dilation, kernel1, new Point(-1, -1), 7);

    final List<MatOfPoint> contours = new ArrayList<>();
    final Mat hierarchy = new Mat();
    Imgproc.findContours(dilation, contours, hierarchy,
                         Imgproc.RETR_TREE, Imgproc.CHAIN_APPROX_SIMPLE);

    for (MatOfPoint contour : contours) {
        RotatedRect rect = Imgproc.minAreaRect(new MatOfPoint2f(contour.toArray()));
        Mat box = new Mat();
        Imgproc.boxPoints(rect, box);

        Imgproc.drawContours(image, contours, -1, new Scalar(0,0,255));
    }
Mat image=Imgcodecs.imread(“test.png”);
Mat灰色=新Mat();
Imgproc.cvt颜色(图像,灰色,Imgproc.COLOR\u bgr2灰色);
Core.absdiff(灰色,新标量(255),灰色);
Imgproc.阈值(灰色,灰色,5255,Imgproc.阈值为零);
Mat kernel1=Imgproc.getStructuringElement(Imgproc.morp_椭圆,新大小(11,11));
Mat kernel2=Mat.one(3,3,CvType.CV_8U);
垫侵蚀=新垫();
侵蚀(灰色,侵蚀,核2,新点(-1,-1),1);
垫扩张=新垫();
扩张(侵蚀,扩张,核1,新点(-1,-1),7);
最终列表轮廓=新的ArrayList();
最终材料层次结构=新材料();
Imgproc.findContours(膨胀、轮廓、层次结构、,
Imgproc.RETR\u树、Imgproc.CHAIN\u近似值\u简单值);
用于(点轮廓:轮廓){
RotatedRect rect=Imgproc.minareact(新的MatOfPoint2f(contour.toArray());
垫箱=新垫();
Imgproc.boxPoints(矩形、长方体);
绘制等高线(图像,等高线,-1,新标量(0,0255));
}
这是生成的图像:

正如您可能看到的-连同有用的内容,仍然有一些扫描伪影位于红色轮廓上

是否有可能以某种常见的方式(不仅适用于此图片)移除这些扫描伪影而不损坏内容


此外,如何根据轮廓正确旋转此图像内部的内容(而不是图像本身?

此问题可以视为文本检测情况

我们可以使用一些静态图像分析:

  • 转换为灰阶
  • 应用模糊/平滑
  • 阈值图像
  • 应用形态扩张
  • 查找连接的组件
  • 过滤掉小面积的组件
--

高斯模糊

阈值化

反转颜色

扩张

已更新检测区域(过滤后)

--

System.load(“opencv_java320.dll”);
Mat dst=新Mat();
Mat src=Imgcodecs.imread(“path/to/your/image.png”);
//转换为灰度
Imgproc.cvt颜色(src、dst、Imgproc.COLOR_RGB2GRAY,0);
//模糊/平滑
Imgproc.GaussianBlur(dst,src,新尺寸(15.0,15.0),0.0,0.0);
//阈值化/二值化
Imgproc.threshold(src,dst,150255,Imgproc.THRESH_二进制);
已涂漆垫=新垫();//更新
src.copyTo(涂漆);//更新
//反转颜色(有助于放大)
Core.bitwise_not(dst,src);
//图像膨胀
Mat structuringElement=Imgproc.getStructuringElement(Imgproc.morp_RECT,新大小(55.0,55.0));
Imgproc.deflate(src、dst、structuringElement);
//检测文本区域
列表文本块=findTextBlocks(dst);
//绘制检测到的文本区域
paintTextBlocks(textBlocks,painted);

静态列表findTextBlocks(扩展)
{
材料标签=新材料();
Mat stats=新Mat();
垫质心=新垫();
//查找连接的组件
int numberOfLabels=Imgproc.connectedComponentsWithStats(扩展,标签,统计,质心,8,CvType.CV_16U);
List textBlocks=new ArrayList();
//根据需要调整此阈值
双尺寸threshold=0.01;
//标签0被认为是背景标签,因此我们跳过它
对于(int i=1;i0){
textBlocks.add(textBlock);
}
}
返回文本块;
}
静态无效paintTextBlocks(列出textBlocks,Mat原件)
{
for(Rect r:textBlocks)
{
Imgproc.矩形(原始、新点(r.x、r.y)、新点(r.x+r.width、r.y+r.height),
新标量(100.0),2;
}
}
您可以调整以下内容:

1)Imgproc.threshold的第三个参数方法。查看代码,这意味着颜色值大于150的任何像素都将替换为255(白色)。因此,增加此数字将减少黑色/文本像素。 减少数量将导致更多的黑色区域,例如伪影

2)膨胀结构元素(矩形)的大小。宽度和高度应相同,且均为奇数。结构元素的尺寸越小,膨胀越弱;连接的组件越小。尺寸越大,膨胀越宽,连接的组件越大


findTextBlocks()中的sizeThreshold
method。此变量根据连接组件的大小/面积控制过滤的强度。非常小的阈值将导致获得较小的区域,例如瑕疵,而较大的阈值将仅导致非常大的检测区域。

非常感谢您的回答。此方法的最大问题是它不会清除文本附近的工件。它只是突出显示文本周围的矩形…如果在这个矩形内有工件,它们也会出现在屏幕上screen@alexanoid工件将被移除,无论是在文本区域的内部还是外部法师,现在我有了
    System.load("opencv_java320.dll");

    Mat dst = new Mat();
    Mat src = Imgcodecs.imread("path/to/your/image.png");

    // Converting to Grey Scale
    Imgproc.cvtColor(src, dst, Imgproc.COLOR_RGB2GRAY, 0);

    // Blurring/Smoothing
    Imgproc.GaussianBlur(dst, src, new Size(15.0,15.0),0.0,0.0);

    // Thresholding / Binarization
    Imgproc.threshold(src, dst, 150,255,Imgproc.THRESH_BINARY);
    Mat painted = new Mat(); // UPDATED
    src.copyTo(painted); // UPDATED

    // Invert colors (helps with dilation)
    Core.bitwise_not(dst,src);

    // Image Dilation
    Mat structuringElement = Imgproc.getStructuringElement(Imgproc.MORPH_RECT, new Size(55.0,55.0));
    Imgproc.dilate(src, dst, structuringElement);

    // Detect Text Areas
    List<Rect> textBlocks = findTextBlocks(dst);

    // Paint detected text areas
    paintTextBlocks(textBlocks, painted);
static List<Rect> findTextBlocks(Mat dilated)
{
    Mat labels = new Mat();
    Mat stats = new Mat();
    Mat centroids = new Mat();
    // Find connected components
    int numberOfLabels = Imgproc.connectedComponentsWithStats(dilated,labels,stats,centroids,8, CvType.CV_16U);
    List<Rect> textBlocks = new ArrayList<>();
    // adjust this threshold as your desire
    double sizeThreshold = 0.01;
    // Label 0 is considered to be the background label, so we skip it
    for (int i = 1; i < numberOfLabels; i++)
    {
        // stats columns; [0-4] : [left top width height area}
        Rect textBlock = new Rect(new Point(stats.get(i,0)[0],stats.get(i,1)[0]),new Size(stats.get(i,2)[0],
                stats.get(i,3)[0]));
        // stats.get(i,4)[0] is the area of the connected component / Filtering out small areas
        if (Double.compare(stats.get(i,4)[0],dilated.height() * dilated.width() * sizeThreshold) > 0){
            textBlocks.add(textBlock);
        }
    }
    return textBlocks;
}

static void paintTextBlocks(List<Rect> textBlocks, Mat original)
{
    for (Rect r : textBlocks)
    {
        Imgproc.rectangle(original, new Point(r.x,r.y), new Point(r.x+r.width,r.y+r.height),
                new Scalar(100.0),2);
    }
}