#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0

    Using GDI to analyze image pixels


    Hi,
    I am not C programmer, however came here to ask if somebody of you have experience with programming of graphical programs which can analyse either what you see on screen or what is in bitmap. I program in AHK language and am starting to use GDI there. It should be similar how to use it there and here in C, but I stucked some problems in AHK so want to ask you what are your experience with it or if is it possible to solve the problem more efficiently with some other method I don't know about.

    What I wanted to do is a application which can analyze pixels from bitmap or screen to find out some shapes or areas of certain color, select that area and get it to image. I believe these must be some better way to do it because for example Photohop is pretty fast to perform similar actions. So I just dont know correct way. What I do is that I run a loop in AHK like this:

    obj := findRectXYLimits(0,400,65) ; define Integrals for search
    While ( c < 2073601)
    while (y<1080)
    {
    y++
    x:=0 ; reset x coordinate
    While ( x < 1920)
    {
    x++
    ; PixelGetColor, c, %x%, %y% ; get pixel from screen or:
    DllCall("gdiplus\GdipBitmapGetPixel", "uint", pImage, "int", x, "int", y, "uint*", ARGB); get pixel from image
    c:=DEC2HEX(ARGB); convert to HEX format
    count := findRectXYLimits( obj, x, y, c); compare coords with Integrals and save results.
    }
    }

    So this is nested loop for my max. screen resolution. On my computer which is 2.4GHz it takes 1 minute and 27 seconds to finish. If I skip the function calls it takes only 6 seconds. If I add GdipBitmapGetPixel call so it take 28 seconds. So 22 seconds to recieve the pixel color information. If I add convertion from decimal to hex format I increases to 45 seconds which is delay 16 seconds for this function. Then if I had my little function to compare coordinates with integrals, I got total delay of one minute and 27 seconds. Which is pretty much.

    So I ask if is it possible tu use GDI in more efficient way to detect whether pixels of certain color is in the image. Is it possible for example to define my own function and pass it to some GDI function to read and compare pixels from graphics? Also am interested if you know how to get better performance in C. Maybe I could use C program to get what I want.
  2. #2
  3. Contributed User
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Jun 2005
    Posts
    4,379
    Rep Power
    1871
    > DllCall("gdiplus\GdipBitmapGetPixel", "uint", pImage, "int", x, "int", y, "uint*", ARGB); get pixel from image
    If you can do this, then there is no reason why you couldn't do the same thing with a DLL you've written yourself.

    Eventually, you might migrate the whole code into the DLL (written in C or C++), but you could start with a simple step like for example returning a row of pixels into ARGB.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    Originally Posted by salem
    > DllCall("gdiplus\GdipBitmapGetPixel", "uint", pImage, "int", x, "int", y, "uint*", ARGB); get pixel from image
    If you can do this, then there is no reason why you couldn't do the same thing with a DLL you've written yourself.

    Eventually, you might migrate the whole code into the DLL (written in C or C++), but you could start with a simple step like for example returning a row of pixels into ARGB.
    Yep, but main thing what I ask is if will I get better performance by C or C++ language. If C has some other methods how do the same things faster. Yesterday I thought about the problem once again, with relation to Drawing programs like Gimp or Photoshop. E.g.Photoshop has such function where you can select certain color and you defain fuzz. You can select as many colors and add them into one group, which is selected.

    It looks like this:


    It does not mind if your image is 2000*2000 or 4000*4000 dimensions, it will simply select the area very fast, maybe in 0,001 s. I believe there must be different way to do it than to use loops and GdipBitmapGetPixel which results into few minutes proccess. But I think these graphical programs uses there own libraries. MayBe I should also do my own dll library, however I have no idea how to do it in C and what alghoritm use to detect pixels of some color. I have some formula which can find out weather color is in certain fuzz limit, but if I would apply the fomula with loops and GdipBitmapGetPixel so I got very bad delays. Do you think I can do dll in C which would be much faster that those approchs in AHK? I could for example pass into the dll, request for colors which I look for, fuzziness and shapes coordinates and the dll could return results (probably image) which I could read in AHK.
  6. #4
  7. Contributed User
    Devshed Specialist (4000 - 4499 posts)

    Join Date
    Jun 2005
    Posts
    4,379
    Rep Power
    1871
    Done right, C and C++ will give you the same level of performance as photoshop (which will for the most part have been written in C or C++).

    For one thing, it's easy enough to arrange for direct access to the whole image, rather than some pin-hole through an obstructive interface.
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper
  8. #5
  9. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    187
    Rep Power
    82
    A serious performance issue with the native GdipBitmapGetPixel function is that you have to call it for every pixel in your image. Using LockBits would give a substantial performance improvement since you only have to call it once for your image.

    A LockBits example can be found here
  10. #6
  11. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    Originally Posted by BobS0327
    A serious performance issue with the native GdipBitmapGetPixel function is that you have to call it for every pixel in your image. Using LockBits would give a substantial performance improvement since you only have to call it once for your image.[/URL]
    I am just starting to use C++ and Virtual Studio Express. I tried the first example but I got error. I probably need gdiplus.h where can I get it? Does msdn have it on the site? I probably need whole library. Where to download?
  12. #7
  13. No Profile Picture
    Contributing User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Oct 2012
    Posts
    187
    Rep Power
    82
    My bad. I should have been more specific when I pointed you to the link. But anyway, I was referring to the following code...

    Code:
        // test.cpp
        // Compile cl /CLR test.cpp
        #include <windows.h>
        #using <System.dll>
        #using <System.Drawing.dll>
        #using <System.Windows.Forms.dll>
        using namespace System;
        using namespace System::Drawing;
        using namespace System::Windows::Forms;
        public ref class myTest : public Form
        {
        private:
        PictureBox ^ pctBox;
        public:
        myTest()
        {
        InitializeComponent();
        }
        void InitializeComponent()
        {
        pctBox = gcnew PictureBox;
        pctBox->Location = Point(10, 10);
        pctBox->Size = System::Drawing::Size(1200, 1550);
        Controls->Add(pctBox);
        Bitmap^ pImage = gcnew Bitmap("c:\\temp\\my24bit.bmp", true);
        pctBox->Image = pImage;
        // Lock the bitmap's bits.
        System::Drawing::Rectangle rect = System::Drawing::Rectangle(0,0,pImage->Width,pImage->Height);
        System::Drawing::Imaging::BitmapData^ bmpData = pImage->LockBits( rect, System::Drawing::Imaging::ImageLockMode::ReadWrite, pImage->PixelFormat );
        // Get the address of the first line.
        IntPtr ptr = bmpData->Scan0;
        // Declare an array to hold the bytes of the bitmap.
        // This code is specific to a bitmap with 24 bits per pixels.
        int bytes = Math::Abs(bmpData->Stride) * pImage->Height;
        array<Byte>^rgbValues = gcnew array<Byte>(bytes);
        // Copy the RGB values into the array.
        System::Runtime::InteropServices::Marshal::Copy( ptr, rgbValues, 0, bytes );
        // Set every ninth value to 255.
        for ( int counter = 8; counter < rgbValues->Length; counter += 9 )
        rgbValues[ counter ] = 255;
        // Copy the RGB values back to the bitmap
        System::Runtime::InteropServices::Marshal::Copy( rgbValues, 0, ptr, bytes );
        // Unlock the bits.
        pImage->UnlockBits( bmpData );
        // Draw the modified image.
        pctBox->Image=pImage;
        }
        };
        int APIENTRY WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nCmdShow)
        {
        Application::Run(gcnew myTest());
        return 0;
        }
    Please note that it is compiled from the command line as follows:

    cl.exe /CLR test.cpp
  14. #8
  15. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    Edit: The command line compile command works now...
  16. #9
  17. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    Edit: The command line compile command works now...
    I have one more error:

    test6.cpp(3) : fatal error C1034: windows.h: no include path set
  18. #10
  19. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    the command line compilation does not work for me in Visual Studio, it says that include path is not set
  20. #11
  21. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    Someone helped me, so I think I can make it compile now. Your code works, I got some window with image opened.
  22. #12
  23. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Jan 2011
    Posts
    23
    Rep Power
    0
    Back with your sample code.

    I have few questions:

    First, how would you set pctBox->size to be same size as my image pctBox->Image when pctBox->Image was not defined yet. My image is momentally 800*600 so is it possible to set the box size later?

    I am a bit confused from the sample code you recommend me. There are not RGB values as I would expect them, three numbers which mean three components of the color. There is only one variable which can have simple number from 0 to 255. Not something like [0,0,0] or [255,255,255]... So how can you change color to get some other one like [22,55,200]?

    Also is there some method which can scan some row of lines? Or I must always set the lock first to the area which I want to read? What is better for performance? My idea was like this: if there were function to scan row y in the interval of x = [x1,x2] , then I could make function or class to detect pixels which are neighbours. I would first analyze first line, pixel by pixel to compare color of the actual pixel with the previous pixel. With a fuzz function I could detect if the color is same or similar to the color I look for. If it is similar then I will call this group of pixels for simplicity to explain: A1. Then I continue to search pixels. If the color is not the one I look for, there would be another group name prepared. So in the case that I found next color I am having group name A2. I continue reading and when I found same color I add it to A2 group. Same way for A1 - An. (C++ would realize it as array myarr[y][xgroup]) ...

    Same for next row. B1 mean group of pixels in second row, which create first row of pixels which is same color. B2 is next line same color and so on...

    Now when I would have all sets saved, I can start to loop the array and to compare pixels from the particular set/group, if it is similar to the next row next set. So here, if myarr[0][0][0][1][color] meanning x,y, is similar to myarr[1][0][0][0][color], then it means that group myarr[0][0] (first line, group 1) is same as myarr[1][0] (second line, group 1). So I move the array element into it. myarr[0][0][0] under myarr[1][0][1]. If it is not similar, then I go to compare next pixels from myarr[0][0]...

    Well I see problem, because if I don't have direct access to index in the format of myarr[1][x], where x means x position of the pixel, yeah that is problem. I mean, if I create array like this:
    e.g. I want to add color of pixel coord 1,25 ... into array myarr[x] . If I will add it I have myarr[1][0] not myarr[1][25] and that is the problem! Does it mean that I cannot access it directly but have to use loop and loop all values or at least first and last values to check if it is bigger or less value than the value I look for. That would mean performance problem. Instead of if I could access directly that pixel of x, then I could make it simply as myarr[1][25][color]....

    Any solution for this?

    As what I have described above, I ask if is there function to read whole line or I must just lock the bitmap as a rectangle 1 px height and width of image width.

    Also I ask if is there another function to read whole column directly, so for the case that I would want to look rectangle or some lines which touches them self like for instance, when you would look for big character "I" in the image, you would look for one vertical line, with the width w and height h. Then when you would add it to collection array and later then the primary search was made, you would continue in secondary comparison to detect weather the group A1 is in touch with B2 in exact position.... This would be harder function but also possible to determine a shape. So for the case that I would like to create such function, I ask if there is possible to do such things like to read the column of pixels directly. Or again, I must do it as in the example: before I look for pixels from the column x I must lock the image width 1 px, and height=pImage->height ... and then read the pixels from the image with a loop (so I read next pixels column). Then repeat the main loop and increase s by x++ ... so next column would be locked and read to read.

IMN logo majestic logo threadwatch logo seochat tools logo