Trying to find the border of a color and set the pixel to another, but it does something weird

Not entierally sure if this is in the right place, but yeah.

So right now I have a basic script to find the border of a color, then using SetPixel(), I set the pixel of another texture 2d and apply it to a matieral. It works for strait lines, but when I made this image:
7869502--999943--upload_2022-2-4_16-57-25.png

Yet it creates this:

I have no idea why. The code seems fine:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MapDisplay : MonoBehaviour
{
    [SerializeField]
    Texture2D provinceMapTexture;

    Color provinceBorderColor = new Color(0, 0, 0);

    public GameObject MapDisplayObject;

    private void Awake()
    {

        int provinceTextureMapWidth = provinceMapTexture.width;
        int provinceTextureMapHeight = provinceMapTexture.height;

        Texture2D provinceMapTextureDisplay = new Texture2D(provinceTextureMapWidth, provinceTextureMapHeight);

        for (int x = 0; x < provinceTextureMapWidth; x++)
        {
            for (int y = 0; y < provinceTextureMapHeight; y++)
            {
                Color previousPixel = provinceMapTexture.GetPixel(x - 1, y - 1);
                Color currentPixel = provinceMapTexture.GetPixel(x, y);

                if (previousPixel != currentPixel)
                {
                    provinceMapTextureDisplay.SetPixel(x - 1, y - 1, provinceBorderColor);
                }

            }
        }

        provinceMapTextureDisplay.Apply();
        MapDisplayObject.GetComponent<Renderer>().material.mainTexture = provinceMapTextureDisplay;

    }

}

Also it would be helpful to remove the Anti-Aliasing (I think thats what it is)

I’d strip it down to the tiniest dataset that shows the problem, like a 2x2 or 3x3 or 4x4 image. That way you can easily use Debug.Log() to find out what is going on.

Also, comparing colors is not always what you think. For one they are floating point. For two, if you have any compression on your source image, blue might not be the blue to think. :slight_smile:

In any case, you must find a way to get the information you need in order to reason about what the problem is.

What is often happening in these cases is one of the following:

  • the code you think is executing is not actually executing at all
  • the code is executing far EARLIER or LATER than you think
  • the code is executing far LESS OFTEN than you think
  • the code is executing far MORE OFTEN than you think
  • the code is executing on another GameObject than you think it is
  • you’re getting an error or warning and you haven’t noticed it in the console window

To help gain more insight into your problem, I recommend liberally sprinkling Debug.Log() statements through your code to display information in realtime.

Doing this should help you answer these types of questions:

  • is this code even running? which parts are running? how often does it run? what order does it run in?
  • what are the values of the variables involved? Are they initialized? Are the values reasonable?
  • are you meeting ALL the requirements to receive callbacks such as triggers / colliders (review the documentation)

Knowing this information will help you reason about the behavior you are seeing.

You can also put in Debug.Break() to pause the Editor when certain interesting pieces of code run, and then study the scene

You could also just display various important quantities in UI Text elements to watch them change as you play the game.

If you are running a mobile device you can also view the console output. Google for how on your particular mobile target.

Another useful approach is to temporarily strip out everything besides what is necessary to prove your issue. This can simplify and isolate compounding effects of other items in your scene or prefab.

Here’s an example of putting in a laser-focused Debug.Log() and how that can save you a TON of time wallowing around speculating what might be going wrong:

Well the thing is its all running, all executing, and from the debug logs you suggested, its “working” (The variables are all correct for this situaition), but I think its the pixel generation for some reason, since the variables are right.

When the for loop element is 0 where is x - 1 and y - 1 on pixel coordinates of the texture 2D?

Kurt suggested break a small piece down. Try painting the first pixel and force a break when x and y are > 0

see what you get (if anything).

There may be a few things on the Texture2D that you need to modify, as you would on import settings of a texture you would otherwise have dragged and dropped into unity.
I see the resolution is low, I wonder if this is a low Rez view of a bilinear

take a scroll down this page you might get some ideas

It’s possible you are reading from a texture that is blended by import settings. Has Blurred edges. It’s particularly noticeable for small textures and sprites, and usually has to be removed if you want the classic pixel look.

I agree. There’s a reason I added the second sentence in my second paragraph above:

I meant to say “the blue you think…” I think compression is making your colors be slightly off what you expect, so your comparisons are not right.

Further backing this theory is the output you show has extra blockiness that is VERY reminiscent of 4x4 or 8x8 cel compression that is used on textures in Unity. :slight_smile:

That’s also why I suggested trying it on a TINY dataset so you could view every pixel and know that it is what you expect.

I too am still interested in whether the import settings of his guide texture were causing the issue.
Maybe he resolved?

1 Like

I was asleep, so I didn’t get a chance to respond. Sorry.

I’m not sure how to quote certain sections, but to Kurt-Dekker I think I must have skipped over that part on accident or forgotten it in my reply, and I agree with you, although I don’t know what would cause that to happen. I saved the file in gimp as a .png and tried to make sure it stayed the same image as normal. Whether it got compressed though, I don’t know. I would like to turn off the 4x4 8x8 cel compression thing that you mentioned if it is the cause of the “blurryness” of the black.

As for Animalman
“When the for loop element is 0 where is x - 1 and y - 1 on pixel coordinates of the texture 2D?”

That would make it -1 but it doesn’t matter because even changing it to +1 (I made some variations, such as setting it equal to the map width and height, and changing it to one based on the Debug.Log() Kurt suggested.). No changes could be detected, other than it fully looping through.

For Kurt again, I will try that smaller data set but I fail to see why it matter because it works for straight lines, but not anything else.

For Animal, how would I go about removing it? I assume that could be the problem of the “blurryness” I would like to get rid of. Either that or what Kurt said about the compression.

For the last thing, I did try adding a few breaks everywhere, and it wouldn’t do what I showed, but it would semi randomly from what I could tell place pixels. I think I tried break in the If, just outside, in an else, and after the Y loop

Could it actually be because the if is in the loop, and its required to execute before the loop repeats or something? Brain might not be working correctly right now but if nothing else works it could be worth a shot

Check the import settings of this guy in your project.

Put this guy on a material and apply it to your plane, check if the edges are blurred by default.

if yes scroll down on import settings of this item in the project folder in the inspector and turn off things like bilinear there are four drop down menus which apply effects to the texture on import.

I don’t know if this is the problem but it’s all I can think of because I don’t see anything crazy in the shape produced that indicates that the for loop ran away with itself it looks like it was building from the above texture after unity had automatically applied an effect to that texture on import.

the shape of your result is a good approximation of what you want however you look at it so something is distorting it

Why are you sampling diagonally? Surely you just want to check if x - 1, y is different.

EDIT: Actually you’re going to want to check various directions to see if it’s on a border.

Oh there we go, thats how you qoute

Anyway, yeah I realized with this it might be better to swap the X an Y that way if an entire row is filled with the same color it won’t take ages to go to the other row (As in the draw a pixel). Not sure what you mean by Diagonally

Done all of that, and there was no observable effect other than the edges being unblurred if applied to a material. It had no effect on anything else. I will try maybe swapping X and Y like I said in a reply above, but I doubt anything will happen

OKAY, MAJOR BREAKTHROUGH

So for some reason, I changed the Awake to Update, so I could measure it in real time, and I also while it was playing swapped the image of the ProvinceTest, with the Heightmap, which created this:

Now I’m sure its the image for ProvinceTest, possibly because it was a simple, hand made one or maybe because its way too small. The water is about the same color so thats why the whole world is black more or less. Hopefully with a slight tweak I can get it all black and then I make a new image and test it there. Thanks for all the help if this works!

By diagonal I mean you’re only checking the pixel above and to the left, which isn’t going to be accurate for certain paths. If you change it to check all the pixels around the point, you end up with a thick border like this:

Also make sure you set the compression on the map texture to None to remove all the artifacts.

1 Like

How would you do that?

Even after tweaking multiple things its still going on and I cant figure out how to fix it. Help is still very much appriated

Set the compression to None on your source texture importer settings, as I pointed out in my first AND my second post, and as Grozz has now pointed out.

Seriously. Do it. Trust me on this. I am a pixel professional.

If you don’t know how, google. Here’s my setup with and without compression.

7874113--1001050--Screen Shot 2022-02-06 at 7.15.23 PM.png

1 Like

I must have overlooked it, sorry. Finally though, thanks. With a few tweaks I think I can fix some of the holes! Thank you so much. Do you have any idea though how to fix those holes? I might have to end up redoing the code which is fine, but still want to see.

Definitely use Grozz’ idea from post #13 above: check the pixel in question against not just +1 and +1, but all 8 directions around the pixel.

EDIT: do keep in mind this isn’t a super-performant operation, not something you would want to be doing every frame. On a huge Texture it might take several seconds or even minutes to run.

You just need one extra inner loop to iterate all the offsets around a single pixel and if any are different, set an output pixel.

I usually use a pair of lookup tables for x and y offsets and iterate them. I always name them xtable8 so it’s easy for me to find many examples in my games:

        public static int[] xtable8 = new int[] { 0, 1, 1, 1, 0, -1, -1, -1};
        public static int[] ytable8 = new int[] { -1, -1, 0, 1, 1, 1, 0, -1};
1 Like

Well, as others have said this is essentially a curde diagonal edge detection without any tolerance. Of course the initial issues where due to compression and maybe resizing of the image (keep in mind that non power of two textures can create additional stretching artifacts). So when you are sure you don’t have any unwanted compression and preferably a power ot two texture, you’re only doing a diagonal edge detection from the bottom left to the top right which can be seen nicely in Kurt’s right image. Edges that go from the top left to the bottom right are nicely drawn however edges that go along the sweeping direction (bottom left to top right) are pretty much invisible or just some pixel artifacts.

To get a better outline you would need to do a horizontal and vertical edge detection seperately and combine the results. Some time ago someone asked for a shape outline detection on UA. I’ve posted some code in the comments below the answer as well as this WebGL example. Though shape detection may be not what you want depending on your usecase. My approach produces a list of boundary pixels. Though it has some limitations. Shapes with only a single pixel width that are not closed may suffer from being split into several shapes. The algorithim scans for shapes from the bottom up, line by line and then huggs to the outside of the shape and goes along the outline. As I said this was a different usecase but maybe the code could be utilized / modified here if the actual outlines are needed.

1 Like

I will do so, thanks so much for your help!.

I will check out the post, but you said to do the Horizontal and Vertical detection deperatly, then combine. I assume you mean to find the x value, then the y value, combine them and then do GetPixel? I’m not sure how that would be any different from what I am doing currently.