Last active
September 1, 2022 06:53
-
-
Save unitycoder/518188714ab8a29a0c6ca312e71fc0a3 to your computer and use it in GitHub Desktop.
Connected-component labeling script test
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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