Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
4 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
0
Question by didicayu · Nov 20, 2021 at 10:36 AM · texture2dimageresolutionpixelatedrecognition

Can't find a way to downscale a 560x560 Texture2D / Image to a 28x28 resolution without losing lots of pixels

Over the past days I've developed a Doodle recognition application with a neural network library I wrote a while back, everything Is running fine now and I only need to be able to draw on top of a texture big enough for me to draw comfortably (580x580 works just fine for me), and then get the pixel data to feedforward it through my neural net library, the problem comes when I try to downscale the image because I lose too much data, the resulting image is just a bunch of points following the trace I draw on the bigger image and I don't know how to fix it.

I just want to be able to downscale the texture in a pixel-perfect way since I don't care about blockiness because it's the nature of the training data I used for the ML model.

Here's the script I've been using to downscale my image with no luck: https://pastebin.com/qkkhWs2J

I used @petersvp script

And here's the code triggered by a button once I finish my Doodle on the main texture:

 public void GetDrawingData(){
 
         ScalableTex = new Texture2D(28,28);
         ScalableTex = TextureScale.scaled(DrawnTex,28,28,FilterMode.Point);
         ScalableTex.Apply();
         
         GetResult(ScalableTex.GetPixels().ColorToFloat());
 
         targetSprite.GetComponent<SpriteRenderer>().sprite = Sprite.Create(ScalableTex,new Rect(0,0,ScalableTex.width,ScalableTex.height),new Vector2(0.5f,0.5f));
 
         byte[] bytes = ScalableTex.EncodeToPNG();
         File.WriteAllBytes(Application.dataPath + "/../a.png", bytes);
     }

alt text

screenshot-1.png (25.8 kB)
Comment
Add comment · Show 16
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image petersvp · Nov 20, 2021 at 11:27 AM 0
Share

You asked for point sampling and you've got what you asked for.

Use Trilinear filtering instead. FilterMode.Trilinear

avatar image didicayu petersvp · Nov 20, 2021 at 12:23 PM 0
Share

I changed it to FilterMode.Point just to test since leaving it on default didn't really help me either, sorry if I called your code wrongly I am no texture/image expert and thought the process of downscaling a 2D texture would be pretty straightforward. I've now changed it to FilterMode.Trilinear and got this: alt text

Thanks for making such a helpful script btw, unity Texture2D.scale just gave me a grey pixel array, I read it just downscales the colour array and not actually the image itself.

screenshot-2.png (27.7 kB)
avatar image petersvp didicayu · Nov 20, 2021 at 12:28 PM 1
Share

Trilinear filtering requires that you build the mipmaps of the texture. I can't exactly remember how that was done though. Texture2d Apply maybe, Also 28x28 isn't the best size, try with 32x32? Right now I can't test it but I may be able to later today.

Show more comments
Show more comments
avatar image Namey5 petersvp · Nov 20, 2021 at 12:30 PM 1
Share

Trilinear filtering is unnecessary in this case as there are no mipmaps - it would be equivalent to bilinear filtering, and still wouldn't solve this issue (filtering after sampling won't fill in the information lost when downscaling via nearest neighbour directly from full-res to low-res).

avatar image petersvp Namey5 · Nov 20, 2021 at 12:32 PM 0
Share

He must use mipmaps for Trilinear downsampling. There was a texture2d ctor with bool for mipmaps

avatar image didicayu petersvp · Nov 20, 2021 at 12:31 PM 0
Share

I've really tried every single thing I could find on this forum and on the internet about downscaling a texture in unity but unity just doesn't offer the functionality and my case is very specific where I need to downscale it so much, so it makes sense data is lost, but still I was hoping to be able to find an easy way to do it. The only thing I can think of to solve this is to call a resizing API to do the work for me.

I got my data from here, I don't think it should be this hard for me to downscale an image, is it?

avatar image petersvp didicayu · Nov 20, 2021 at 12:34 PM 0
Share

Again try to use the Texture2d constructor that takes size and a boolean for mipmaps.

avatar image unity_ka6jgzfPPmtNCw · Nov 20, 2021 at 01:17 PM 0
Share

You're removing 99.8% of the pixels. You're going to suffer a lot of data loss.

avatar image didicayu unity_ka6jgzfPPmtNCw · Nov 20, 2021 at 02:18 PM 0
Share

That's the whole point, I want to lose data for my neural net to process it faster and it's the format the data came in anyway, but still I was expecting something more like this: https://imgur.com/a/cgLMqqX

1 Reply

· Add your reply
  • Sort: 
avatar image
2
Best Answer

Answer by Namey5 · Nov 21, 2021 at 11:47 AM

The reason this is occurring is because you are only sampling the full resolution texture once per low-res pixel. As a result, most of the pixels get skipped and you end up with an image that might be passable for some textures, but in your case not at all. Instead what you want to do is keep all of the data for every high resolution pixel and combine them all into the nearest low-res pixel in some way.

To do this 'accurately' you would need to find all of the full-res pixels that are covered by the area of the low-res pixel and average them (be conservative; full-res pixels should be shared if there isn't a 1-1 match). This requires a lot of manual work however - an optimised way to do this is to progressively downsample, i.e. rather than going straight from full-res -> low-res you can instead go from full-res -> half-res -> quarter-res -> ... -> desired-res:

 public Texture2D FilteredDownscale (Texture2D a_Source, int a_NewWidth, int a_NewHeight)
 {
     // Keep the last active RT
     RenderTexture _LastActiveRT = RenderTexture.active;
 
     // Start by halving the source dimensions
     int _Width = a_Source.width / 2;
     int _Height = a_Source.height / 2;
 
     // Cap to the target dimensions.
     // This could be done with Mathf.Max() but that wouldn't take into account aspect ratio.
     if (_Width < a_NewWidth || _Height < a_NewHeight)
     {
         _Width = a_NewWidth;
         _Height = a_NewHeight;
     }
 
     // Create a temporary downscaled RT
     RenderTexture _Tmp1 = RenderTexture.GetTemporary (_Width, _Height, 0, RenderTextureFormat.ARGB32);
 
     // Copy the source into our temporary RT
     Graphics.Blit (a_Source, _Tmp1);
 
     // Loop until our target dimensions have been reached
     while (_Width > a_NewWidth && _Height > a_NewHeight)
     {
         // Keep halving our current dimensions
         _Width /= 2;
         _Height /= 2;
 
         // And match our target dimensions once small enough
         if (_Width < a_NewWidth || _Height < a_NewHeight)
         {
             _Width = a_NewWidth;
             _Height = a_NewHeight;
         }
 
         // Downscale again into a smaller RT
         RenderTexture _Tmp2 = RenderTexture.GetTemporary (_Width, _Height, 0, RenderTextureFormat.ARGB32);
         Graphics.Blit (_Tmp1, _Tmp2);
 
         // Swap our temporary RTs and release the oldest one
         (_Tmp1, _Tmp2) = (_Tmp2, _Tmp1);
         RenderTexture.ReleaseTemporary (_Tmp2);
     }
 
     // At this point _Tmp1 should hold our fully downscaled image,
     // so set it as the active RT
     RenderTexture.active = _Tmp1;
 
     // Create a new texture of the desired dimensions and copy our data into it
     Texture2D _Tex = new Texture2D (a_NewWidth, a_NewHeight);
     _Tex.ReadPixels (new Rect (0, 0, a_NewWidth, a_NewHeight), 0, 0);
     _Tex.Apply ();
 
     // Reset the active RT and release our last temporary copy
     RenderTexture.active = _LastActiveRT;
     RenderTexture.ReleaseTemporary (_Tmp1);
 
     return _Tex;
 }

The reason this works is because sampling at exactly half resolution with bilinear filtering will cause every low-res texture sample to land directly inbetween all 4 full-res neighbouring pixels - which are then averaged directly by the bilinear filtering.

Comment
Add comment · Show 3 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image didicayu · Nov 23, 2021 at 10:18 PM 0
Share

This is actually brilliant, thank you very much for your answer, I ended up fixing it by increasing the width of the texture painter asset I was using, which also ended up favouring my neural net input with a much more clean input and data very similar to the training one. I'm sorry I didn't check your answer earlier I had homework to do and classes to take and left this project aside for a while. While increasing the width of the pen was enough for me I'll be marking your answer as correct since you put so much effort and time into it to help me, just as many other kind users here have done. I've taken a look at the script and it makes perfect sense to me, this might actually solve the problem for someone that is looking to downscale a texture as much as I needed to. Here's a screenshot of my little project now working btw :Dalt text

neuralnetworking.png (72.0 kB)
avatar image petersvp · Nov 24, 2021 at 01:25 AM 0
Share

You basically made a multi pass bilinear downsampler that emulates Trilinear filtering in a way. By using your code and writing to texture mipmaps using RawTextureData the mipmaps I've got feeded on the original texture were much better looking in native Trilinear filter that the crappy mipmaps Unity generates with Apply(true) - thanks

avatar image Namey5 petersvp · Nov 24, 2021 at 08:10 AM 0
Share

From memory Unity's autogenerated mipmaps are internally handled by the GPU directly - so there might be some differences depending on your hardware, but at a base level the mipmaps are probably generated the same way (i.e. by feeding the previous mip into the next like this). Using trilinear filtering and mipmaps should generate pretty much the same image I would think.

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

141 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Display an image in native resolution of a Head Mounted Display 0 Answers

How do you download image to UI.Image? 4 Answers

What's the best way to draw to a texture? 0 Answers

How to redye pixels of a texture? 1 Answer

Image Stitching without external libraries 0 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges