关于如何使图像的背景颜色透明有很多问题,但是所有的答案似乎都使用RgbImageFilter
来使特定颜色的每一次出现都是透明的。
我的问题是,如何在Java中实现这种“背景删除”,以便从固定点(根据Paint中的“bucket”操作或RMagick函数Image#matte_floodfill)泛滥透明度?
答案 0 :(得分:1)
与互联网的方式一样,经过一些搜索后,我试图找到一些类似的代码。
这是我的共同解决方案。它并不完美,但它可能是其他人试图去做的起点。
这可以通过选择图像的四个角,对它们求平均值并将其用作锚色来实现。我最初使用Pixel类看起来很方便,结果浪费了我的时间!哈。就是这样。
public class Pixel implements Comparable{
int x,y;
public Pixel(int x, int y){
this.x = x;
this.y = y;
}
@Override
public int compareTo(Object arg0) {
Pixel p = (Pixel) arg0;
if(p.x == x && p.y == y)
return 0;
return -1;
}
}
这就是牛肉:
public class ImageGrab {
private static int pixelSimilarityLimit = 20;
public static void main(String[] args){
BufferedImage image = null;
try {
URL url = new URL("http://animal-photography.com/thumbs/russian_blue_cat_side_view_on_~AP-0PR4DL-TH.jpg");
image = ImageIO.read(url);
} catch (IOException e) {
e.printStackTrace();
}
Color[] corners = new Color[]{new Color(image.getRGB(0, 0)),
new Color(image.getRGB(image.getWidth()-1, 0)),
new Color(image.getRGB(0, image.getHeight()-1)),
new Color(image.getRGB(image.getWidth()-1, image.getHeight()-1))};
int avr = 0, avb=0, avg=0, ava=0;
for(Color c : corners){
avr += c.getRed();
avb += c.getBlue();
avg += c.getGreen();
ava += c.getAlpha();
}
System.out.println(avr/4+","+avg/4+","+avb/4+","+ava/4);
for(Color c : corners){
if(Math.abs(c.getRed() - avr/4) < pixelSimilarityLimit &&
Math.abs(c.getBlue() - avb/4) < pixelSimilarityLimit &&
Math.abs(c.getGreen() - avg/4) < pixelSimilarityLimit &&
Math.abs(c.getAlpha() - ava/4) < pixelSimilarityLimit){
}
else{
return;
}
}
Color master = new Color(avr/4, avg/4, avb/4, ava/4);
System.out.println("Image sufficiently bordered.");
LinkedList<Pixel> open = new LinkedList<Pixel>();
LinkedList<Pixel> closed = new LinkedList<Pixel>();
open.add(new Pixel(0,0));
open.add(new Pixel(0,image.getHeight()-1));
open.add(new Pixel(image.getWidth()-1,0));
open.add(new Pixel(image.getWidth()-1,image.getHeight()-1));
while(open.size() > 0){
Pixel p = open.removeFirst();
closed.add(p);
for(int i=-1; i<2; i++){
for(int j=-1; j<2; j++){
if(i == 0 && j == 0)
continue;
if(p.x+i < 0 || p.x+i >= image.getWidth() || p.y+j < 0 || p.y+j >= image.getHeight())
continue;
Pixel thisPoint = new Pixel(p.x+i, p.y+j); boolean add = true;
for(Pixel pp : open)
if(thisPoint.x == pp.x && thisPoint.y == pp.y)
add = false;
for(Pixel pp : closed)
if(thisPoint.x == pp.x && thisPoint.y == pp.y)
add = false;
if(add && areSimilar(master,new Color(image.getRGB(p.x+i, p.y+j)))){
open.add(thisPoint);
}
}
}
}
for(Pixel p : closed){
Color c = new Color(image.getRGB(p.x, p.y));
Color newC = new Color(0, 0, 0, 0);
image.setRGB(p.x, p.y, newC.getRGB());
}
try {
File outputfile = new File("C:/Users/Mike/Desktop/saved.png");
ImageIO.write(image, "png", outputfile);
} catch (IOException e) {
}
}
public static boolean areSimilar(Color c, Color d){
if(Math.abs(c.getRed() - d.getRed()) < pixelSimilarityLimit &&
Math.abs(c.getBlue() - d.getBlue()) < pixelSimilarityLimit &&
Math.abs(c.getGreen() - d.getGreen()) < pixelSimilarityLimit &&
Math.abs(c.getAlpha() - d.getAlpha()) < pixelSimilarityLimit){
return true;
}
else{
return false;
}
}
}
如果有人担心,请考虑这个公共领域。干杯!希望它有所帮助。
答案 1 :(得分:0)
我目前正在使用的一个令人不满意的解决方案是简单地预测你要放置透明图像的背景颜色(正如你通常会这样做的)并使用如所描述的RgbImageFilter
的解决方案here
如果有人想要发布一个满意的解决方案,请做 - 直到那时,我会接受这个,因为它有效。
答案 2 :(得分:0)
这是我刚刚放在一起从BufferedImage中删除背景的东西。这很简单,但可能有更有效的方法。
我设置了三个输入(源图像,允许的公差,以及要替换背景的颜色)。它只返回一个缓冲的图像,并对其进行了更改。
它找到每个角落附近的颜色并将它们平均以创建参考颜色,然后它替换在参考的公差范围内的每个像素。
为了使背景透明,您需要传入
BufferedImage RemoveBackground(BufferedImage src, float tol, int color)
{
BufferedImage dest = src;
int h = dest.getHeight();
int w = dest.getWidth();
int refCol = -(dest.getRGB(2,2)+dest.getRGB(w-2,2)+dest.getRGB(2,h-2)+dest.getRGB(w-2,h-2))/4;
int Col = 0;
int x = 1;
int y = 1;
int upperBound = (int)(refCol*(1+tol));
int lowerBound = (int)(refCol*(1-tol));
while (x < w)
{
y = 1;
while (y < h)
{
Col = -dest.getRGB(x,y);
if (Col > lowerBound && Col < upperBound)
{
dest.setRGB(x,y,color);
}
y++;
}
x++;
}
return dest;
}
我知道这是一个旧帖子,但希望这对某人来说会派上用场。
编辑:我刚刚意识到这不适用于透明胶片,只是用另一个RGB值替换RGB值。这需要一点适应来做ARGB值。