首页 文章

Android实时黑白阈值图像

提问于
浏览
6

我有一个代码,使用以下代码将具有灰色的位图转换为黑色和白色的位图:

// scan through all pixels
for (int x = 0; x < width; ++x) {                                                                                                                                                                                               
  for (int y = 0; y < height; ++y) {                                                                                                                                                                                            
    // get pixel color
    pixel = bitmap.getPixel(x, y);                                                                                                                                                                                              
    A = Color.alpha(pixel);                                                                                                                                                                                                     
    R = Color.red(pixel);                                                                                                                                                                                                       
    G = Color.green(pixel);                                                                                                                                                                                                     
    B = Color.blue(pixel);                                                                                                                                                                                                      
    int gray = (int) (0.2989 * R + 0.5870 * G + 0.1140 * B);                                                                                                                                                                    

    // use 128 as threshold, above -> white, below -> black
    if (gray > 128)
    gray = 255;                                                                                                                                                                                                                 
    else
    gray = 0;                                                                                                                                                                                                                   
    // set new pixel color to output bitmap
    bmOut.setPixel(x, y, Color.argb(A, gray, gray, gray));                                                                                                                                                                      
  }
}

正如你所看到的那样,我遍历原始位图的所有像素点,然后我将颜色的分量与给定的阈值进行比较,在这种情况下为128,然后如果它在上面我说它是白色,否则它将是黑色像素 .

我现在想做的是一个可以改变该阈值的Spinner,然后BW图像会有所不同 .

要做到这一点,我需要再次绘制所有图像,这是非常cpu的成本时间,它需要时间再次传输所有像素 .

有没有办法实时使用不同的BW阈值更改图像?

有人告诉我使用GIF,然后我会做的只是改变GIF的查找表值,有人在Android上有这方面的知识吗?

3 回答

  • 1

    自问这个问题以来已经过了一点时间,但我碰到了这个寻找其他东西并碰巧得到了解决方案 . 您可以在没有OpenCV或任何其他第三方库的情况下实现此目的,仅使用API级别1以后的ColorMatrixColorFilter .

    以下是您可以使用的矩阵:

    //matrix that changes picture into gray scale
    public static ColorMatrix createGreyMatrix() {
        ColorMatrix matrix = new ColorMatrix(new float[] {
                0.2989f, 0.5870f, 0.1140f, 0, 0,
                0.2989f, 0.5870f, 0.1140f, 0, 0,
                0.2989f, 0.5870f, 0.1140f, 0, 0,
                0, 0, 0, 1, 0
        });
        return matrix;
    }
    
    // matrix that changes gray scale picture into black and white at given threshold.
    // It works this way:
    // The matrix after multiplying returns negative values for colors darker than threshold
    // and values bigger than 255 for the ones higher. 
    // Because the final result is always trimed to bounds (0..255) it will result in bitmap made of black and white pixels only
    public static ColorMatrix createThresholdMatrix(int threshold) {
        ColorMatrix matrix = new ColorMatrix(new float[] {
                85.f, 85.f, 85.f, 0.f, -255.f * threshold,
                85.f, 85.f, 85.f, 0.f, -255.f * threshold,
                85.f, 85.f, 85.f, 0.f, -255.f * threshold,
                0f, 0f, 0f, 1f, 0f
        });
        return matrix;
    }
    

    以下是如何使用它们:

    BitmapFactory.Options options = new BitmapFactory.Options();
    options.inScaled = false;
    
    //load source bitmap and prepare destination bitmap
    Bitmap pic = BitmapFactory.decodeResource(getResources(), R.drawable.thePicture, options);
    Bitmap result = Bitmap.createBitmap(pic.getWidth(), pic.getHeight(),  Bitmap.Config.ARGB_8888);
    Canvas c = new Canvas(result);
    
    //first convert bitmap to grey scale:
    bitmapPaint.setColorFilter(new ColorMatrixColorFilter(createGreyMatrix()));
    c.drawBitmap(pic, 0, 0, bitmapPaint);
    
    //then convert the resulting bitmap to black and white using threshold matrix 
    bitmapPaint.setColorFilter(new ColorMatrixColorFilter(createThresholdMatrix(128)));
    c.drawBitmap(result, 0, 0, bitmapPaint);
    
    //voilà! You can now draw the result bitmap anywhere You want:
    bitmapPaint.setColorFilter(null);
    otherCanvas.drawBitmap(result, null, new Rect(x, y, x + size, y + size), bitmapPaint);
    

    希望这会对某人有所帮助 .

  • 4

    我建议您使用适用于Android的OpenCV库 . 安装完成后,可以使用它在图像上的许多其他常用操作中进行快速阈值处理 . 以下代码片段将您的颜色位图转换为灰度图像,阈值为灰度级128 .

    // first convert bitmap into OpenCV mat object
    Mat imageMat = new Mat (bitmap.getHeight(), bitmap.getWidth(),
                    CvType.CV_8U, new Scalar(4));
    Bitmap myBitmap = bitmap.copy(Bitmap.Config.ARGB_8888, true);
    Utils.bitmapToMat(myBitmap, imageMat);
    
    // now convert to gray
    Mat grayMat = new Mat ( bitmap.getHeight(), bitmap.getWidth(),
                CvType.CV_8U, new Scalar(1));
    Imgproc.cvtColor(imageMat, grayMat, Imgproc.COLOR_RGB2GRAY, 1);
    
    // get the thresholded image
    Mat thresholdMat = new Mat ( bitmap.getHeight(), bitmap.getWidth(),
                CvType.CV_8U, new Scalar(1));
    Imgproc.threshold(grayMat, thresholdMat , 128, 255, Imgproc.THRESH_BINARY);
    
    // convert back to bitmap for displaying
    Bitmap resultBitmap = Bitmap.createBitmap(bitmap.cols(), bitmap.rows(),
                    Bitmap.Config.ARGB_8888);
    thresholdMat.convertTo(thresholdMat, CvType.CV_8UC1);
    Utils.matToBitmap(thresholdMat, resultBitmap);
    
  • 3

    GIF在 Headers 中有一个调色板,可以为每个"value"关联哪种颜色(Wikipedia entry) . 问题:回到那天他们认为256种颜色就足够了,这就像有多少调色板条目一样 . 您可以使用类GifDecoder.java与256个条目和两个值的本地颜色表:您的两个输出0和255.但是在一天结束时,有一个循环检查每个字节,通过调色板查找关联哪个值( this is the line) . 不比你的代码快 .

    另一种方法是使这个更快,可以使用OpenGL来产生阈值(假设你要在屏幕上绘制Bitmap,对吧?) . 这需要一堆额外的代码,幸运的是this tutorial正是你想要做的,减去阈值部分 .

    对于阈值处理,需要编译OpenGL程序(在 GlRenderer::onSurfaceChanged 上)并且每次都使用(在 GlRenderer::onDrawFrame 上) . 该程序获得两个着色器,以光速为每个像素阈值! (好吧,也许有点慢,但速度非常快!) .

    着色器将如下所示 . 如果我可以帮助您将它们插入到示例代码中,请告诉我 .

    private static final String mVertexShader =
        "attribute vec4 aPosition;\n" +
        "attribute vec4 aTextureCoord;\n" +
        "varying vec2 vTextureCoord;\n" +
        "void main() {\n" +
        "  gl_Position = aPosition;\n" +
        "  vTextureCoord = aTextureCoord;\n" +
        "}\n";
    
    private static final String mFragmentShader =
        "precision mediump float;\n" +
        "varying vec2 vTextureCoord;\n" +
        "uniform float threshold_in_range_0_to_1;\n" +
        "const vec4 coeff_y = vec4(0.256, 0.504, 0.097, 0.0625);\n" +
        "float y;\n" +
        "void main() {\n" +
        "  y = dot(coeff_y, texture2D(sTexture, vTextureCoord));\n" +
        "  if ( y > threshold_in_range_0_to_1)" +
        "    gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0)\n" +
        "  else" +
        "    gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0)\n" +
        "}\n";
    

相关问题