Friday, May 29, 2020

CSharp - Bits per Pixel or bit depth in a Human Readable Format for an image

Here's how to get human readable bit depth or color depth for an image in C#.

Note: This is measure in bits not bytes, therefore the denominator is 1000 not 1024.

You can get bit depth using Windows API Code Pack, but understand JPEG do not use a color map, so it consistently returns 24 / 3 = 8bpp.

http://metadataconsulting.blogspot.com/2020/05/How-to-iterate-over-image-music-and-video-properties-using-Microsoft-Windows-API-Code-Pack.html



Update - Remove LINQ dependency  use getting fast integer extraction

https://metadataconsulting.blogspot.com/2020/06/CSharp-Get-a-number-or-integer-from-a-string-fast-a-speed-comparison.html


        //Bits Per Pixel Number of Colors Available Common Name(s)
        //1 2 Monochrome
        //2 4 CGA
        //4 16 EGA
        //8 256 VGA
        //16 65536 XGA, High Color
        //24 16777216 SVGA, True Color
        //32 16777216 + Transparency  
        //48 281 Trillion
        //https://victoriaprice135050.wordpress.com/2015/04/30/investigate-bit-depth-sampling-bits-per-pixel-bpp-monochrome-256-high-colour-true-colour-2/

        //https://en.wikipedia.org/wiki/Tera-
      

        public static string ToBitsPerPixelDecimalReadableFormat(string numint)
        {
            string unit;
            double d;

            string firstnumber = new string(numint
                     .SkipWhile(x => !char.IsDigit(x))
                     .TakeWhile(x => char.IsDigit(x))
                     .ToArray());

            int pow = 0; //reqr'd -> cannot do a bitwise shift on a double
            int.TryParse(firstnumber , out pow);

            long abs = (pow < 0 ? -pow : pow);   //absolute value, we could use ulong if you know you will not get negative values

            if (pow == 0) return "1-bit";
            
            int bit = 1 << pow;
            abs = bit; 
            
            if (abs >= 0x1000000000000000) // Exa - quintillion
            {
                unit = "Quintn";
                d = (bit >> 50);
                d = (d / 1000);            // Divide by 1000 to get fractional value
            }
            else if (abs >= 0x4000000000000) // Peta - quadrillion
            {
                unit = "Quadn";
                d = (bit >> 40);
                d = (d / 1000);
            }
            else if (abs >= 0x10000000000) // Tera -  trillion
            {
                unit = "Trln";
                d = (bit >> 30);
                d = (d / 1000);
            }
            else if (abs >= 0x40000000) // Giga -  billion
            {
                unit = "Bn";
                d = (bit >> 20);
                d = (d / 1000);
            }
            else if (abs >= 0x100000) // Mega -  million
            {
                unit = "Mn";
                d = (bit >> 10);
                d = (d / 1000);
            }
            else if (abs >= 0x400) // Kilo -  thousand
            {
                unit = "Tn";
                d = bit;
                d = (d / 1000);
            }
            else
            {
                unit = "";
                d = bit;
            }

            return string.Concat(pow, "-bits(", d.ToString("0.##"), unit, "bpp)");


        }

Wednesday, May 27, 2020

CSharp - How to get either first row or last row of repeating values fast

Here's a design pattern to get either first or last row of a repeating field on multiple rows. In this example we'll extract the "File size" as a field to group on.

Internet media type                      : video/x-ms-wmv
File size                                : 372673
File size                                : 364 KiB
File size                                : 364 KiB
File size                                : 364 KiB
File size                                : 364 KiB
File size                                : 363.9 KiB
Duration                                 : 29166
Duration                                 : 29 s 166 ms
Duration                                 : 29 s 166 ms
Duration                                 : 29 s 166 ms
Duration                                 : 00:00:29.166
Duration                                 : 00:00:29:02
Duration                                 : 00:00:29.166 (00:00:29:02)
Overall bit rate                         : 102221
Overall bit rate                         : 102 kb/s
Maximum Overall bit rate                 : 103080
Maximum Overall bit rate                 : 103 kb/s

Result

Internet media type                      : video/x-ms-wmv
File size                                : 363.9 KiB
Duration                                 : 00:00:29.166 (00:00:29:02)
Overall bit rate                         : 102 kb/s
Maximum Overall bit rate                 : 103 kb/s

Here's a design pattern to get either 1st or last row of a repeating field, that is fast and does not rely on Linq library.

            string m = @"
Internet media type                      : video/x-ms-wmv
File size                                : 372673
File size                                : 364 KiB
File size                                : 364 KiB
File size                                : 364 KiB
File size                                : 364 KiB
File size                                : 363.9 KiB
Duration                                 : 29166
Duration                                 : 29 s 166 ms
Duration                                 : 29 s 166 ms
Duration                                 : 29 s 166 ms
Duration                                 : 00:00:29.166
Duration                                 : 00:00:29:02
Duration                                 : 00:00:29.166 (00:00:29:02)
Overall bit rate                         : 102221
Overall bit rate                         : 102 kb/s
Maximum Overall bit rate                 : 103080
Maximum Overall bit rate                 : 103 kb/s"; 
            
            string field = string.Empty;
            string prevfield = string.Empty;
            int idxsemi = 0;
            
            string[] linesIn = m.Split(new[] { Environment.NewLine },StringSplitOptions.None); 
            string[] linesOut = new string[linesIn.Length];
            
            int idxOut = linesIn.Length - 1;
            for (int i = linesIn.Length - 1; i >= 0; i--) //get last field in a repeating list
          //for (int i = 0; i < linesIn.Length; i++)     //get first field in a repeating list             
            {           
                idxsemi = linesIn[i].IndexOf(':');
                if (idxsemi > -1)
                    field = linesIn[i].Substring(0, idxsemi - 1); //field to dedup
                else
                    field = linesIn[i];

                if (prevfield == field)
                    continue;

                linesOut[idxOut--] = linesIn[i];
                
                prevfield = field;
                
            }

            string final = string.Join(Environment.NewLine, linesOut).TrimStart(); //gets rid of head empty lines
        }
        
    }
    



Monday, May 25, 2020

How to iterate over image, music and video properties using Microsoft Windows API Code Pack




















Here's how to iterate over SystemProperties.System.... properties to get metadata for an music, image or video file (there are many other filetypes as well) using Microsoft Windows API Code Pack

From MS SystemProperties.System documentation 
https://docs.microsoft.com/en-us/uwp/api/windows.storage.systemproperties?view=winrt-18362 we get following metadata categories to interrogate. 

SystemProperties.System.

TABLE 2
Audio
Gets an object that provides the indexing names of Windows file properties for System.Audio.
Author
Gets the name of the System.Author property (one of the Windows file properties.
Comment
Gets the name of the System.Comment property (one of the Windows file properties.
GPS
Gets an object that provides the indexing names of Windows system file properties for System.GPS.
Image
Gets an object that provides the indexing names of Windows file properties for System.Image.
ItemNameDisplay
Gets the name of the System.ItemNameDisplay property (one of the Windows file properties.
Keywords
Gets the name of the System.Keywords property (one of the Windows file properties.
Media
Gets an object that provides the indexing names of system media file properties such as System.Media.Duration.
Music
Gets an object that provides the indexing names of Windows file properties for System.Music.
Photo
Gets an object that provides the indexing names of Windows file properties for System.Photo.
Rating
Gets the name of the System.Rating property (one of the Windows file properties.
Title
Gets the name of the System.Title property (one of the Windows file properties.
Video
Gets an object that provides the indexing names of Windows file properties for System.Video.


There is not collection that works for all the above categories, so I had to implement the following. There is a default collection and for photo only.

Warning! This code does not sniff the properties of underlying type format.  So if you have an image that is webp, but mislabel as an jpg an imageisreallywebp.jpg or worse imageisreallywebp.webp (but is an exe) then this library will NOT detect that!

This code base is not maintained, and the BIT Depth for example is not reported accurately, test using samples here - https://etc.usf.edu/techease/win/images/what-is-bit-depth/

Code to iterate over Media Properties


Console.WriteLine("");
Console.WriteLine("SystemProperties.System.Media - Media Category Properties");
Console.WriteLine("");
string mediaProp = string.Empty;
PropertyInfo[] mediaPI = typeof(SystemProperties.System.Media).GetProperties();
foreach (PropertyInfo property in mediaPI)
{
 //https://docs.microsoft.com/en-us/windows/win32/properties/props-system-photo-aperture !@@@@@@@
 mediaProp = "System.Media." + property.Name; //@@@@@@@@@@@! This took  tooo long to figure out!
   
 //NOT -  mediaProp = "SystemProperties.System.Media"
 
 try
 {
  IShellProperty ishellprop = picture.Properties.GetProperty(mediaProp); //very slow

  if (ishellprop != null && ishellprop.ValueAsObject != null)
   Console.WriteLine(ishellprop.Name + "=" + ishellprop.ValueAsObject.ToString());

 }
 catch
 {
  continue;
 }

}