【问题标题】:what happens with an strokeweight less than 1 pixel笔画重量小于 1 像素会发生什么
【发布时间】:2019-08-21 08:09:03
【问题描述】:

我正在将一个处理脚本重新输入 python,以便我可以在 gimp-python 脚本中使用它。 我遇到了 strokeWeight(0.3) 的问题,这意味着厚度小于 1 像素。

我遵循了将 rgb 颜色与 alpha 混合的建议,但没有奏效。 处理似乎正在做其他事情。 我试图在处理源代码中查找 strokeWeight 函数,但我无法真正理解它。 我还尝试将 strokeWeight 为 1 的颜色值与 strokeWeight 为 0.3 的颜色值进行比较,但我也无法理解。

有谁知道将 strokeWeight 设置为 0.3 时会进行什么处理,如何模拟?

我使用的脚本是:

String filename = "image2";
String fileext = ".jpg";
String foldername = "./";

// run, after 30 iterations result will be saved automatically
// or press SPACE

int max_display_size = 800; // viewing window size (regardless image size)

/////////////////////////////////////
int n=2000;
float [] cx=new float[n];
float [] cy=new float[n];

PImage img;
int len;

// working buffer
PGraphics buffer; 

String sessionid; 

void setup() {
  sessionid = hex((int)random(0xffff),4);
  img = loadImage(foldername+filename+fileext);

  buffer = createGraphics(img.width, img.height);
  buffer.beginDraw();
  //buffer.noFill();
  //buffer.smooth(8);
  //buffer.strokeWeight(1);
  buffer.strokeWeight(0.3);
  buffer.background(0);
  buffer.endDraw();

  size(300,300);

  len = (img.width<img.height?img.width:img.height)/6;

  background(0);
  for (int i=0; i<n; i++) {
    cx[i] = i; // changed to a none random number for testing 
    cy[i] = i; 
  //for (int i=0;i<n;i++) {
  //  cx[i]=random(img.width);
  //  cy[i]=random(img.height);
  }
}

int tick = 0;

void draw() {  
  buffer.beginDraw();
  for (int i=1;i<n;i++) {
    color c = img.get((int)cx[i], (int)cy[i]);
    buffer.stroke(c);
    buffer.point(cx[i], cy[i]);
    // you can choose channels: red(c), blue(c), green(c), hue(c), saturation(c) or brightness(c)
    cy[i]+=sin(map(hue(c),0,255,0,TWO_PI));
    cx[i]+=cos(map(hue(c),0,255,0,TWO_PI));
  }

  if (frameCount>len) {
    frameCount=0;
    println("iteration: " + tick++);
    //for (int i=0;i<n;i++) {
    //  cx[i]=random(img.width);
    //  cy[i]=random(img.height);
    for (int i=0; i<n; i++) {
      cx[i] = i; // changed to a none random number for testing 
      cy[i] = i;
    }

  }

  buffer.endDraw();
  if(tick == 30) keyPressed();

  image(buffer,0,0,width,height);
}

void keyPressed() {
  buffer.save(foldername + filename + "/res_" + sessionid + hex((int)random(0xffff),4)+"_"+filename+fileext);
  println("image saved");
}

以下是一些测试结果(按 200% 缩放): (需要注意的是,这是一条相当直线,用于比较。实际代码使用随机坐标。)

【问题讨论】:

  • Hacky 建议,但是否可以放大坐标,使 Gimp 中的 strokeWeight 等于 1 像素? (然后,您可以对绘图进行更锐利的双三次缩放,任何可能会覆盖绘图的高通滤波版本,以防止边缘过度模糊)。 Gimp 笔刷是否不允许亚像素厚度?
  • 您的建议很有趣,但并不能真正解决这个特定问题。我经常使用一个名为“绘图生成”的脚本,并希望能够在 gimp 中使用它。我试图使脚本尽可能与原始脚本相同。
  • 可能值得分享您尝试移植到 gimp-python 和源代码草图,希望对 api 更有经验的人可以指导您
  • 也许,应该提到的是,在计划制作 gimp-python 脚本时,我只有一个 python 脚本。它更容易测试,当一切正常时,很容易从中制作一个 gimp 脚本。我已经在 python 中复制了处理草图,当我用 strokeWeight(1) 测试它时给出了相同的结果。获得与处理草图完全相同的结果的第一步是将 strokeWeight 设置为 0.3,它应该是。所以我想我的问题不是关于 python(或 gimp),而是关于当 strokeWeight 设置为低于 1 像素时处理的问题。

标签: python processing gimp


【解决方案1】:

0.3 像素的描边粗细意味着一个像素的整个 3/10 将被着色 (*),因此,假设路径穿过中间的像素,像素被绘制为 3/10 的不透明度(可能对线性/感知进行一些调整)。

(*) 当然如果路径靠近像素的边缘,这可能会少一点,并且相邻的像素也会稍微着色。

【讨论】:

  • 是的,我已经尝试过了,但结果看起来不像处理正在做的事情。我已经检查了你在一些单色像素上所说的内容并围绕它们,但这也没有告诉我太多。一些像素,例如 255,0,0 甚至没有着色。这对我来说非常令人费解。也许它与其他像素有关,但我真的不知道如何找到。
  • 您能否向您添加问题:代码摘录、路径截图(具有足够的缩放)和示例图片?
  • 我已经添加了代码并添加了一些测试图像。从第 10 次迭代来看,不透明度显然是 1 像素以下笔画重量的重要组成部分,但从一次迭代中可以看出,还有其他事情发生。
  • Gimp 隐含在哪里?这不是 Gimp API。你提到的“处理”是processing软件吗?如果是这样,用processing 而不是gimp 标记您的问题会很有用。
  • 是的,我向 George Profenza 解释了这一点,他编辑了我的问题并将标签 python 和 gimp 放在那里。我不确定这在stackoverflow上是如何工作的。我应该再次删除标签吗?确实,我说我正在从处理脚本中制作一个 gimp-python 脚本,但我也认为我清楚地提到了我的问题是关于 strokeWeight 函数在处理中的工作。我想这还不够清楚,这些标签确实没有帮助。
【解决方案2】:

基于许多 cmets,目标似乎是使用基本算法对像素进行采样并合成新图像来生成 9000x9000 图像。

该算法似乎工作如下:

  1. 根据输入图像尺寸分配随机像素位置
  2. 在预分配像素位置绘制一个小(亚像素)点
  3. 根据sin()/cos() 函数的颜色通道 (H/S/B/R/G/B) 强度到角度映射来偏移曲线上的像素位置

现有的处理脚本可以很容易地适应输出 9000x9000 的图像。 在输入图像较小的情况下,可以重新映射坐标:

float remappedX = map(cx[i],0,input.width,0,output.width);
float remappedY = map(cy[i],0,input.height,0,output.height);

这是顶部发布的处理草图的修改版本:

String filename = "image2";
String fileext = ".jpg";
String foldername = "./";

// run, after 30 iterations result will be saved automatically
// or press SPACE

/////////////////////////////////////
// number of pixels to sample
int n = 2000;
float[] cx;
float[] cy;

PImage img;
int len;

// working buffer
PGraphics buffer;
// buffer size
int bufferWidth  = 9000;
int bufferHeight = 9000;

String sessionid; 

int tick = 0;

boolean imageAutoSaved = false;

void setup() {
  size(300,300);
  background(0);

  // load image, setup session
  sessionid = hex((int)random(0xffff),4);
  img = loadImage(foldername+filename+fileext);
  len = (img.width<img.height?img.width:img.height)/6;
  // sample each pixel
  n = img.pixels.length;
  cx = new float[n];
  cy = new float[n];
  resetPixelSampling();
  // setup buffer
  buffer = createGraphics(bufferWidth,bufferHeight);
  buffer.beginDraw();
  //buffer.noFill();
  //buffer.smooth(8);
  //buffer.strokeWeight(1);
  buffer.strokeWeight(0.3);
  buffer.background(0);
  buffer.endDraw();
}

void resetPixelSampling(){
  for (int i=0; i < n; i++) {
    //cx[i] = i; // changed to a none random number for testing 
    //cy[i] = i; 
    cx[i] = random(img.width);
    cy[i] = random(img.height);
  }
}

void updatePixelSampling(){
  buffer.beginDraw();
  for (int i = 1; i < n; i++) {
    color c = img.get((int)cx[i], (int)cy[i]);
    buffer.stroke(c);
    // remap/re-scale coordinates from sampled image to target buffer size
    float bufferX = map(cx[i],0,img.width,0,bufferWidth);
    float bufferY = map(cy[i],0,img.height,0,bufferHeight);
    buffer.point(bufferX,bufferY);
    // you can choose channels: red(c), blue(c), green(c), hue(c), saturation(c) or brightness(c)
    float channel = hue(c);
    cy[i]+=sin(map(channel,0,255,0,TWO_PI));
    cx[i]+=cos(map(channel,0,255,0,TWO_PI));
  }

  buffer.endDraw();
}

void draw() {
  if (frameCount>len) {
    frameCount=0;
    println("iteration: " + tick++);
    resetPixelSampling();
  }

  updatePixelSampling();

  if(tick == 30) {
    if(!imageAutoSaved){
      imageAutoSaved = true;
      saveImage();
    }
  }

  image(buffer,0,0,width,height);
}

void keyPressed() {
  saveImage();
}

void saveImage(){
  buffer.save(foldername + filename + "/res_" + sessionid + hex((int)random(0xffff),4)+"_"+filename+fileext);
  println("image saved");
}

脚本可以进一步优化。例如,采样(分析)和合成算法可以移植到GLSL fragment shader。请注意,9000x9000 是一张大图像,无法按原样在 GPU 上上传纹理,但是,可以将较小的缓冲区用作可以一次将一个部分渲染到大图像中的图块。

关于将处理代码移植到 python 中以便可以在 gimp-python 脚本中使用它:

  • Gimp 有一个“墨水”绘画工具,其大小可以小于一个像素。
  • 如果选中,Gimp Python API 允许设置此大小:pdb.gimp_context_set_ink_size(0.3)
  • Gimp Python API 也有绘画功能

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-03
    • 1970-01-01
    • 2020-05-12
    • 2021-11-06
    • 1970-01-01
    • 2020-08-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多