Page 1 of 2

How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T01:37:46-07:00
by IvanShuvin
I need to precess each pixel on image:

Code: Select all

        MagickImage img = new MagickImage(@"d:\TEST\110706M01000509.jpg");
        PixelCollection pc = img.GetReadOnlyPixels(0, 0, img.Width, img.Height);

        for (int x = 0; x < pc.Width; x++)
            for(int y = 0; y < pc.Height; y++)
            {
                byte[] color = pc.GetValue(x,y);

                //SOME ACTIONS
            }
But, access to the color of pixels of the image(12000 X 16000) takes more than 5 minutes... How more quickly process all pixels of the image?

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T02:10:35-07:00
by snibgo
It may be much faster if you reverse x and y, so you process in rows, not columns.

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T05:31:06-07:00
by IvanShuvin
If the first loop by x, the second loop by y, I received next result:
start load -> 14:51:34
start loop -> 14:51:46
end loop -> 14:58:53


If the first loop by y, the second loop by x, I received next result:
start load -> 15:05:56
start loop -> 15:06:08
end loop -> 15:12:56


Improve absent. :(

Anymore ideas?

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T05:42:34-07:00
by magick
The slow down comes from accessing pixels from disk, one pixel at a time, in 1 thread. Try accessing one row @ a time:

Code: Select all

        MagickImage img = new MagickImage(@"d:\TEST\110706M01000509.jpg");

        for (int y = 0; x < pc.Height; y++)
        {
            PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);
            for(int x = 0; y < pc.Width; x++)
            {
               byte[] color = pc.GetValue(x,0);

                //SOME ACTIONS
            }
Does that help? You can get some timing information if you set the MAGICK_DEBUG environment variable to 'Cache'.

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T06:49:18-07:00
by IvanShuvin
Does that help?
No, I modified my code:

Code: Select all

            MagickImage img = new MagickImage(@"d:\TEST\brightness\tiles\32-1-470-109-25.tif");

            for (int y = 0; y < img.Height; y++)
            {
                PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);

                for (int x = 0; x < pc.Width; x++)
                {
                    byte[] color = pc.GetValue(x, 0);

                    //SOME ACTIONS
                }
            }
I received next result:
start load -> 16:28:38
start loop -> 16:28:40
end loop -> 16:36:13


Improve absent. :( :(
Maybe You have any other ideas?

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T07:55:14-07:00
by magick
Let's take .NET out of the equation. How long does this command take:
  • convert 2-1-470-109-25.tif -negate null:
That reads the TIFF image and passes over the pixels one row at a time, there is no output. It took 4.5 seconds on our Linux system (12000x16000 pixel image).

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-16T08:10:17-07:00
by dlemstra
What are you doing in '//SOME ACTIONS'? How quick is it when you don't do anything there?

Re: How to process each pixel of the image maximally quickly?

Posted: 2015-06-17T10:15:07-07:00
by IvanShuvin
What are you doing in '//SOME ACTIONS'?
Nothing. Source code run as is.
I found one the solve:

Code: Select all

            MagickImage img = new MagickImage(@"d:\TEST\brightness\tiles\32-1-470-109-25.tif");
            byte[] color = new byte[3];

            for (int y = 0; y < img.Height; y++)
            {
                PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);
                int chanelsCount = pc.Channels;
                var pcv = pc.GetValues();

                for (int x = 0; x < pcv.Length; x+=chanelsCount)
                {
                    color[0] = pcv[x];
                    color[1] += pcv[x + 1];
                    color[2] += pcv[x + 2];

                    //SOME ACTIONS
                }
            }
Result:
start load -> 20:05:54
start loop -> 20:05:59
end loop -> 20:06:06


But, the function .GetValues() in the version 7.0.0.0014 return only red channel, in the version 6.8.9.601 all correct.

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-16T16:14:20-07:00
by pablobhz
Sorry to revive this topic, but , any of you implemented this on the latest MagickNET release ? I'm getting a outofmemory exception.

Here's what i tried to do - i tried to adapt his original solution:

Code: Select all

        private static decimal inkGrayLevel(MagickImage img, bool inPercent = true)
        {
            decimal totalPixelValue = 0;
            decimal totalPixelValues = (img.Width * img.Height) * 255;
            //byte[] color = new byte[3];
            ushort[] color = new ushort[3];


                 PixelCollection pc = img.GetPixels();
                //PixelCollection pc = img.GetReadOnlyPixels(0, y, img.Width, 1);
                int chanelsCount = pc.Channels;
                var pcv = pc.GetValues();

                for (int x = 0; x < pcv.Length; x += chanelsCount)
                {
                    if (color[0] != color[1] || color[1] != color[2] || color[2] != color[3])
                    {
                        throw new ArgumentOutOfRangeException("Image is not grayscale");
                    }
                    color[0] = pcv[x];
                    color[1] += pcv[x + 1];
                    color[2] += pcv[x + 2];
                    totalPixelValue += color[0];

                //SOME ACTIONS
            }
            return (1 - (totalPixelValue / totalPixelValues)) * (inPercent ? 100 : 1);

        }
Thanks in advance

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-16T16:32:40-07:00
by snibgo
What version of IM?

You declare array color to have 3 elements but reference elements 0,1,2 and 3.

What are the values of relevant variables? Eg image width and height?

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-16T17:24:01-07:00
by pablobhz
Image Width: 461
Image Height: 7370
Yeah, i just noted that i declared the array with the wrong size. But this wouldn't affect my issue, i think.

What i want is a fast way to scan all image pixels using ImageMagick. The traditional way using System.Bitmap takes a minute or more for every image(and i'm scanning at least 20 of those).

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-17T00:06:13-07:00
by dlemstra
What is the goal of your scan action? It might be possible that you could do it with a build in operation.

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-17T04:10:47-07:00
by pablobhz
I need to scan each pixel of the image in order to get the BitMap Gray level.

In the end, i created a function to do the job:

Code: Select all

        private static decimal GetGrayLevel(Bitmap input, bool inPercent = true)
        {
            decimal totalPixelValue = 0;
            decimal totalPixelValues = (input.Width * input.Height) * 255;

            for (int iy = 0; iy < input.Height; iy++)
            {
                for (int i = 0; i < input.Width; i++)
                {
                    Color clr = input.GetPixel(i, iy);
                    if (clr.R != clr.G || clr.R != clr.B || clr.B != clr.G)
                    {
                        throw new ArgumentOutOfRangeException("Image is not grayscale");
                    }
                    totalPixelValue += clr.R;
                }
            }
            return (1 - (totalPixelValue / totalPixelValues)) * (inPercent ? 100 : 1);
        }
However, since i'm scanning 23 images (461w x 7370h), it takes something like 30 seconds to do the job. And i want to reduce this time.

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-17T09:09:17-07:00
by snibgo
I don't use Magick.NET, but I notice you use "input.GetPixel(i, iy)" at every pixel.

Above, IvanShuvin found a much quicker method was to use "var pcv = pc.GetValues();" at each y, then simply access elements of the pcv array at each x. So the call to ImageMagick is made once per row, instead of once per pixel.

Re: How to process each pixel of the image maximally quickly?

Posted: 2017-02-20T08:21:52-07:00
by pablobhz
Just tried IvanShuvin solution.

Code: Select all

        private static decimal inkGrayLevel(MagickImage img, bool inPercent = true)
        {
            decimal totalPixelValue = 0;
            decimal totalPixelValues = (img.Width * img.Height) * 255;
            int[] color = new int[3];
            for(int y=0; y < img.Height; y++)
            {
                PixelCollection pc = img.GetPixels();
                int channelsCount = pc.Channels;
                var pcv = pc.GetValues();
                for(int x = 0; x < pcv.Length; x += channelsCount)
                {
                    color[0] = (pcv[x]/255) -2;
                    color[1] += (pcv[x + 1]/255)-2;
                    color[2] += (pcv[x + 2]/255)-2;
                    

                   // if (color[0] != color[1] || color[0] != color[2] || color[2] != color[1])
                   // {
                  //      throw new ArgumentOutOfRangeException("Image is not grayscale");
                  //  }
                    totalPixelValue += color[0];
                } 
            }  
            return (1 - (totalPixelValue / totalPixelValues)) * (inPercent ? 100 : 1);
        }
To be honest, it is taking longer than my previous solution.
Doing those operations on a image 460w x 7370h. One minute now and going so far...
Also, i had to remove the conditional to detect if image wasn't grayscale. Sometimes pixels would return 510 on their values (after dividing by 255).
I can remove my division, since i only need to compare if they're different. However, i think this will have influence on my final results.