效果:
需要做一个照片替换底色的,所以第一部先把轮廓的坐标找出来,先看一下效果:边缘我用绿色的线条描述了一下:
下一步把人像抠出来,代码内容如下:
package com.zjxf.test;
import com.google.common.collect.Lists;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
import java.util.stream.IntStream;
/**
* created with IntelliJ IDEA
*
* @author: create by limu
* Date: 2020/11/19
* Time:14:20
*/
public class PictureHandleUtils {
public static final Integer intensityValue = 30; //处理图片的阀值
public static final Map<Integer, List<Integer>> xLineMap = new HashMap<>(); //轮廓线坐标
public static final Map<Integer, List<Integer>> yLineMap = new HashMap<>(); //轮廓线坐标
private static int width = 0;
private static int height = 0;
private static int minX = 0;
private static int minY = 0;
public static void main(String[] args) throws Exception {
drawOutlinePic(calculationPic("C:\\Users\\18701\\Desktop\\img\\2.jpg"));
}
/**
* 计算图片轮廓
*
* @param filePath 文件地址
* @throws IOException 文件流操作异常
*/
public static BufferedImage calculationPic(String filePath) throws IOException {
File imageFile = new File(filePath);
BufferedImage bufferedImage = ImageIO.read(imageFile);
width = bufferedImage.getWidth();
height = bufferedImage.getHeight();
minX = bufferedImage.getMinX();
minY = bufferedImage.getMinY();
IntStream.range(minX, width).boxed().forEach(item -> IntStream.range(minY, height).boxed().forEach(itemJ -> {
int rgb = bufferedImage.getRGB(item, itemJ);
if (item != minX && itemJ != minY && item != width - 1 && itemJ != height - 1) {
int rgb1 = bufferedImage.getRGB(item + 1, itemJ);
int rgb2 = bufferedImage.getRGB(item - 1, itemJ);
int rgb3 = bufferedImage.getRGB(item, itemJ + 1);
int rgb4 = bufferedImage.getRGB(item, itemJ - 1);
if (comparePixel(rgb, rgb1, rgb2, rgb3, rgb4)) {
addXLineMap(item, itemJ);
addYLineMap(itemJ, item);
}
} else {
int rgb1 = 0;
int rgb2 = 0;
int rgb3 = 0;
if (item == 0 && itemJ != 0 && itemJ != height - 1) {
rgb1 = bufferedImage.getRGB(item + 1, itemJ);
rgb2 = bufferedImage.getRGB(item, itemJ + 1);
rgb3 = bufferedImage.getRGB(item, itemJ - 1);
}
if (item == width - 1 && itemJ != 0 && itemJ != height - 1) {
rgb1 = bufferedImage.getRGB(item - 1, itemJ);
rgb2 = bufferedImage.getRGB(item, itemJ + 1);
rgb3 = bufferedImage.getRGB(item, itemJ - 1);
}
if (itemJ == 0 && item != 0 && item != width - 1) {
rgb1 = bufferedImage.getRGB(item + 1, itemJ);
rgb2 = bufferedImage.getRGB(item - 1, itemJ);
rgb3 = bufferedImage.getRGB(item, itemJ + 1);
}
if (itemJ == height - 1 && item != 0 && item != width - 1) {
rgb1 = bufferedImage.getRGB(item + 1, itemJ);
rgb2 = bufferedImage.getRGB(item - 1, itemJ);
rgb3 = bufferedImage.getRGB(item, itemJ - 1);
}
if (rgb1 != 0 && rgb2 != 0 && rgb3 != 0) {
if (comparePixel(rgb, rgb1, rgb2, rgb3)) {
addXLineMap(item, itemJ);
addYLineMap(itemJ, item);
}
}
}
}));
List<Integer> lineList = xLineMap.getOrDefault(0, Lists.newArrayList());
List<Integer> yList = xLineMap.getOrDefault(width - 1, Lists.newArrayList());
IntStream.range(Collections.min(yList), height - 1).boxed().forEach(item -> addXLineMap(width - 1, item));
if (lineList.size() != 0) {
IntStream.range(Collections.min(lineList), height - 1).boxed().forEach(itemJ -> addYLineMap(0, itemJ));
lineList.forEach(item -> addYLineMap(item, 0));
}
return bufferedImage;
}
/**
* 绘制轮廓
*/
public static void drawOutlinePic(BufferedImage bufferedImage) throws Exception {
//预处理图片边框
xLineMap.forEach((key, value) -> value.forEach(itemJ -> {
List<Integer> defaultList = yLineMap.getOrDefault(itemJ, Lists.newArrayList());
if (defaultList.size() != 0) {
Integer max = Collections.max(defaultList);
Integer min = Collections.min(defaultList);
bufferedImage.setRGB(key, itemJ, 65281);
}
}));
calculationContinuousPoint(bufferedImage);
buildFile(bufferedImage, "zh.jpg");
}
/**
* 添加到间隔线map里面
*
* @param item x
* @param itemJ y
*/
public static void addXLineMap(int item, int itemJ) {
List<Integer> itemJList = xLineMap.getOrDefault(item, Lists.newArrayList());
itemJList.add(itemJ);
xLineMap.put(item, itemJList);
}
/**
* 添加到间隔线map里面
*
* @param item x
* @param itemJ y
*/
public static void addYLineMap(int itemJ, int item) {
List<Integer> itemList = yLineMap.getOrDefault(itemJ, Lists.newArrayList());
itemList.add(item);
yLineMap.put(itemJ, itemList);
}
/**
* 构建生成文件
*
* @param bufferedImage 文件包装信息
* @param fileName 文件名称
* @throws IOException 文件流处理异常信息
*/
public static void buildFile(BufferedImage bufferedImage, String fileName) throws IOException {
FileOutputStream ops = new FileOutputStream(new File("C:\\Users\\18701\\Desktop\\img\\new_1_" + fileName));
ImageIO.write(bufferedImage, "jpg", ops);
ops.flush();
ops.close();
}
/**
* 计算阻像素点连续性
* 解释:
* 1:假设 如果像素坐标出现无法连续的情况,自动补全轮廓的连续点坐标;
*
* @param bufferedImage 像素点连续
* @throws Exception 流信息异常
*/
public static void calculationContinuousPoint(BufferedImage bufferedImage) throws Exception {
//从坐标x为0开始
Integer integer = xLineMap.getOrDefault(0, Lists.newArrayList()).stream().findFirst().orElse(0);
IntStream.range(minX, width).boxed().forEach(item -> IntStream.range(minY, height).boxed().forEach(itemJ -> {
Boolean aBoolean = CoordinateUtils.upperPixed(xLineMap, item, itemJ);
if (!aBoolean){
System.out.println("有节点");
}else{
System.out.println("无节点");
}
}));
}
/**
* 判断色值是否超过设定的阀值
*
* @param rgb 目标色值
* @param rgbArr 相邻色值数组
* @return boolean
*/
public static boolean comparePixel(int rgb, int... rgbArr) {
int[] rgbArray = handleColorValue(rgb);
return Arrays.stream(rgbArr).mapToObj(PictureHandleUtils::handleColorValue).anyMatch(item -> compareArr(rgbArray, item));
}
/**
* 比较rgb的值得大小
*
* @param aArr 目标色值a
* @param bArr 目标色值b
* @return boolean
*/
public static boolean compareArr(int[] aArr, int[] bArr) {
int i1 = aArr[0];
int i2 = aArr[1];
int i3 = aArr[2];
int j1 = bArr[0];
int j2 = bArr[1];
int j3 = bArr[2];
return i1 - j1 >= intensityValue || i2 - j2 >= intensityValue || i3 - j3 >= intensityValue;
}
/**
* 处理颜色色值
*
* @param rgb 颜色十进制
* @return int[]
*/
public static int[] handleColorValue(int rgb) {
int[] rgbValue = new int[3];
rgbValue[0] = (rgb & 0xff0000) >> 16;
rgbValue[1] = (rgb & 0xff00) >> 8;
rgbValue[2] = (rgb & 0xff);
return rgbValue;
}
}
下一步打算把人头像给抠出来,下一篇明天写。