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
基于这两个教程:
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.globalAlpha
和 rgba
颜色仍将受到尊重,它们将简单地在黑色背景上计算)。这是因为默认情况下,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>
我正在从事光学字符识别学校项目。
在这一步,我必须画一个角色然后应用弗里曼链码。
我正在使用 sketch.js
在 canvas 上绘图前端代码用 angularjs 处理,后端用 Spring & BufferedImage
基于这两个教程:
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.globalAlpha
和rgba
颜色仍将受到尊重,它们将简单地在黑色背景上计算)。这是因为默认情况下,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>