Rgba 到基本矩阵(白色像素为 0,其他任何像素为 1)与 BufferedImage 的转换

Rgba to basic matrix (0 for white pixel, 1 for anything else) transformation with BufferedImage

我正在从事光学字符识别学校项目。

在这一步,我必须画一个角色然后应用弗里曼链码。

我正在使用 sketch.js

在 canvas 上绘图

前端代码用 angularjs 处理,后端用 Spring & BufferedImage

基于这两个教程:

对于canvas绘图:https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Pixel_manipulation_with_canvas#Grayscaling_and_inverting_colors

sketch.js:https://intridea.github.io/sketch.js/docs/sketch.html

我写了这个客户端代码:

...
    <div class="container">
        <div ng-controller="ctrl">
            <canvas ng-model="canvas" ng-click="enableSave()" class="panel panel-default" width="200" height="200"></canvas>
            <form class="form-horizontal" enctype="multipart/form-data">
                <div class="input-group col-md-4">
                    <span class="input-group-btn">
                        <button ng-click="clear()" id="clear" type="button" class="btn btn-default">Clear</button>
                    </span>
                    <input ng-model="num" type="number" id="num" class="form-control" type="text">
                    <span class="input-group-btn">
                        <button ng-click="save()" ng-disabled="modif == false" type="button" class="btn btn-success">Save</button>
                    </span>
                </div>
            </form>
        </div>
    </div>
    <script type="text/javascript" src="assets/js/jquery-2.2.0.min.js"></script>
    <script type="text/javascript" src="assets/js/sketch.min.js"></script>
    <script type="text/javascript" src="assets/js/angular.min.js"></script>
    <script type="text/javascript">
        var app = angular.module('app', []);
        app.controller('ctrl', ['$scope', '$log', '$http', function($scope, $log, $http) {
            // init
            $scope.modif = false;
            $scope.canvas = angular.element('canvas');
            $scope.context = $scope.canvas[0].getContext('2d');
            $scope.canvas.sketch({
                defaultColor: "#000000",
                defaultSize: 2
            });

            // enable save button if user draw on canvas
            $scope.enableSave = function() {
                $scope.modif = true;
            };
            // convert image to blob : 
            $scope.toBlob = function(dataURI) {
                var byteString;
                if (dataURI.split(',')[0].indexOf('base64') >= 0)
                    byteString = atob(dataURI.split(',')[1]);
                else
                    byteString = unescape(dataURI.split(',')[1]);
                var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
                var ia = new Uint8Array(byteString.length);
                for (var i = 0; i < byteString.length; i++)
                    ia[i] = byteString.charCodeAt(i);
                return new Blob([ia], {
                    type: mimeString
                });
            };
            // send user's input to server
            $scope.save = function() {
                var img = $scope.canvas[0].toDataURL('image/png');
                $log.info(img);
                var fd = new FormData();
                var blob = $scope.toBlob(img);
                fd.append('img', blob);
                $http.post('add.html', fd, {
                        withCredentials: true,
                        headers: {
                            'Content-Type': undefined
                        },
                        transformRequest: angular.identity
                    })
                    .success(function(result) {
                        $log.info(result);
                    })
                    .error(function(data, status) {});
                // clear canvas
                img = null;
                $scope.clear();
                // disable save button
                $scope.modif = false;
            };
            // clear canvas
            $scope.clear = function() {
                $scope.context.clearRect(0, 0, $scope.canvas[0].width, $scope.canvas[0].height);
                $scope.canvas.sketch('actions', []);
            }
        }]);
        $(document).ready(function() {});
    </script>
</body>

</html>

Spring 控制器:

@RequestMapping(value = "/add", method = RequestMethod.POST)
@ResponseBody
public String add(@RequestParam("img") MultipartFile image) throws IOException {
    log.info("add : POST");

    log.info("bytes image : " + image.getBytes().length);

    BufferedImage original = ImageIO.read(image.getInputStream());
    BufferedImage non_transparent = new BufferedImage(original.getWidth(), original.getHeight(),
            BufferedImage.TYPE_INT_RGB);
    BufferedImage black_white = new BufferedImage(non_transparent.getWidth(), non_transparent.getHeight(),
            BufferedImage.TYPE_BYTE_BINARY);

    non_transparent.getGraphics().drawImage(original, 0, 0, null);
    non_transparent.getGraphics().dispose();

    black_white.getGraphics().drawImage(non_transparent, 0, 0, null);
    black_white.getGraphics().dispose();

    byte[] original_pixels = ((DataBufferByte) original.getRaster().getDataBuffer()).getData();
    log.info("original pixels : " + original_pixels.length);

    int[] non_transparent_pixels = ((DataBufferInt) non_transparent.getRaster().getDataBuffer()).getData();
    log.info("non transparent pixels : " + non_transparent_pixels.length);

    byte[] black_white_pixels = ((DataBufferByte) black_white.getRaster().getDataBuffer()).getData();
    log.info("black white pixels : " + black_white_pixels.length);

    FileOutputStream fos_original = new FileOutputStream("original.txt");
    fos_original.write(original_pixels);
    fos_original.close();
    File f_original = new File("original.png");
    ImageIO.write(original, "png", f_original);

    File f_non_transparent = new File("non_transparent.png");
    ImageIO.write(non_transparent, "png", f_non_transparent);

    FileOutputStream fos_black_white = new FileOutputStream("black_white.txt");
    fos_black_white.write(black_white_pixels);
    fos_black_white.close();
    File f_black_white = new File("black_white.png");
    ImageIO.write(black_white, "png", f_black_white);
    return STATUS;
}

无论我在 var img = $scope.canvas[0].toDataURL('image/png'); 中选择什么图片格式 除了在像素上绘制的图片之外,图片总是发送全透明。

我想将发送的图片从 rgba 转换为基本矩阵(0 表示白色像素,1 表示其他任何像素)。

我开始测试 BufferedImage class 来做这个转换,因为我读过你可以通过创建 来自原始 BufferedImage

的图像类型为 TYPE_BYTE_BINARY 的新 BufferedImage

但此方法总是生成全黑图像,所有字节都设置为 0。

所以我有两个问题? 有没有办法防止 canvas 透明? & 有没有内置的方法来进行 rgba 到 0/1 矩阵转换?

谢谢。

对于问题 1:

Is there a way to prevent transparency on the canvas ?

实际上有几个。

  • 您可以将 {alpha: false} 对象传递给 canvas.getContext('2d') 方法。
    这将禁用上下文的默认透明度,将其设置为黑色矩形。请注意,它不会禁用进一步绘制对象的透明度(ctx.globalAlphargba 颜色仍将受到尊重,它们将简单地在黑色背景上计算)。这是因为默认情况下,canvas 的透明度是 rgba(0,0,0,0),所以删除 alpha 值只保留 rgb(0,0,0).

  • 另一种解决方案是用您想要的纯色填充一个用作背景的矩形。

var img = new Image();
img.onload = function(){

  //_____OPTION 1______//

  // get the #noAlpha canvas' 2d context, with the alpha parameter set to false
  var naCtx = noAlpha.getContext('2d', {alpha:false});
  // now you can draw a partially transparent image and it will have a black background
  naCtx.drawImage(img, 20,20);
  
  
  //_____OPTION 2______//  
  
  // get the #whiteFill canvas' 2d context
  var wfCtx = whiteFill.getContext('2d');
  // set the context' fill color to white
  // this can be any CSS color argument
  wfCtx.fillStyle = 'white';
  // fill a rectangle as big as your canvas
  wfCtx.fillRect(0,0, whiteFill.width, whiteFill.height);
  // you've got a white background
  // now you can draw a partially transparent image and it will have a white background
  wfCtx.drawImage(img, 20,20);
  };
img.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png"
body{background-color: ivory;}
<canvas id="noAlpha" width="70" height="70"></canvas>
<canvas id="whiteFill" width="70" height="70"></canvas>

对于第二个问题,

Is there a built in way to do rgba to 0/1 matrix transformation ?

没有,至少据我所知没有。

我会做什么:保持图像的透明度,除非您确实需要将白色视为透明,在这种情况下,请参阅此答案的最后一个片段。

然后很容易使用 ctx.getImageData() 方法,遍历其 data 属性 并只检查 alpha 值。 (getImageData.data 是一个 TypedArray,表示 canvas(红色、绿色、蓝色和 alpha 通道)请求部分的 rgba 值。

var img = new Image();

// to be able to use the getImageData method, we have to be sure we don't taint the canvas
// see  for more info
img.crossOrigin = 'anonymous';

img.onload = function(){

  var ctx = source.getContext('2d');
  // draw your image and keep the transparency
  ctx.drawImage(img, 0,0);
  
  var yourArray = [];
  // get the canvas' imageData from position 0,0 and of the ful size of the canvas
  var imageData = ctx.getImageData(0,0, source.width, source.height);
  // get the rgba array of this imageData
  var data = imageData.data;
  // loop through all pixels, checking only for the alpha value
  for(var i=3; i<data.length-3; i+=4){
    // this is a non-transparent pixel
    if(data[i] > 0) {
      yourArray.push(1);
      // set the r,g,b values to 0 (black)
      data[i-3] = data[i-2] = data[i-1] = 0;
      // set the alpha value to 255
      data[i] = 255;
      }
    else{
      yourArray.push(0);
      }
    }
  console.log(yourArray);
  // draw this new image over the export canvas (could alos be the first one
  output.getContext('2d').putImageData(imageData, 0,0);
  };
img.src = "https://dl.dropboxusercontent.com/s/4e90e48s5vtmfbd/aaa.png"
body{background-color: ivory;}
<canvas id="source" width="32" height="32"></canvas>
<canvas id="output" width="32" height="32"></canvas>

以下是如果您需要将白色像素值视为透明时如何遍历所有像素值的方法:

var img = new Image();
img.crossOrigin = 'anonymous';

img.onload = function(){

  var ctx = source.getContext('2d');

  ctx.drawImage(img, 0,0);
  
  var imageData = ctx.getImageData(0,0, source.width, source.height);
  var data = imageData.data;
  
  var yourArray = [];
  
  // loop through all pixels, checking for every rgba value
  for(var i=0; i<data.length; i+=4){
    // this is a non-white pixel
    if( data[i]+data[i+1]+data[i+2]+data[i+3] < 255*4 ) {
      yourArray.push(1);
      // set the r,g,b values to 0 (black)
      data[i] = data[i+1] = data[i+2] = 0;
      // set the alpha value to 255
      data[i+3] = 255;
     }else{
      yourArray.push(0);
      // set the alpha value to 0
      data[i+3] = 0;
     }
    }
  console.log(yourArray);
  output.getContext('2d').putImageData(imageData, 0,0);
  };
// an image with a white background
img.src = "https://dl.dropboxusercontent.com/s/iac97oiynwthrg5/someWhite.png"
body{background-color: ivory;}
<canvas id="source" width="284" height="383"></canvas>
<canvas id="output" width="284" height="383"></canvas>