简体   繁体   English

Android:将位图保存到sdcard时,图像质量真的很差

[英]Android: Really bad image quality when saving bitmap to sdcard

I am making an OCR app for Android, that will take a screenshot of some text, recognise it and search a key word on Google. 我正在开发一个用于Android的OCR应用,它将对一些文本进行截图,识别并在Google上搜索关键字。 If you haven't already realized, I'm trying to make a "Google Now on Tap" clone. 如果您还没有意识到,我正在尝试制作一个“ Google Now on Tap”克隆。

To make the OCR work better, I am first rotating the image, then filtering the image. 为了使OCR更好地工作,我首先旋转图像,然后过滤图像。 First by getting rid of the status bar and the navigation bar, then converting it to grayscale, then sharpening. 首先,摆脱状态栏和导航栏,然后将其转换为灰度,然后进行锐化。

But the image quality after filtering the image is extremely pixelated, and this greatly effects OCR accuracy. 但是对图像进行滤波后的图像质量非常像素化,这大大影响了OCR的准确性。

Here are the images, before and after (just of an IFTTT email I got) 这是之前和之后的图片(仅是我收到的IFTTT电子邮件) 过滤之前过滤后

As you can see, the before image is much higher quality than the filtered and rotated one. 如您所见,之前的图像质量比经过过滤和旋转的图像要高得多。

Here is my code for rotating, filtering and saving the image: 这是我用于旋转,过滤和保存图像的代码:

Firstly taking screenshot, then saving the screenshot. 首先获取屏幕截图,然后保存屏幕截图。

    public void getScreenshot()
    {
        try
        {
            Process sh = Runtime.getRuntime().exec("su", null, null);

            OutputStream os = sh.getOutputStream();
            os.write(("/system/bin/screencap -p " + _path).getBytes("ASCII"));
            os.flush();

            os.close();
            sh.waitFor();

            onPhotoTaken();

            Toast.makeText(this, "Screenshot taken", Toast.LENGTH_SHORT).show();
        }
        catch (IOException e)
        {
            System.out.println("IOException");
        }
        catch (InterruptedException e)
        {
            System.out.println("InterruptedException");
        }

    }

Then, rotate the image: 然后,旋转图像:

 protected void onPhotoTaken() { _taken = true; BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 4; Bitmap bitmap = BitmapFactory.decodeFile(_path, options); try { ExifInterface exif = new ExifInterface(_path); int exifOrientation = exif.getAttributeInt( ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL); Log.v(TAG, "Orient: " + exifOrientation); int rotate = 0; switch (exifOrientation) { case ExifInterface.ORIENTATION_ROTATE_90: rotate = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: rotate = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: rotate = 270; break; } Log.v(TAG, "Rotation: " + rotate); if (rotate != 0) { // Getting width & height of the given image. int w = bitmap.getWidth(); int h = bitmap.getHeight(); // Setting pre rotate Matrix mtx = new Matrix(); mtx.preRotate(rotate); // Rotating Bitmap bitmap = Bitmap.createBitmap(bitmap, 0, 0, w, h, mtx, false); } // Convert to ARGB_8888, required by tess bitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true); } catch (IOException e) { Log.e(TAG, "Couldn't correct orientation: " + e.toString()); } // _image.setImageBitmap( bitmap ); setImageFilters(bitmap); } 

Then, filter the image: 然后,过滤图像:

public void setImageFilters(Bitmap bmpOriginal)
    {
        //Start by cropping image
        Bitmap croppedBitmap = ThumbnailUtils.extractThumbnail(bmpOriginal, 1080, 1420);

        //Then convert to grayscale
        int width, height;
        height = 1420;
        width = 1080;

        Bitmap bmpGrayscale = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas c = new Canvas(bmpGrayscale);
        Paint paint = new Paint();
        ColorMatrix cm = new ColorMatrix();
        cm.setSaturation(0);
        ColorMatrixColorFilter f = new ColorMatrixColorFilter(cm);
        paint.setColorFilter(f);
        c.drawBitmap(croppedBitmap, 0, 0, paint);

        //Finally, sharpen the image
        double weight = 11;
        double[][] sharpConfig = new double[][]
                {
                        { 0 ,   -2  , 0  },
                        { -2, weight, -2 },
                        { 0 ,   -2  , 0  }
                };

        ConvolutionMatrix convMatrix = new ConvolutionMatrix(3);
        convMatrix.applyConfig(sharpConfig);
        convMatrix.Factor = weight - 8;

        Bitmap filteredBitmap = ConvolutionMatrix.computeConvolution3x3(bmpGrayscale, convMatrix);

        //Start Optical Character Recognition
        startOCR(filteredBitmap);

        //Save filtered image
        saveFiltered(filteredBitmap);
    }

Then, saving the filtered and rotated image: 然后,保存过滤并旋转的图像:

public void saveFiltered(Bitmap filteredBmp) {
        try {
            ByteArrayOutputStream bytes = new ByteArrayOutputStream();
            filteredBmp.compress(Bitmap.CompressFormat.JPEG, 20, bytes);

            //You can create a new file name "test.jpg" in sdcard folder.
            File f = new File("/sdcard/SimpleAndroidOCR/ocrgray.jpg");
            f.createNewFile();
            //Write the bytes in file
            FileOutputStream fo = new FileOutputStream(f);
            fo.write(bytes.toByteArray());

            //Remember close the FileOutput
            fo.close();

        } catch (Exception e) {
            e.printStackTrace();

        }
    }

Thanks heaps for anyone taking the time to help. 感谢您抽出宝贵的时间为您提供帮助。

It was actually in my onPhotoTaken method. 它实际上在我的onPhotoTaken方法中。 After taking and saving the screenshot in get screenshot, I am reading the file from the location it was saved to, then filtering it. 截取屏幕截图并将其保存在get屏幕截图中之后,我将从文件保存到的位置读取文件,然后对其进行过滤。 I changed this line in the onPhotoTaken method: 我在onPhotoTaken方法中更改了这一行:

options.inSampleSize = 4 to options.inSampleSize = 1 options.inSampleSize = 4options.inSampleSize = 1

It does look like the jpeg compression is messing the image up. 看起来jpeg压缩确实使图像混乱。 Try using a format better suited for images with sharp edges, such as of text. 尝试使用更适合于带有锐利边缘的图像(例如文本)的格式。 I would recommend png or even gif. 我会推荐png甚至gif。 You could also store the uncompressed BMP. 您还可以存储未压缩的BMP。

Jpeg compression works by exploiting the fact that in most pictures (nature, people, objects), sharp edges are not that visible to the human eye. Jpeg压缩通过利用以下事实进行工作:在大多数图片(自然,人,物体)中,人眼看不见锐利的边缘。 This makes it really bad for storing sharp edged content, such as text. 这对于存储尖锐的边缘内容(例如文本)确实非常不利。

Also, your image filter is effectively removing the anti-aliasing of the image, which further decreases the perceived image quality. 同样,您的图像滤镜可以有效消除图像的抗锯齿,从而进一步降低可感知的图像质量。 That might be what you want to do, however, since it might make OCR easier. 但是,这可能就是您要执行的操作,因为这可能会使OCR变得更容易。

I also missed the sampling size due to the images you uploaded being the same size here on the site. 由于您上传的图片与网站上的图片大小相同,因此我也错过了采样大小。 From the Android documentation : Android文档中

If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory. 如果设置为大于1的值,则请求解码器对原始图像进行二次采样,返回较小的图像以节省内存。 The sample size is the number of pixels in either dimension that correspond to a single pixel in the decoded bitmap. 样本大小是任一维度中与已解码位图中单个像素相对应的像素数。 For example, inSampleSize == 4 returns an image that is 1/4 the width/height of the original, and 1/16 the number of pixels. 例如,inSampleSize == 4返回的图像为原始宽度/高度的1/4,像素数目的1/16。 Any value <= 1 is treated the same as 1. Note: the decoder uses a final value based on powers of 2, any other value will be rounded down to the nearest power of 2. 任何小于等于1的值都与1相同。注意:解码器使用基于2的幂的最终值,任何其他值将四舍五入为最接近的2的幂。

Setting options.inSampleSize = 4; 设置options.inSampleSize = 4; to 1 instead will increase the quality. 改为1将提高质量。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM