【问题标题】:Rgba to basic matrix (0 for white pixel, 1 for anything else) transformation with BufferedImage使用 BufferedImage 将 Rgba 转换为基本矩阵(0 表示白色像素,1 表示其他像素)
【发布时间】:2016-05-17 12:50:18
【问题描述】:

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

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

我正在使用 sketch.js 在画布上绘图

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

基于这两个教程:

画布绘图: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 : https://stackoverflow.com/questions/4998908/convert-data-uri-to-file-then-append-to-formdata
            $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>

弹簧控制器:

@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 类进行测试以进行此转换,因为我读过您可以通过创建 来自原始 BufferedImage 的图像类型为 TYPE_BYTE_BINARY 的新 BufferedImage

但是这种方法总是会产生一个全黑的图像,所有字节都设置为 0。

所以我有两个问题? 有没有办法防止画布上的透明度? & 是否有内置方法可以将 rgba 转换为 0/1 矩阵?

谢谢。

【问题讨论】:

    标签: angularjs spring canvas png bufferedimage


    【解决方案1】:

    对于问题 1:

    有没有办法防止画布透明?

    其实有好几个。

    • 您可以将{alpha: false} 对象传递给canvas.getContext('2d') 方法。
      这将禁用上下文的默认透明度,将其设置为黑色矩形。请注意,它不会禁用进一步绘制的对象的透明度(ctx.globalAlphargba 颜色仍将受到尊重,它们将简单地在黑色背景上计算)。这是因为默认情况下,画布上的透明度为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>

    第二个问题,

    是否有内置方法可以将 rgba 转换为 0/1 矩阵?

    没有,或者至少在我的知识中没有。

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

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

    var img = new Image();
    
    // to be able to use the getImageData method, we have to be sure we don't taint the canvas
    // see http://stackoverflow.com/questions/34743987/canvas-image-crossplatform-insecure-error/34755162#34755162 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>

    【讨论】:

    • 我使用了您提出的最后一个解决方案,在客户端上进行转换,而不是在服务器上进行。谢谢老兄:)
    猜你喜欢
    • 1970-01-01
    • 2012-07-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多