Skip to content

Instantly share code, notes, and snippets.

@unitycoder
Last active September 1, 2022 06:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save unitycoder/518188714ab8a29a0c6ca312e71fc0a3 to your computer and use it in GitHub Desktop.
Save unitycoder/518188714ab8a29a0c6ca312e71fc0a3 to your computer and use it in GitHub Desktop.
Connected-component labeling script test
// https://unitycoder.com/blog/2022/09/01/testing-connected-component-labeling/
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CCL : MonoBehaviour
{
Renderer r;
int size = 32;
Texture2D tex;
Color32[] pixels;
int[] labels;
Camera cam;
Color32[] labelColors;
void Start()
{
cam = Camera.main;
r = GetComponent<Renderer>();
tex = new Texture2D(size, size, TextureFormat.ARGB32, false);
tex.filterMode = FilterMode.Point;
tex.wrapMode = TextureWrapMode.Clamp;
pixels = tex.GetPixels32(0);
ClearImage();
r.material.mainTexture = tex;
}
void Update()
{
// draw
if (Input.GetMouseButton(0))
{
RaycastHit hit;
var ray = cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out hit, 999))
{
var pix = hit.textureCoord * size;
tex.SetPixel((int)pix.x, (int)pix.y, Color.white);
tex.Apply(false);
}
}
// erase
if (Input.GetMouseButton(1))
{
RaycastHit hit;
var ray = cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out hit, 999))
{
var pix = hit.textureCoord * size;
tex.SetPixel((int)pix.x, (int)pix.y, Color.clear);
tex.Apply(false);
}
}
// check
if (Input.GetMouseButtonDown(2))
{
RaycastHit hit;
var ray = cam.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray.origin, ray.direction, out hit, 999))
{
var pix = hit.textureCoord * size;
var l = labels[((int)pix.y) * size + ((int)pix.x)];
Debug.Log("Pos: " + ((int)pix.x) + "," + ((int)pix.y) + " label=" + l);
}
}
if (Input.GetKeyDown(KeyCode.Space)) StartCoroutine(Find());
if (Input.GetKeyDown(KeyCode.Escape))
{
ClearImage();
}
}
// https://en.wikipedia.org/wiki/Connected-component_labeling
IEnumerator Find()
{
Debug.Log("Find..");
// get current pixels
pixels = tex.GetPixels32(0);
// init labels array
labels = new int[pixels.Length];
int labelCount = 1;
// generate random label colors
int maxLabels = 32;
labelColors = new Color32[maxLabels];
float m = 0;
float step = 1f / (float)maxLabels;
for (int n = 0; n < maxLabels; n++)
{
labelColors[n] = Random.ColorHSV(m, m, 1, 1, 1, 1);
m += step;
}
Color32 c = new Color32(0, 0, 0, 0);
int i = 0;
for (int y = 1; y < size - 1; y++)
{
for (int x = 1; x < size - 1; x++)
{
yield return 0;
i = y * size + x;
// skip empty pixels
if (pixels[i].a == 0)
{
// mark checked empty pixels to gray
pixels[i] = Color.gray;
tex.SetPixels32(pixels);
tex.Apply(false);
//yield return 0;
continue;
}
// check neighbours
// FIXME have to do floodfill (otherwise cannot find connected pixels further away)
// or need to do 2nd pass to cleanup connected labels (merge with connected label?)
int up = (y + 1) * size + x;
int down = (y - 1) * size + x;
int right = y * size + (x + 1);
int upRight = (y + 1) * size + (x + 1);
int downRight = (y - 1) * size + (x + 1);
int left = y * size + (x - 1);
int upLeft = (y + 1) * size + (x - 1);
int downLeft = (y - 1) * size + (x - 1);
int sum = labels[up] + labels[down] + labels[left] + labels[right];
sum += labels[upRight] + labels[downRight] + labels[upLeft] + labels[downLeft];
// no labeled neighbours
if (sum == 0)
{
//Debug.Log("No labeled neighbours: " + x + "," + y);
//yield return new WaitForSeconds(1f);
// label this pixels with current label
labels[i] = labelCount++;
}
else // have labeled neighbours, then use their label?
{
int nLabel = labels[up];// + labels[down] + labels[left] + labels[right]
if (nLabel == 0) nLabel = labels[down];
if (nLabel == 0) nLabel = labels[left];
if (nLabel == 0) nLabel = labels[right];
if (nLabel == 0) nLabel = labels[upRight];
if (nLabel == 0) nLabel = labels[downRight];
if (nLabel == 0) nLabel = labels[upLeft];
if (nLabel == 0) nLabel = labels[downLeft];
labels[i] = nLabel;
}
// skip already labeled pixels?
//if (labels[i] > 0) continue;
// if up is pixels and not labeled, then label it with current pixel
// if (labels[up] == 0) labels[up] = labelCount;
// if ( labels[down] == 0) labels[down] = labelCount;
// if (labels[left] == 0) labels[left] = labelCount;
// if ( labels[right] == 0) labels[right] = labelCount;
// set color by label
pixels[i] = labelColors[labels[i]];
//yield return new WaitForSeconds(0.5f);
tex.SetPixels32(pixels);
tex.Apply(false);
}
}
Debug.Log("Labelcount= " + labelCount);
}
void ClearImage()
{
// clear image
for (int k = 0; k < pixels.Length; k++)
{
pixels[k] = new Color32(0, 0, 0, 0);
}
tex.SetPixels32(pixels);
tex.Apply(false);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment