Android:将位图保存到SD卡时图像质量非常差

时间:2015-07-15 09:08:56

标签: java android image filter

我正在制作适用于Android的OCR应用,它会截取一些文字,识别它并在Google上搜索关键字。如果你还没有意识到,那我就试图制作一个" Google Now on Tap"克隆

为了使OCR更好地工作,我首先旋转图像,然后过滤图像。首先删除状态栏和导航栏,然后将其转换为灰度,然后进行锐化。

但是过滤图像后的图像质量极其像素化,这极大地影响了OCR的准确性。

以下是图片,之前和之后(仅我收到的IFTTT电子邮件) Before Filtering After Filtering

正如您所看到的,之前的图像质量远高于过滤和旋转的图像。

以下是我的旋转,过滤和保存图片的代码:

首先截屏,然后保存屏幕截图。

    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");
        }

    }

然后,旋转图像:



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);
	}




然后,过滤图像:

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);
    }

然后,保存已过滤和旋转的图像:

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();

        }
    }

感谢大家花时间提供帮助。

1 个答案:

答案 0 :(得分:1)

看起来像jpeg压缩正在弄乱图像。尝试使用更适合具有锐边的图像的格式,例如文本。我会推荐png甚至是gif。您还可以存储未压缩的BMP。

Jpeg压缩的工作原理是,在大多数图片(自然,人物,物体)中,锐利的边缘对于人眼来说是不可见的。这使得存储锋利边缘内容(例如文本)非常糟糕。

此外,您的图像滤镜有效地消除了图像的抗锯齿,这进一步降低了感知的图像质量。然而,可能是您想要做的,因为它可能使OCR更容易。

由于您上传的图片在网站上的尺寸相同,我也错过了采样尺寸。来自Android documentation

  

如果设置为值> 1,请求解码器对原始进行二次采样   图像,返回较小的图像以节省内存。样本量是   任一维度中对应于单个像素的像素数   解码后的位图中的像素。例如,inSampleSize == 4返回一个   图像是原稿宽度/高度的1/4,和1/16   像素数。任何值< = 1都被视为1.注意:   解码器使用基于2的幂的最终值,任何其他值都将   向下舍入到最接近2的幂。

options.inSampleSize = 4;设置为1会提高质量。