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.