Identifying specific Blobs in Processing - java

I am working in Processing making contour maps from height maps using the blobDetection library. My end goal is to laser cut the blobs to make sort of architectural landscape maquettes.
Currently I can obtain the contours and export the contour map as SVG which is great, but I want to be able to identify each blob (or contour, or ring) and to then be able to manipulate each contour separately. Namely I want to reposition them on the window so that they are not on top of each other and do not overlap. I also want to assign coordinates to each individual blob so they would be easy to know where each blob goes once it has been laser cut.
Here is the code (from an example provided by author v3ga):
import processing.svg.*;
import blobDetection.*;
import peasy.*;
import processing.pdf.*;
PeasyCam cam;
PImage img;
float levels = 10;
float factor = 10;
float elevation = 125;
float colorStart = 0;
float colorRange = 160;
BlobDetection[] contours = new BlobDetection[int(levels)];
boolean recording = false;
void keyPressed(){
if (key == 'r' || key == 'R'){
recording = !recording;
}
}
void setup() {
size(1000,800,P3D);
surface.setResizable(true);
img = loadImage("map1.gif");
surface.setSize(img.width, img.height);
cam = new PeasyCam(this, img.width, img.height, 0, 500);
colorMode(HSB, 360, 100, 100);
for (int i=0; i<levels; i++){
contours[i] = new BlobDetection(img.width, img.height);
contours[i].setThreshold(i/levels);
contours[i].computeBlobs(img.pixels);
}
}
void draw(){
if (recording){
beginRecord(SVG, "frame_####.svg");
}
for (int i=0; i<levels; i++){
drawContours(i);
}
if (recording) {
endRecord();
recording = false;
}
}
void drawContours(int i) {
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<contours[i].getBlobNb() ; n++) {
b=contours[i].getBlob(n);
if (b!=null) {
stroke(250,75,90);
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
line(
eA.x*img.width, eA.y*img.height,
eB.x*img.width, eB.y*img.height
);
}
}
}
}
After testing a few things I think the best is to create and array of objects that contain information for each blob (x and y coordinates, level) and to fill this array in the drawContours method. However I am having a lot of trouble obtaining the right information to store in this array.
So my questions are:
How to identify x,y coordinates of complex shapes such as these blobs
How to reposition the blobs once I have their info store in an array
Any suggestion, even using other techniques (i.e. not Processing) would be greatly appreciated.

To display the blobs in 3D add the height_scale constant at the top and modify the drawContours function to the following:
final int HEIGHT_SCALE = 10; // amount of z space between each blob
void drawContours(int i) {
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<contours[i].getBlobNb() ; n++) {
b=contours[i].getBlob(n);
if (b!=null) {
stroke(250,75,90);
beginShape();
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
vertex(eA.x*img.width, eA.y*img.height, i*HEIGHT_SCALE);
vertex(eB.x*img.width, eB.y*img.height, i*HEIGHT_SCALE);
}
endShape(CLOSE);
}
}
}
Keep in mind I have not run this code, and I haven't used the framework that you are using however the beginShape(), vertex(), and endShape() functions should allow you to create the same lines between edges, and I added a z coordinate to the vertex's so that they are separated in height.
Just realized you could also just go with the following because line(x1,y1,x2,y2) can also take line(x1,y1,z1,x2,y2,z2):
final int HEIGHT_SCALE = 10; // amount of z space between each blob
void drawContours(int i) {
Blob b;
EdgeVertex eA,eB;
for (int n=0 ; n<contours[i].getBlobNb() ; n++) {
b=contours[i].getBlob(n);
if (b!=null) {
stroke(250,75,90);
for (int m=0;m<b.getEdgeNb();m++) {
eA = b.getEdgeVertexA(m);
eB = b.getEdgeVertexB(m);
if (eA !=null && eB !=null)
line(eA.x*img.width, eA.y*img.height, i*HEIGHT_SCALE, eB.x*img.width, eB.y*img.height, i*HEIGHT_SCALE);
}
}
}
}

Related

Filling an area of piece of circle in square grid

I am trying to fill some particular circle areas on a square grid in Proceesing. However, I am facing some weird filling after I run my code. I used the "atan2" function to get the angle of points in the grid and apply some if conditions to limit the area. But it doesn't work. Actually, it works some way but not exactly what I want.This is the result I run the code-> enter image description here, but it should be like -> enter image description here Can someone help me to solve this, please?
(Additionally, I seized this page to detect which points are in the area I specified enter link description here.
Cell [][] cells;
int res = 10;
PVector circleCenter ;
float rad=290;
void setup() {
size(1200, 800);
cells = new Cell[width/res ][height/res];
for (int i=0; i<cells.length; i++) {
for (int j = 0; j<cells[0].length; j++) {
cells[i][j] = new Cell();
}
}
}
void draw() {
background(255);
circleCenter = new PVector(mouseX, mouseY);
display();
pushStyle();
ellipse(circleCenter.x, circleCenter.y, 20, 20);
popStyle();
//println(frameRate);
}
void display() {
for (int i=0; i<cells.length; i++) {
for (int j = 0; j<cells[0].length; j++) {
if (sq(i*res-width/2) + sq(j*res-height/2) <=sq(rad/2)) {
float angleInRad = atan2(j*res-height/2, i*res-width/2);
///
if (angleInRad<radians(-10) && angleInRad>radians(-60)) {
fill(0, 255, 0); // degrees(atan2(j*res-circleCenter.y, i*res-circleCenter.x))
}
} else {
noFill();
}
rect(i*res, j*res, res, res);
}}
class Cell {
boolean blackness=false;
Cell() {
}
}
You have not specified input data for your task. I assume we have center coordinates cx, cx, radius r, starting and ending angle sa, ea of the sector (in code -10, -60).
There is an approach to check whether vector direction lies in sector, it's robust to potential troubles with periodicity, negative values etc. At first normalize range ends, then find middle angle and half-angle
half = (ea - sa) / 2
mid = (ea + sa) / 2
coshalf = Cos(half)
Now compare that difference of angle and middle one is lower then half-angle
if Cos(angle - mid) >= coshalf then
angle lies in range sa..ea

Processing Java -- Find the length of a multi-point curve?

I'm working on a sketch which draws a multi-point curve using curveVertex, and I need to calculate the length of the resulting curve (both the total curve length, and the length of each individual segment as well). I've found this page, which deals with bez curve length, but it's from several years ago, and I'm wondering if there's a more modern way to handle this?
Here's a quick demo of what I'm looking for:
class curveNode() {
int x,y,z;
float startPosition, segmentLength;
curveNode() {
x = int(random(30));
y = int(random(30));
z = int(random(30));
}
}
void draw() {
// Make some nodes
curveNode[] nodeArray = new curveNode[5];
for (int i=0;i<nodeArray.length;i++) {
nodeArray[i] = new curveNode();
}
// draw the curve
noFill();
beginShape();
for (int i=0;i<nodeArray.length;i++) {
curveVertex(nodeArray[i].x, nodeArray[i].y, nodeArray[i].z);
}
endShape();
// This is where I need help!
// calculate the distances
float lengthRunningTotal = 0;
float segmentLength = 0;
for (int i=0;i<nodeArray.length-1;i++) {
nodeArray[i].startPosition = distanceRunningTotal;
// ???? calculate segment length ????
// float segmentLength = nodeArray[1] ??? nodeArray[n+1]
// lengthRunningTotal += segmentLength;
}
// TK fill values for the final node in the array.
}

Slow map in java

I'm making a game in java, is a rpg, however, only with the map the game is slow.
The map is made ​​in TiledMap Editor, therefore, an XML that is read and loaded into an ArrayList. My PC is a dual-core 3.0, 4GB RAM, 1GB Video.
The do the rendering is done as follows:
//method of tileset class
public void loadTileset(){
positions = new int[1 + tilesX * tilesY][2];
int yy = 0;
int xx = 0;
int index = 0;
// save the initial x and y point of each tile in an array named positions
// positions[tileNumber] [0] - X position
// positions[tileNumber] [1] - Y position
for(int i = 1 ; i < positions.length; i++){
if(index == tilesX ){
yy += tileHeight;
xx = 0;
index = 0;
}
positions[i][0] = xx;
positions[i][1] = yy;
xx += tileWidth;
index++;
}
}
//method of map class
public void draw(Graphics2D screen){
//x and y position of each tile on the screen
int x = 0; int y = 0;
for(int j = 0; j < 20 ; j++){
for(int i = initialTile ; i < initialTile + quantTiles ; i++){
int tile = map[j][i];
if(tile != 0){
screen.drawImage(tileSet.getTileImage().getSubimage(tileSet.getTileX(tile), tileSet.getTileY(tile),tileSet.getTileWidth(), tileSet.getTileHeight()),x,y,null);
}
x += tileSet.getTileWidth();
}
x = 0;
y += tileSet.getTileHeight();
}
}
Am I doing something wrong?
Note: I'm new to the forum and to make matters worse I do not understand very much English, so excuse any mistake.
First of all, you should not create the subimages for the tiles during each call. Strictly speaking, you should not call getSubimage at all for images that you want to paint: It will make the image "unmanaged", and this can degrade rendering performance by an order of magnitude. You should only call getSubimage for images that you do not want to render - for example, when you are initially creating individual images for the tiles.
You obviously already have a TileSet class. You could add a bit of functionality to this class so that you can directly access images for the tiles.
Your current code looks like this:
screen.drawImage(
tileSet.getTileImage().getSubimage(
tileSet.getTileX(tile),
tileSet.getTileY(tile),
tileSet.getTileWidth(),
tileSet.getTileHeight()),
x,y,null);
You could change it to look like this:
screen.drawImage(tileSet.getTileImage(tile), x,y,null);
The getTileImage(int tile) method suggested here could then obtain tiles that have been stored internally.
I'll sketch a few lines of code from the tip of my head, you'll probably be able to transfer this into your TileSet class:
class TileSet
{
private Map<Integer, BufferedImage> tileImages;
TileSet()
{
....
prepareTileImages();
}
private void prepareTileImages()
{
tileImages = new HashMap<Integer, BufferedImage>();
for (int tile : allPossibleTileValuesThatMayBeInTheMap)
{
// These are the tiles that you originally rendered
// in your "draw"-Method
BufferedImage image =
getTileImage().getSubimage(
getTileX(tile),
getTileY(tile),
getTileWidth(),
getTileHeight());
// Create a new, managed copy of the image,
// and store it in the map
BufferedImage managedImage = convertToARGB(image);
tileImages.put(tile, managedImage);
}
}
private static BufferedImage convertToARGB(BufferedImage image)
{
BufferedImage newImage = new BufferedImage(
image.getWidth(), image.getHeight(),
BufferedImage.TYPE_INT_ARGB);
Graphics2D g = newImage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return newImage;
}
// This is the new method: For a given "tile" value
// that you found at map[x][y], this returns the
// appropriate tile:
public BufferedImage getTileImage(int tile)
{
return tileImages.get(tile);
}
}

Is there a faster way to get the colors in a screen-shot?

I have piece of code, and what it does is find the colors of an image (or section of the screen) and if the R G and B color's are greater than 127 it put's a 1 in the corresponding position of a 2D int array. Here is the segment I have now, but it is obviously extremely slow. Is there a better way to do this?
private void traceImage(){
try{
Robot r = new Robot();
b = new int[470][338];
for(int y = 597; y < 597+469; y++)
{
for(int x = 570; x < 570+337; x++)
{
if(r.getPixelColor(x,y).getRed() > 127 &&
r.getPixelColor(x,y).getGreen() > 127 &&
r.getPixelColor(x,y).getBlue() > 127)
{
b[y-597][x-570] = 1;
}
else
{
b[y-597][x-570] = 0;
}
}
}
}
catch(Exception ex){System.out.println(ex.toString());}
}
There must be a faster way to do this using the values above. b is the int array and it is initialized in this segment. r is a robot that I use to find the color of the pixel on the screen. x and y should be self explanatory.
Thanks Mikera! This is the end result I got based on your answer:
private void traceActionPerformed(java.awt.event.ActionEvent evt) {
try{
Robot r = new Robot();
BufferedImage image = r.createScreenCapture(new Rectangle(597,570,470,337));
b = new int[image.getWidth()][image.getHeight()];
for(int y=0;y<image.getWidth();y++){
for(int x=0;x<image.getHeight();x++){
if(image.getRGB(y,x)==-1){
b[y][x]=0;
}
else{
b[y][x]=1;
}
}
}
System.out.println("Done");
}
catch(Exception ex){System.out.println(ex.toString());}
}
The -1 in the if else statement apparently finds the value of black pixels like I wanted.
You should use Robot.createScreenCapture() to capture the entire subimage in one go.
Then you will be able to query the individual pixels in the subimage much faster, e.g. using BufferedImage.getRGB(x,y)
Interestingly your specific operation has a very fast bitwise implementation: just do ((rgb & 0x808080) == 0x808080)

Stack Overflow error with ArrayLists

I'm trying to write some code to look through an image file for groups of pixels that are the same color.
The way I do this is I iterate through the image (in the form of a 1d integer array with the color's hash code) by pixel to find a pixel of the color that I'm searching for. Once one is found, I do a dfs to find adjacent pixels of the same color and add them to a new object I called a Blob. I use a boolean array to keep track of which pixels have already been added so I don't add identical blobs.
I'm using ArrayList for each Blob object to keep track of the pixel numbers. And then I use another ArrayList of Blobs to store the different groups.
When I try to run this on a simple example, a picture with the top half white and the bottom half bottom, I get a stack overflow error when I try to use a picture that's too large. Specifically, when I try to do this with a 320x240 image, I get the stackoverflow once 2752 pixels are added to the blob.
Am I just not using the right data structure for what I want to do? I read that ArrayLists can store Integer.maxValue objects in them.
My code is pasted below. Any help is greatly appreciated.
//blobfind tests code to find similar pixels of a minimum size and groups them together for analysis later
//purpose is to identify color coded objects through the webcam
//util for ArrayList
import java.util.*;
import java.awt.Color;
import java.io.*;
public class Blobfind2 {
//width and height of image in pixels
private int width;
private int height;
//hash code for the color being searched for
private int colorCode;
//minimum blob size to be added
private int minPixels;
//image in form of array of hashcodes for each pixel
private int[] img;
//keeping track of which pixels have been added to a blob
private boolean[] added;
//keeping track of which pixels have been visited when looking for a new blob
private boolean[] visited;
//debugging variable
private int count;
public Blobfind2(int inwidth, int inheight, int inCCode, int inminPixels, int[] inimage) {
width = inwidth;
height = inheight;
colorCode = inCCode;
minPixels = inminPixels;
img = inimage;
count = 0;
}
//takes hashCodeof color, minimum pixel number, and an image in the form of integer array
public ArrayList findColor() {
//makes an arraylist of "blobs"
ArrayList bloblist = new ArrayList();
//keep track of which pixels have been added to a blob
boolean[] added = new boolean[width * height];
//checks through each pixel
for (int i = 0; i < img.length; i++) {
//if it matches and is not part of a blob, we run dfs to collect all the pixels in that blob
if ((img[i] == colorCode) && (added[i] == false)) {
//visited keeps track of which pixels in the blob have been visited
//refreshed each time a new blob is made
boolean[] visited = new boolean[width*height];
Blob currBlob = new Blob();
dfs(img, currBlob, i, Color.white.hashCode(), added, visited);
//adds the blob to the bloblist if it is of a certain size
if (currBlob.mass() >= minPixels) {
bloblist.add(currBlob);
}
}
}
return bloblist;
}
//recursive algorithm to find other members of a blob
public void dfs (int[] img, Blob blob, int currPixel, int colorCode, boolean[] added, boolean[] visited) {
//System.out.print(currPixel + " - " + count + " ");
count++;
//check current pixel, this only happens on the first pixel
if (visited[currPixel] == false) {
blob.add(img[currPixel]);
added[currPixel] = true;
visited[currPixel] = true;
}
//checks down pixel
if ((currPixel + width < height*width) && (visited[currPixel + width] == false)) {
if (img[currPixel + width] == colorCode) {
blob.add(img[currPixel + width]);
currPixel = currPixel + width;
added[currPixel] = true;
visited[currPixel] = true;
dfs(img, blob, currPixel, colorCode, added, visited);
}
}
//checks up pixel
if ((currPixel - width > 0) && (visited[currPixel - width] == false)) {
if (img[currPixel - width] == colorCode) {
blob.add(img[currPixel - width]);
currPixel = currPixel - width;
added[currPixel] = true;
visited[currPixel] = true;
dfs (img, blob, currPixel, colorCode, added, visited);
}
}
//checks right pixel
if ((currPixel + 1 < width * height) && (visited[currPixel + 1] == false) && (((currPixel + 1) % width) != 0)) {
if (img[currPixel + 1] == colorCode) {
blob.add(img[currPixel + 1]);
currPixel = currPixel + 1;
added[currPixel] = true;
visited[currPixel] = true;
dfs(img, blob, currPixel, colorCode, added, visited);
}
}
//checks left pixel
if ((currPixel - 1 > 0) && (visited[currPixel - 1] == false) && (((currPixel - 1) % width) != width - 1)) {
if (img[currPixel - 1] == colorCode) {
blob.add(img[currPixel - 1]);
currPixel = currPixel - 1;
added[currPixel] = true;
visited[currPixel] = true;
dfs(img, blob, currPixel, colorCode, added, visited);
}
}
return;
}
//test case, makes a new image thats half black and half white
//should only return one blob of size width*height/2
public static void main(String[] args) {
int width = 320;
int height = 240;
//builds the image
int[] img = new int[width * height];
for (int i = 0; i < img.length; i++) {
if (i < img.length/4) {
img[i] = Color.white.hashCode();
} else {
img[i] = Color.black.hashCode();
}
}
//runs blobfind
Blobfind2 bf = new Blobfind2(width, height, Color.white.hashCode(), 1, img);
ArrayList bloblist = bf.findColor();
System.out.println(bloblist.size());
//need to typecast things coming out of arraylists
Blob firstblob = (Blob)bloblist.get(0);
System.out.println(firstblob.mass());
}
private class Blob {
private ArrayList pixels = new ArrayList();
private Blob() {
}
private int mass() {
return pixels.size();
}
private void add(int i) {
pixels.add(i);
}
private ArrayList getBlob() {
return pixels;
}
}
}
The stack overflow error has nothing to do with whether you use an List, or a Map, or any other particular data structure. Those constructs are allocated on the heap. You are seeing your stack overflow error because you make recursive function calls. Each recursive function call allocates memory on the stack. You can increase your -Xss value (e.g java -Xss8m HelloWorld) or you can re-write your algorithm to be non-recursive (assuming your algorithm is correct).
This looks very similar to a flood-fill algorithm. The recursive implementation might blow the stack (e.g. make too many recursive calls) for large blobs, simply because you have to explore 4 neighbours for every pixel. Worst case is an image all in the same blob!
I would try making the stack explicit. You want to avoid the recursion and use a simple loop based approach instead.
public void dfs () {
Stack<Pixel> pixels = new Stack<Pixel>();
pixels.push(currentPixel);
while (!pixels.isEmpty()) {
Pixel x = pixels.pop();
// Do whatever processing on this pixel
Pixel upPixel = getUpPixel();
if (upPixel == colorCode) {
pixels.push(upPixel);
}
// And so on
}
}

Categories