微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在图像上查找透明像素并使相同像素在另一个图像上透明

如何解决在图像上查找透明像素并使相同像素在另一个图像上透明

我有两张图片。第二个是对第一个应用某种掩码的结果。我需要的是获得该蒙版并能够将其应用于其他图像。 这是两张图片normaltattered

如您所见,第二个有破烂的边缘,它们是透明的,而不是白色的。 (也有某种模糊,如果有一种方法我可以找出它到底是什么模糊,那就太好了,但这里并不是真的需要)

我需要的是能够从第一个图像创建第二个图像。

理论上,我应该创建一个遮罩 - 任何颜色的相同大小的图像,每个像素的透明度为 0 或 255,具体取决于上面第二张图像中相同像素的透明度值。然后我可以将任何输入图像的像素的 alpha 设置为来自此掩码的 alpha 值。

但是,我不知道如何实际操作。我使用 BufferedImage 在 java 中尝试过它,但是,它不起作用。当我尝试从所选像素的颜色中获取 Alpha 时,它始终为 255,即使对于应该是透明的像素也是如此。我确实设法在 Processing 中获得了 alpha 值(它们实际上不仅仅是 0 或 255,中间还有很多值),但是,当我尝试将此值应用于新图像并保存时,它会保存为完全不透明的图像,当我加载它,alpha 值都是 255。

  PImage mask = loadImage("some/path/1.png");
  PImage img = loadImage("some/path/2.png");

  img.loadPixels();
  for (int x = 0; x < img.width; x++) {
    for (int y = 0; y < img.height; y++) {
      color maskColor = mask.get(x,y);
      if (alpha(maskColor) < 255) {
        color imgColor = img.get(x,y);
        img.pixels[y*img.width + x] = color(red(imgColor),green(imgColor),blue(imgColor),alpha(maskColor));
      }
    }
  }
  img.updatePixels();
  img.save("some/path/3.png"); 

解决方法

您可以尝试获取原始图像和破烂图像的 Alpha 通道之间的差异。

PImage tattered = loadImage("some/path/1.png");
PImage img = loadImage("some/path/2.png");
PImage mask = image.copy();

img.loadPixels();

for (int x = 0; x < img.width; x++) {
   for (int y = 0; y < img.height; y++) {
      mask[x][y] = abs(alpha(img.get(x,y)) - alpha(tattered.get(x,y)));
    }
}

mask.updatePixels();
mask.save("some/path/3.png"); 

,

您也可以将破烂的图像用作其他图像的蒙版。您只需要蒙版中的 alpha 信息。
使用BufferedImage创建破碎边框的实现:

public class Test {

    public static void main(String[] args) throws IOException {
        
         BufferedImage mask = loadImage("e:\\mask.png");
         BufferedImage img = loadImage("e:\\1.png");

         int width = mask.getWidth();
         int height = mask.getHeight();
         
         BufferedImage processed = new BufferedImage(width,height,BufferedImage.TYPE_4BYTE_ABGR);

          for (int x = 0; x < width; x++) {
            for (int y = 0; y < height; y++) {
              int rgb = mask.getRGB(x,y);
              int maskAlpha = alpha(rgb);
              int imgColor = img.getRGB(x,y);
              if (maskAlpha < 255) {
                processed.setRGB(x,y,maskAlpha(imgColor,maskAlpha));
              } else {
                  processed.setRGB(x,imgColor);
              }
            }
          }
         
          writeImage(processed,"e:\\2.png");
    }

    static BufferedImage loadImage(String imagePath) {
        File file = new File(imagePath);
        BufferedImage image = null;
        try {
            image = ImageIO.read(file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return image;
    }
    
    static void writeImage(BufferedImage img,String filePath){
        String format = filePath.substring(filePath.indexOf('.')+1);
        //Get Picture Format
        System.out.println(format);
        try {
            ImageIO.write(img,format,new File(filePath));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    static int maskAlpha(int rgb,int alpha) {
        //strip alpha from original color
        int color = (0x00ffffff&rgb);
        return color + ((alpha)<<24);
    }
    
    static int alpha(int rgb) {
        return (0xff&(rgb>>24));
    }
    
    static int red(int rgb) {
        return (0xff&rgb);
    }
    static int green(int rgb) {
        return (0xff&(rgb>>8));
    }
    static int blue(int rgb) {
        return (0xff&(rgb>>16));
    }
}

这里的 BufferedImage.TYPE_4BYTE_ABGR 表示

用颜色表示具有 8 位 RGBA 颜色分量的图像 蓝色、绿色和红色存储在 3 个字节和 1 个字节的 alpha 中。图片 有一个带 alpha 的 ComponentColorModel。该图像中的颜色数据是 认为不与 alpha 预乘。字节数据是 以 A、B、G、R 的顺序从低位交错排列在一个单字节数组中 到每个像素内的更高字节地址。

表示color integer是32位,在java中按照abgr的顺序存储,即alpha是前8位,r是后8位,所以可以得到argb值如下:

int r = (0xff&rgb);
int g = (0xff&(rgb>>8));
int b = (0xff&(rgb>>16));
int a = (0xff&(rgb>>24));
,

使用 odoo.define('web_widget_many2one_template',function (require) { "use strict"; var FieldMany2One = require('web.relational_fields').FieldMany2One; var fieldRegistry = require('web.field_registry'); var core = require('web.core'); var qweb = core.qweb; var rpc = require('web.rpc'); var webWidgetMany2oneTemplate = FieldMany2One.extend({ className: 'o_m2o_template',supportedFieldTypes: ['many2one'],init: function () { this._super.apply(this,arguments); this.rpc_rec = false; this.readOnlyTemplate = 'many2one_template'; if ( this.nodeOptions.hasOwnProperty('template') && this.nodeOptions.template ) { this.readOnlyTemplate = this.nodeOptions.template; } },willStart: function () { var self = this; var self_qweb = qweb; var def; if ( this.hasOwnProperty('value') && this.value.hasOwnProperty('data') && this.value.data.hasOwnProperty('id')) { var def = this._rpc({ model: this.value.model,method: 'search_read',domain: [ ['id','=',this.value.data.id] ],}).then(function (result) { if (Array.isArray(result) && result.length > 0) { self.rpc_rec = result[0]; } }); } return $.when(this._super.apply(this,arguments),def); },_renderReadonly: function () { var _this = this; var self_qweb = qweb; return $.when(this._super.apply(this,arguments)).then(function () { var template_render = self_qweb.render(_this.readOnlyTemplate,{record: _this.rpc_rec}); _this.$el.empty().css({"width":"100%"}).append(template_render); }); },}); fieldRegistry.add('m2o_template',webWidgetMany2oneTemplate); return webWidgetMany2oneTemplate; }); BufferedImageGraphics2D,您可以像这样构图:

AlphaComposite

PS:如果您知道源图像(上面代码中的BufferedImage image = ImageIO.read(new File("image.png")); BufferedImage mask = ImageIO.read(new File("mask.png")); BufferedImage composed = new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_INT_ARGB); Graphics2D g = composed.createGraphics(); try { g.setComposite(AlphaComposite.Src); // Possibly faster than SrcOver g.drawImage(image,null); // Clear out the transparent parts from mask g.setComposite(AlphaComposite.DstIn); g.drawImage(mask,null); } finally { g.dispose(); } if (!ImageIO.write(composed,"PNG",new File("composed.png"))) { throw new IIOException("Could not write image using PNG format: " + composed); } )包含透明度并且之后不需要原始图像,则可以直接在其上合成蒙版。这会更快并使用更少的内存,因为您跳过了内存分配和额外的组合。

,

我不确定为什么在您的特定示例中需要进行检查。如果目标图像不使用 Alpha 通道(它都是不透明的),您可以简单地使用源图像的 Alpha 通道覆盖数据。

顺便说一下,如果您使用的是 pixels[],则应该使用一个循环:

PImage withAlpha;
PImage noAlpha;

void setup(){
  size(120,130);
  background(0);
  
  withAlpha = loadImage("8fXFk.png");
  noAlpha = loadImage("AOsi0.png");
  
  copyAlphaChannel(withAlpha,noAlpha);
}

void draw(){
  background(map(sin(frameCount * 0.1),-1.0,1.0,192),0);
  image(withAlpha,0);
  image(noAlpha,60,0);
}

void copyAlphaChannel(PImage src,PImage dst){
  // quick error check
  if(src.width != dst.width || src.height != dst.height){
    println(String.format("error,mismatching dimensions src(%d,%d) != dst(%d,%d)",src.width,src.height,dst.width,dst.height));
    return;
  }
  // load pixel data
  src.loadPixels();
  dst.loadPixels();
  
  int numPixels = src.pixels.length;
  // for each pixel
  for(int i = 0 ; i < numPixels; i++){
      // extract source alpha
      int srcAlpha = (src.pixels[i] >> 24) & 0xFF;
      // apply it to the destination image
      //              src alpha      |   dst RGB
      dst.pixels[i] = srcAlpha << 24 | (dst.pixels[i] & 0xFFFFFF);
  }
  
  dst.updatePixels();
}

two images that appear identical: the second one has it's alpha channel (tattered edges) copied from the first

更新感谢您指出这一点:我错过了这个细节。

PImage 可以有三种格式:

  • RGB (=1)
  • ARGB (=2)
  • ALPHA(=4)

RGB 格式 PImage 转换为 ARGB 格式的一种解决方法是应用不透明的 mask()

PImage withAlpha;
PImage noAlpha;

void setup(){
  size(120,130);
  background(0);
  
  withAlpha = loadImage("8fXFk.png");
  noAlpha = loadImage("AOsi0.png");
  
  println("before",withAlpha.format,noAlpha.format,ARGB,RGB); // notice noAlpha's format is RGB
  
  forceAlphaChannel(noAlpha);
  
  println("after",RGB); // notice noAlpha's format is ARGB
  
  copyAlphaChannel(withAlpha,noAlpha);
  
  noAlpha.save("test.png");
  
}

void draw(){
  background(map(sin(frameCount * 0.1),0);
}

void forceAlphaChannel(PImage src){
  // make an opaque mask
  PImage mask = createImage(src.width,ALPHA);
  java.util.Arrays.fill(mask.pixels,color(255));
  mask.updatePixels();
  // apply the mask force the RGB image into ARGB format
  src.mask(mask);
}

void copyAlphaChannel(PImage src,dst.height));
    return;
  }
  // load pixel data
  src.loadPixels();
  dst.loadPixels();
  
  int numPixels = src.pixels.length;
  // for each pixel
  for(int i = 0 ; i < numPixels; i++){
      // extract source alpha
      int srcAlpha = (src.pixels[i] >> 24) & 0xFF;
      // apply it to the destination image
      //              src alpha      |   dst RGB
      dst.pixels[i] = srcAlpha << 24 | (dst.pixels[i] & 0xFFFFFF);
  }
  
  dst.updatePixels();
}

由于上面循环遍历像素很多次(一次创建蒙版,然后再次应用它),首先创建一个 ARGB PImage 可能更有效,然后复制RGB 数据来自一个 PImageALPHA 来自另一个:

PImage withAlpha;
PImage noAlpha;

void setup(){
  size(120,RGB); // notice noAlpha's format is RGB
  
  noAlpha = getAlphaChannelCopy(withAlpha,noAlpha);
  
  println("after",RGB); // notice noAlpha's format is ARGB
  
  noAlpha.save("test.png");
  
}

void draw(){
  background(map(sin(frameCount * 0.1),0);
}

// copy src alpha and dst rgb into new ARGB PImage
PImage getAlphaChannelCopy(PImage src,dst.height));
    return null;
  }
  PImage out = createImage(src.width,ARGB);
  // load pixel data
  src.loadPixels();
  dst.loadPixels();
  out.loadPixels();
  
  int numPixels = src.pixels.length;
  // for each pixel
  for(int i = 0 ; i < numPixels; i++){
      // extract source alpha
      int srcAlpha = (src.pixels[i] >> 24) & 0xFF;
      // apply it to the destination image
      //              src alpha      |   dst RGB
      out.pixels[i] = srcAlpha << 24 | (dst.pixels[i] & 0xFFFFFF);
  }
  
  out.updatePixels();
  
  return out;
}

(这里唯一的小缺点是您需要 loadPixels() 三次:每张图片一次。)

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。