基于Java BufferedImage实现识别图片中的黑色矩形
前言:在项目中需要实现将图片中的黑色矩形替换为其他图形,其中的难点在于图片中存在其他黑点或者黑色小方块。
实现思路:
- 二值化,将纯黑的区域保留下来,其他区域编程白色。
- 去噪:去除噪点
- 转为矩阵,将黑色像素点的位置的值设为1,其他位置的值设为0
- 求极大全为1的子矩阵,使用悬吊法求极大全为1的子矩阵
二值化代码:
public static void binaryImage(String filePath,double threshold){
try {
BufferedImage image = ImageIO.read(new File(filePath));
int minX = 0;//图片起始点X
int minY = 0;//图片起始点Y
int width = image.getWidth();//图片的宽度
m = width;
int height = image.getHeight();//图片的高度
n = height;
//将黑色区域化为1,其他为0
for (int i = minX; i < width; i++) {
for (int j = minY; j < height; j++) {
Object data = image.getRaster().getDataElements(i, j, null);//获取该点像素,并以object类型表示
int red = image.getColorModel().getRed(data);
int blue = image.getColorModel().getBlue(data);
int green = image.getColorModel().getGreen(data);
if(red==0&&green==0&&blue==0){
a[i+1 ][j+1 ] = 1;
}
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
去噪:
public static void removeNoise(int whiteThreshold,int blackThreshold){
int i,j,nValue,nCount,m,n;
int nWidth=Calculate.maxn;
int nHeight=Calculate.maxn;
//对图像上下边缘去噪
for(i=0;i<nWidth;i++){
a[i][0]=0;
a[i][nHeight-1]=0;
}
//对图像上下边缘去噪
for( i=0;i<nHeight;i++){
a[0][i]=0;
a[0][nWidth-1]=0;
}
//根据周围点的颜色去噪
//遍历所有的点 //j是y, i是x
for(i=1;i<nWidth-1;i++){
for( j=1;j<nHeight-1;j++){
nValue=a[i][j];
if(nValue==1&&whiteThreshold!=0){//如果一点是黑点
nCount=0;
//遍历他周围的八个点,如果他
for(m=j-1;m<=j+1;m++){
for(n=i-1;n<=i+1;n++){
if(a[n][m]==0){//周围白点的个数
nCount++;
}
}
}
if(nCount>=whiteThreshold){//周围白点的个数大于阈值则变为白色
a[i][j]=0;
}
}else{//如果一个点是白色的点,周围的点是黑色
nCount=0;
for(m=j-1;m<=j+1;m++){
for(n=i-1;n<=i+1;n++){
if(a[n][m]==1){
nCount++;
}
}
}
if(nCount>=blackThreshold){//周围黑点的个数大于阈值就变成黑色
a[i][j]=1;
}
}
}
}
}
找到极大全为1的矩形矩阵:
public static void findMaxBlackRect(){
int ans = 0;
//初始化
for(int i=1;i<=m;++i) {
l[0][i] = 1;
r[0][i] = m;
}
for(int i=1; i<=n; i++){
int maxl=1, maxr=m;
//计算h和l
for(int j=1; j<=m; j++){
if(a[i][j]==0){
h[i][j] = 0;
l[i][j] = 1;
maxl = j+1;
}else{
l[i][j] = Math.max(l[i-1][j],maxl);
h[i][j] = h[i-1][j]+1;
}
}
//计算r
for(int j=m; j>=1; j--){
if(a[i][j]==0){
r[i][j] = m;
maxr = j-1;
}else{
r[i][j] = Math.min(r[i-1][j],maxr);
int temp=ans;
ans = Math.max(ans,(r[i][j]-l[i][j]+1)*h[i][j]);
if(temp!=ans){
y1=l[i][j];
x1=i-h[i][j]+1;
rwidth=r[i][j]-l[i][j]+1;
rheight=h[i][j];
// System.out.println("i: "+i+" j:"+j+" l:"+l[i][j]+" r:"+r[i][j]+" h:"+h[i][j]);
}
}
}
}
//由于在二值化时将所有的点横纵坐标都加了1,找到矩形起点时要减去1
x1=x1-1;
y1=y1-1;
System.out.println("x1: "+x1+" y1: "+y1+" "+rwidth+" height: "+rheight);
}
粘贴图片:
public static void stickImage(String bigImagePath,String smallImagePath,String outImagePath) {
try {
BufferedImage bigImage = ImageIO.read(new File(bigImagePath));
BufferedImage smallImage = ImageIO.read(new File(smallImagePath));
Graphics2D gh=bigImage.createGraphics();
gh.drawImage(smallImage,null,x1,y1);
gh.dispose();
ImageIO.write(bigImage,"jpg",new File(outImagePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
没有输出路劲就随机产生一个:
public static void stickImage(String bigImagePath,String smallImagePath) {
try {
BufferedImage bigImage = ImageIO.read(new File(bigImagePath));
BufferedImage smallImage = ImageIO.read(new File(smallImagePath));
Graphics2D gh=bigImage.createGraphics();
gh.drawImage(smallImage,null,x1,y1);
gh.dispose();
ImageIO.write(bigImage,"jpg",new File(Generate.randomOutPutPath()));
} catch (IOException ex) {
ex.printStackTrace();
}
}
全文程序:
package com.edupt;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class Calculate {
static int maxn = 2000;
static int n,m;
static int[][]a=new int[maxn][maxn]; //二值化结果
// static int[][]pixels;
static int[][]h=new int[maxn][maxn]; //高度
static int[][]l=new int[maxn][maxn];//向左能移动到的终点
static int[][]r=new int [maxn][maxn];//向右能移动到的终点
static int x1;
static int y1;
static int x2;
static int y2;
static int rwidth;
static int rheight;
static final int FZ=130;
public static void main(String[] args) {
String filePath="C:\Users\86130\Desktop\素材\2019-12-30-17-56.jpg";
String filePath2="C:\Users\86130\Desktop\素材\BigHead.jpg";
// String outPath="C:\Users\86130\Desktop\素材\result1.jpg";
binaryImage(filePath,7);
// removeNoise(0,3);
print();
// findMaxBlackRect();
//stickImage(filePath,filePath2);
}
//二值化图像
public static void binaryImage(String filePath,double threshold){
try {
BufferedImage image = ImageIO.read(new File(filePath));
int minX = 0;//图片起始点X
int minY = 0;//图片起始点Y
int width = image.getWidth();//图片的宽度
m = width;
int height = image.getHeight();//图片的高度
n = height;
//pixels=new int[height][width];
//将黑色区域化为1,其他为0
for (int i = minX; i < width; i++) {
for (int j = minY; j < height; j++) {
Object data = image.getRaster().getDataElements(i, j, null);//获取该点像素,并以object类型表示
int red = image.getColorModel().getRed(data);
int blue = image.getColorModel().getBlue(data);
int green = image.getColorModel().getGreen(data);
if(red==0&&green==0&&blue==0){
a[i+1 ][j+1 ] = 1;
}
}
}
}catch (IOException e) {
e.printStackTrace();
}
}
/**
*
* @param whiteThreshold 默认为0
* @param blackThreshold
*/
public static void removeNoise(int whiteThreshold,int blackThreshold){
int i,j,nValue,nCount,m,n;
int nWidth=Calculate.maxn;
int nHeight=Calculate.maxn;
//对图像上下边缘去噪
for(i=0;i<nWidth;i++){
a[i][0]=0;
a[i][nHeight-1]=0;
}
//对图像上下边缘去噪
for( i=0;i<nHeight;i++){
a[0][i]=0;
a[0][nWidth-1]=0;
}
//根据周围点的颜色去噪
//遍历所有的点 //j是y, i是x
for(i=1;i<nWidth-1;i++){
for( j=1;j<nHeight-1;j++){
nValue=a[i][j];
if(nValue==1&&whiteThreshold!=0){//如果一点是黑点
nCount=0;
//遍历他周围的八个点,如果他
for(m=j-1;m<=j+1;m++){
for(n=i-1;n<=i+1;n++){
if(a[n][m]==0){//周围白点的个数
nCount++;
}
}
}
if(nCount>=whiteThreshold){//周围白点的个数大于阈值则变为白色
a[i][j]=0;
}
}else{//如果一个点是白色的点,周围的点是黑色
nCount=0;
for(m=j-1;m<=j+1;m++){
for(n=i-1;n<=i+1;n++){
if(a[n][m]==1){
nCount++;
}
}
}
if(nCount>=blackThreshold){//周围黑点的个数大于阈值就变成黑色
a[i][j]=1;
}
}
}
}
}
//找到极大全为1的矩形矩阵
public static void findMaxBlackRect(){
int ans = 0;
//初始化
for(int i=1;i<=m;++i) {
l[0][i] = 1;
r[0][i] = m;
}
for(int i=1; i<=n; i++){
int maxl=1, maxr=m;
//计算h和l
for(int j=1; j<=m; j++){
if(a[i][j]==0){
h[i][j] = 0;
l[i][j] = 1;
maxl = j+1;
}else{
l[i][j] = Math.max(l[i-1][j],maxl);
h[i][j] = h[i-1][j]+1;
}
}
//计算r
for(int j=m; j>=1; j--){
if(a[i][j]==0){
r[i][j] = m;
maxr = j-1;
}else{
r[i][j] = Math.min(r[i-1][j],maxr);
int temp=ans;
ans = Math.max(ans,(r[i][j]-l[i][j]+1)*h[i][j]);
if(temp!=ans){
y1=l[i][j];
x1=i-h[i][j]+1;
rwidth=r[i][j]-l[i][j]+1;
rheight=h[i][j];
// System.out.println("i: "+i+" j:"+j+" l:"+l[i][j]+" r:"+r[i][j]+" h:"+h[i][j]);
}
}
}
}
//由于在二值化时将所有的点横纵坐标都加了1,找到矩形起点时要减去1
x1=x1-1;
y1=y1-1;
System.out.println("x1: "+x1+" y1: "+y1+" "+rwidth+" height: "+rheight);
}
//粘贴图片
public static void stickImage(String bigImagePath,String smallImagePath,String outImagePath) {
try {
BufferedImage bigImage = ImageIO.read(new File(bigImagePath));
BufferedImage smallImage = ImageIO.read(new File(smallImagePath));
Graphics2D gh=bigImage.createGraphics();
gh.drawImage(smallImage,null,x1,y1);
gh.dispose();
ImageIO.write(bigImage,"jpg",new File(outImagePath));
} catch (IOException ex) {
ex.printStackTrace();
}
}
//没有输出路劲就随机产生一个
public static void stickImage(String bigImagePath,String smallImagePath) {
try {
BufferedImage bigImage = ImageIO.read(new File(bigImagePath));
BufferedImage smallImage = ImageIO.read(new File(smallImagePath));
Graphics2D gh=bigImage.createGraphics();
gh.drawImage(smallImage,null,x1,y1);
gh.dispose();
ImageIO.write(bigImage,"jpg",new File(Generate.randomOutPutPath()));
} catch (IOException ex) {
ex.printStackTrace();
}
}
public static void print(){
String filePath="C:\Users\86130\Desktop\素材\2019-12-30-14-30.jpg";
try {
BufferedImage image=ImageIO.read(new File(filePath));
for(int i=501;i<=1000;i++){
for(int j=501;j<=1000;j++){
if(a[i][j]==0){
Object data = image.getRaster().getDataElements(i-1, j-1, null);//获取该点像素,并以object类型表示
int red = image.getColorModel().getRed(data);
int blue = image.getColorModel().getBlue(data);
int green = image.getColorModel().getGreen(data);
System.out.print("[ red "+red+" blue "+blue+" green "+green+" ]");
}
}
System.out.println();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}