Skip to main content
WMETS
Participating Frequently
August 5, 2024
Answered

Selection of All White Pixel Clusters >1x800 and Fill in New Channel

  • August 5, 2024
  • 3 replies
  • 4414 views

I have a TIFF file with dimensions 18320x24440 pixels and a resolution of 508 ppi. The image consists only of black and white pixels, with no gray tones. I would like to create a script that does the following:

  1. Select all the white areas with a width or height greater than 1x800 pixels.
  2. Combine these areas into one selection.
  3. Fill a layer or channel within this selection with black.

Can someone help me with this?

Correct answer Davide_Barranca12040269

Hi, 

Save the following in a .psjs file and File > Scripts > Browse...

It should output a selection of the clusters you need. Caveat: it doesn't work on Bitmap, please convert the image to Grayscale first.

 

const photoshop = require("photoshop");
const { core, imaging, app } = photoshop;

async function getDocumentPixelData(documentID) {
  const document = app.activeDocument;
  const width = document.width;
  const height = document.height;

  try {
    // Get the pixel data of the document
    const imageObj = await imaging.getPixels({
      documentID: document.id,
      colorSpace: "Grayscale",
    });
    return imageObj.imageData;
  } catch (error) {
    console.error("Error getting document pixel data:", error);
    throw error;
  }
}

async function findWhiteSegments(imageData, width, height, minLength) {
  try {
    const pixelData = await imageData.getData();
    const whiteSegments = [];
    const components = imageData.components;

    // Scan columns
    for (let x = 0; x < width; x++) {
      let whiteLength = 0;
      let startY = -1;
      for (let y = 0; y < height; y++) {
        const index = (y * width + x) * components;
        const gray = pixelData[index];

        if (gray === 255) {
          if (whiteLength === 0) startY = y;
          whiteLength++;
        } else {
          if (whiteLength >= minLength) {
            whiteSegments.push({ x, startY, length: whiteLength });
          }
          whiteLength = 0;
          startY = -1;
        }
      }
      if (whiteLength >= minLength) {
        whiteSegments.push({ x, startY, length: whiteLength });
      }
    }

    return whiteSegments;
  } catch (error) {
    console.error("Error finding white segments:", error);
    throw error;
  }
}

async function createSelectionFromSegments(segments, width, height) {
  try {
    const selectionData = new Uint8Array(width * height).fill(0);

    segments.forEach((segment) => {
      for (let i = 0; i < segment.length; i++) {
        selectionData[(segment.startY + i) * width + segment.x] = 255;
      }
    });

    const selectionBuffer = {
      typedArray: selectionData,
      width,
      height,
      components: 1,
      colorSpace: "Grayscale",
      colorProfile: "Gray Gamma 2.2",
    };

    return selectionBuffer;
  } catch (error) {
    console.error("Error creating selection from segments:", error);
    throw error;
  }
}

async function putSelectionFromBuffer(documentID, selectionBuffer) {
  try {
    console.log("Creating image data from buffer...");
    const imageData = await imaging.createImageDataFromBuffer(
      selectionBuffer.typedArray,
      {
        width: selectionBuffer.width,
        height: selectionBuffer.height,
        components: selectionBuffer.components,
        colorSpace: selectionBuffer.colorSpace,
        // colorProfile: selectionBuffer.colorProfile,
        chunky: true,
      }
    );

    console.log("Putting selection...");
    try {
      await imaging.putSelection({
        imageData,
      });
    } catch (error) {
      console.error("Error putting selection:", error);
      throw error;
    }

    console.log("Selection added successfully.");
  } catch (error) {
    console.error("Error putting selection from buffer:", error);
    throw error;
  }
}

async function main() {
  try {
    await core.executeAsModal(
      async () => {
        const documentID = app.activeDocument.id;
        const imageData = await getDocumentPixelData(documentID);
        const width = imageData.width;
        const height = imageData.height;
        const minLength = 800;

        const whiteSegments = await findWhiteSegments(
          imageData,
          width,
          height,
          minLength
        );
        const selectionBuffer = await createSelectionFromSegments(
          whiteSegments,
          width,
          height
        );

        await putSelectionFromBuffer(documentID, selectionBuffer);
      },
      { commandName: "Get Document Pixels" }
    );
  } catch (error) {
    console.error("Error in main function:", error);
  }
}

main().catch(console.error);

 

It should give you a good starting point.

Best,

 

 

3 replies

Davide_Barranca12040269
Legend
August 8, 2024

Hi, 

Save the following in a .psjs file and File > Scripts > Browse...

It should output a selection of the clusters you need. Caveat: it doesn't work on Bitmap, please convert the image to Grayscale first.

 

const photoshop = require("photoshop");
const { core, imaging, app } = photoshop;

async function getDocumentPixelData(documentID) {
  const document = app.activeDocument;
  const width = document.width;
  const height = document.height;

  try {
    // Get the pixel data of the document
    const imageObj = await imaging.getPixels({
      documentID: document.id,
      colorSpace: "Grayscale",
    });
    return imageObj.imageData;
  } catch (error) {
    console.error("Error getting document pixel data:", error);
    throw error;
  }
}

async function findWhiteSegments(imageData, width, height, minLength) {
  try {
    const pixelData = await imageData.getData();
    const whiteSegments = [];
    const components = imageData.components;

    // Scan columns
    for (let x = 0; x < width; x++) {
      let whiteLength = 0;
      let startY = -1;
      for (let y = 0; y < height; y++) {
        const index = (y * width + x) * components;
        const gray = pixelData[index];

        if (gray === 255) {
          if (whiteLength === 0) startY = y;
          whiteLength++;
        } else {
          if (whiteLength >= minLength) {
            whiteSegments.push({ x, startY, length: whiteLength });
          }
          whiteLength = 0;
          startY = -1;
        }
      }
      if (whiteLength >= minLength) {
        whiteSegments.push({ x, startY, length: whiteLength });
      }
    }

    return whiteSegments;
  } catch (error) {
    console.error("Error finding white segments:", error);
    throw error;
  }
}

async function createSelectionFromSegments(segments, width, height) {
  try {
    const selectionData = new Uint8Array(width * height).fill(0);

    segments.forEach((segment) => {
      for (let i = 0; i < segment.length; i++) {
        selectionData[(segment.startY + i) * width + segment.x] = 255;
      }
    });

    const selectionBuffer = {
      typedArray: selectionData,
      width,
      height,
      components: 1,
      colorSpace: "Grayscale",
      colorProfile: "Gray Gamma 2.2",
    };

    return selectionBuffer;
  } catch (error) {
    console.error("Error creating selection from segments:", error);
    throw error;
  }
}

async function putSelectionFromBuffer(documentID, selectionBuffer) {
  try {
    console.log("Creating image data from buffer...");
    const imageData = await imaging.createImageDataFromBuffer(
      selectionBuffer.typedArray,
      {
        width: selectionBuffer.width,
        height: selectionBuffer.height,
        components: selectionBuffer.components,
        colorSpace: selectionBuffer.colorSpace,
        // colorProfile: selectionBuffer.colorProfile,
        chunky: true,
      }
    );

    console.log("Putting selection...");
    try {
      await imaging.putSelection({
        imageData,
      });
    } catch (error) {
      console.error("Error putting selection:", error);
      throw error;
    }

    console.log("Selection added successfully.");
  } catch (error) {
    console.error("Error putting selection from buffer:", error);
    throw error;
  }
}

async function main() {
  try {
    await core.executeAsModal(
      async () => {
        const documentID = app.activeDocument.id;
        const imageData = await getDocumentPixelData(documentID);
        const width = imageData.width;
        const height = imageData.height;
        const minLength = 800;

        const whiteSegments = await findWhiteSegments(
          imageData,
          width,
          height,
          minLength
        );
        const selectionBuffer = await createSelectionFromSegments(
          whiteSegments,
          width,
          height
        );

        await putSelectionFromBuffer(documentID, selectionBuffer);
      },
      { commandName: "Get Document Pixels" }
    );
  } catch (error) {
    console.error("Error in main function:", error);
  }
}

main().catch(console.error);

 

It should give you a good starting point.

Best,

 

 

Davide Barranca - PS developer and authorwww.ps-scripting.com
WMETS
WMETSAuthor
Participating Frequently
August 8, 2024

It would be very helpfull if the script starts with dialog screen where it horizontal or vertical, black or white, amount of min or amount of max pixels. It`'s only possible to have one answer per question. Something like attached 

Legend
August 5, 2024

You might look at ImageJ, it has a lot of plugins and scripts and probably someone has a way to accomplish this. Post in their forums for help.

 

imagej.net

Sef McCullough
Community Expert
Community Expert
August 6, 2024

For instance, this?

https://imagej.net/ij/plugins/line-analyzer.html

 

That's really cool, first I've hear of ImageJ. Thanks @Lumigraphics 

Legend
August 7, 2024

Wayne Rasband is the original developer of what was then NIHImage and is now ImageJ. Definitely a great tool for image analysis.

c.pfaffenbichler
Community Expert
Community Expert
August 5, 2024

Using Work Path in ESTK would provide an option. 

Though maybe UXP Scripting offers more convenient (and speedier) approaches. 

 

What exactly do you mean by »1x800 pixels«? 

WMETS
WMETSAuthor
Participating Frequently
August 5, 2024

I'll try to explain. Normally, we use industry software designed for this purpose, but it no longer receives updates and only runs on Windows. I work for a textile pattern design company, and we need to check designs against certain specifications. One of the specs is that there cannot be vertical sections of white that are 800 pixels or longer at a resolution of 508. All larger sections need to have a sort of bridge. So we need to look up those parts and then draw some lines in the dessin style to overcome these sections

A will attache a screenshot of a tiff file that was generated by the other software.

c.pfaffenbichler
Community Expert
Community Expert
August 5, 2024

But the 800px apply in both directions? 

Can you provide a sample file?