【问题标题】:Android: Really bad image quality when saving bitmap to sdcardAndroid:将位图保存到 SD 卡时图像质量真的很差
【发布时间】:2015-10-04 05:43:36
【问题描述】:

我正在为 Android 制作一个 OCR 应用程序,它将截取一些文本,识别它并在 Google 上搜索关键词。如果您还没有意识到,我正在尝试制作“Google Now on Tap”克隆。

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

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

这是之前和之后的图片(只是我收到的一封 IFTTT 电子邮件)

如您所见,之前的图像质量比经过过滤和旋转的图像要高得多。

这是我用于旋转、过滤和保存图像的代码:

先截图,再保存。

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

        }
    }

非常感谢任何花时间提供帮助的人。

【问题讨论】:

    标签: java android image filter


    【解决方案1】:

    它实际上是在我的onPhotoTaken 方法中。在获取屏幕截图并保存屏幕截图后,我正在从文件保存到的位置读取文件,然后对其进行过滤。我在onPhotoTaken 方法中更改了这一行:

    options.inSampleSize = 4options.inSampleSize = 1

    【讨论】:

      【解决方案2】:

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

      Jpeg 压缩利用了这样一个事实,即在大多数图片(自然、人物、物体)中,锐利的边缘对人眼来说是不可见的。这使得存储边缘锐利的内容(例如文本)非常糟糕。

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

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

      如果设置为大于 1 的值,则请求解码器对原始图像进行二次采样 图像,返回较小的图像以节省内存。样本量为 任一维度中对应于单个维度的像素数 解码位图中的像素。例如, inSampleSize == 4 返回一个 图像的宽度/高度为原件的 1/4,原件的 1/16 像素数。任何值

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

      【讨论】:

      • 所以,使用 Bitmap.CompressFormat.PNG 代替 Bitmap.CompressFormat.JPG 并使用 .png 而不是 .jpg 保存它:ocrgray.png。抱歉,画质还是一样
      • 字母周围的灰色小瑕疵是由于 jpeg 压缩造成的,锯齿状边缘是由于您的过滤造成的。我希望保存为 PNG 时小工件会消失,但锯齿状边缘不会。你能把你的新png图片贴在这里吗?
      • 没问题,这里是:goo.gl/9LXIiK 如你所见,我试图去掉灰度滤镜,但你是对的,这是保存过程。
      • 是的,在该图像上,jpeg 伪影消失了。你对我的回答满意吗?
      • 真的很抱歉,但我不能接受你的回答,因为我最初的问题是去除像素化,而不是灰色伪影。问题实际上是,一旦我截取屏幕截图,我就从原始屏幕截图的位置读取它,并且在整个过程中对其进行了压缩。我将inSampleSize 更改为1。之前:options.inSampleSize = 4,之后:options.inSampleSize = 1。新图片:goo.gl/5XwiYS
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-08-04
      • 1970-01-01
      • 2012-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多