【问题标题】:Convert all colors other than a particular color in a bitmap to white将位图中除特定颜色之外的所有颜色转换为白色
【发布时间】:2016-04-09 03:34:39
【问题描述】:

我正在使用 tess-two 库,我希望将图像中除黑色以外的所有颜色转换为白色(黑色将是文本)。从而使 tess-two 更容易阅读文本。我尝试了各种方法,但是它们在逐像素转换时花费了太多时间。有没有办法使用画布或任何可以更快产生结果的东西来实现这一点。

更新

这个算法带来的另一个问题是打印机不能使用与 android 中相同的黑色和白色进行打印。所以算法将整张图片转为白色。

我目前正在使用的逐像素方法。

 binarizedImage = convertToMutable(cropped);// the bitmap is made mutable
 int width = binarizedImage.getWidth();
 int height = binarizedImage.getHeight();
 int[] pixels = new int[width * height];
 binarizedImage.getPixels(pixels, 0, width, 0, 0, width, height);

 for(int i=0;i<binarizedImage.getWidth();i++) {
     for(int c=0;c<binarizedImage.getHeight();c++) {
         int pixel = binarizedImage.getPixel(i, c);
         if(!(pixel == Color.BLACK  || pixel == Color.WHITE))
         {
              int index = c * width + i;
             pixels[index] = Color.WHITE;
             binarizedImage.setPixels(pixels, 0, width, 0, 0, width, height);
          }
     }
 }

【问题讨论】:

  • 你可以使用ColorMatrix吗? - 用于转换位图的颜色和 alpha 分量的 4x5 矩阵。 -
  • 感谢@Blackbelt 的评论,但请您提供一些帮助以了解 ColorMatrix 将如何提供帮助
  • 至少修复你的循环。说真的,您循环遍历像素的大小(宽度*高度),然后简单地执行 if (pixel[i] == Color.BLACK) continue 然后将像素设置为白色。没有必要关心它的尺寸。这是一个整数数组,你想对它们都这样做。
  • 您可以在渲染脚本中更快地执行此操作,这将是相当微不足道的,并且会在 GPU 上完成所有工作。但是,我真的也认为 Rishabh 是对的,它应该可以在 ColorMatrix 中完成。

标签: android canvas bitmap tesseract


【解决方案1】:

根据 Rishabh 的评论。使用颜色矩阵。由于黑色是黑色并且是 RGB(0,0,0,255),因此它不受乘法的影响。因此,如果您将所有通道中的所有内容乘以 255,所有内容都将超过限制并卷曲为白色,黑色除外,它将保持黑色。

       ColorMatrix bc = new ColorMatrix(new float[] {
                255, 255, 255, 0, 0,
                255, 255, 255, 0, 0,
                255, 255, 255, 0, 0,
                0, 0, 0, 1, 0,
        });
        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(bc);
        paint.setColorFilter(filter);

您可以使用该颜料在 only-black-stays-black colormatrix 滤镜的荣耀中绘制该位图。

注意:这是一个快速而绝妙的技巧,但是,它适用于黑色。虽然它非常适合您使用,并且会将冗长的操作变成即时的东西,但它实际上并不符合“特定颜色”的标题问题,我的算法适用于您想要的任何颜色,只要它是黑色的。

【讨论】:

  • 这是一个很棒的答案,而且效果很好,但出现的问题是,打印后它不会把黑色当作黑色,因为打印机每次打印的黑色量不同。再次感谢它适用于原始图片。
【解决方案2】:

虽然@Tata​​rize 的答案很完美,但我在阅读打印图像时遇到了麻烦,因为它并不总是黑色。

我在堆栈溢出中发现的这个算法效果很好,它实际上检查特定像素是更接近黑色还是白色,并将像素转换为最接近的颜色。因此提供具有范围的二值化。 (https://stackoverflow.com/a/16534187/3710223)。

我现在正在做的是将不需要的区域保持为浅色,而将文本保持为黑色。该算法在大约 20-35 秒内给出二值化图像。仍然没有那么快但高效。

private static boolean shouldBeBlack(int pixel) {
        int alpha = Color.alpha(pixel);
        int redValue = Color.red(pixel);
        int blueValue = Color.blue(pixel);
        int greenValue = Color.green(pixel);
        if(alpha == 0x00) //if this pixel is transparent let me use TRASNPARENT_IS_BLACK
            return TRASNPARENT_IS_BLACK;

        // distance from the white extreme
        double distanceFromWhite = Math.sqrt(Math.pow(0xff - redValue, 2) + Math.pow(0xff - blueValue, 2) + Math.pow(0xff - greenValue, 2));

        // distance from the black extreme 
        double distanceFromBlack = Math.sqrt(Math.pow(0x00 - redValue, 2) + Math.pow(0x00 - blueValue, 2) + Math.pow(0x00 - greenValue, 2));

       // distance between the extremes
        double distance = distanceFromBlack + distanceFromWhite;

        return ((distanceFromWhite/distance)>SPACE_BREAKING_POINT);
    }

如果返回值为真,那么我们将像素转换为黑色,否则我们将其转换为白色。

我知道可以有更好/更快的答案,欢迎更多答案:)

【讨论】:

  • 如果你不使用欧几里得距离的怪物,你将在这里显着提高你的速度。不要使用幂函数来做 redValue * redValue。另外,不要为昂贵的 sqrt 函数而烦恼。欧几里得颜色的平方仍然同样是一种颜色距离算法,与真正的欧几里得颜色距离一样好。
  • distanceFromBlack = redValue * redValue + greenValue * greenValue + blueValue * blueValue
  • 然后,如果这个值低于某个阈值,比如说如果你想从真正的黑色中得到 ~20,它将是 3*20²,所以 1200 左右。任何小于 1200 的值都会很黑。
【解决方案3】:

同样的事情,但在渲染脚本中完成,时间约为 60-100 毫秒。你甚至不会注意到延迟。

    Bitmap blackbitmap = Bitmap.createBitmap(bitmap.getWidth(),bitmap.getHeight(),bitmap.getConfig());
    RenderScript mRS = RenderScript.create(TouchEmbroidery.activity);
    ScriptC_blackcheck script = new ScriptC_blackcheck(mRS);

    Allocation allocationRaster0 = Allocation.createFromBitmap(
            mRS,
            bitmap,
            Allocation.MipmapControl.MIPMAP_NONE,
            Allocation.USAGE_SCRIPT
    );
    Allocation allocationRaster1 = Allocation.createTyped(mRS, allocationRaster0.getType());
    script.forEach_root(allocationRaster0, allocationRaster1);
    allocationRaster1.copyTo(blackbitmap);

进行分配,使用renderscript将数据写入blackbitmap。

#pragma version(1)
#pragma rs java_package_name(<YOUR PACKAGENAME GOES HERE>)

void root(const uchar4 *v_in, uchar4 *v_out) {
    uint32_t value = (v_in->r * v_in->r);
    value = value + (v_in->g * v_in->g);
    value = value + (v_in->b * v_in->b);
    if (value > 1200) {
        v_out->r = 255;
        v_out->g = 255;
        v_out->b = 255;
    }
    else {
        v_out->r = 0;
        v_out->g = 0;
        v_out->b = 0;
    }
    v_out->a = 0xFF;
}

注意 1200 只是我使用的阈值,应该是所有三个分量都小于 20(或者,像 0、0、sqrt(1200) aka (~34))。您可以相应地上下设置 1200 的限制。

并且构建 gradle 需要 Renderscript:

renderscriptTargetApi 22

构建工具的最后几件事声称已经解决了一系列渲染脚本问题。因此,在像您这样的关键任务场所做这种事情可能是完全合理的。 20 秒等待太长,60 毫秒则不然。

【讨论】:

  • 虽然 ScriptIntrinsicLUT 真的不难为此工作。实际上甚至可能更快(甚至不会做乘法)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-23
  • 2022-11-25
  • 1970-01-01
  • 1970-01-01
  • 2012-05-14
  • 1970-01-01
相关资源
最近更新 更多