Hi guys, I am trying to work on something where I set an image in a picturebox and then use getpixel to scan the whole image and find the percentage of the colour I am interested in, in that image.

The problem is that if the image is large then the program hangs, it only works for count of co-ordinates upto (50,50) even then it takes a few seconds to write the results in the text box. I have read on LockBits method but I dont understand how to use it properly on the picturebox as I am new to C++. Can anyone give me an example on how to go about doing this or are there any better alternatives as I will be using this program on microscope images which have a pretty high resolution probably around 1080p or more ?

` int RequiredCount, nonRequiredCount, CountPerc;

Image^ i = pictureBox1->Image;  

Bitmap^ b = gcnew Bitmap(i);  

For (int y = 0; y < (pictureBox1.Height - 1), y++)
{
    For (int x = 0; x < (pictureBox1.Width - 1), x++)
        {

            Color c = b->GetPixel(x,y);
            int k, j, z;

            k = c.R;
            j = c.G;
            z = c.B;

            if (k <= 75 && j <= 75 && z <= 75) 
             {
                    this->txt1->Text = this->txt1->Text + " | " + c.R.ToString() + "-" + c.G.ToString() + "-" + c.B.ToString(); 
                    RequiredCount = RequiredCount + 1;
              }
            else
              {
                    nonRequiredCount = nonRequiredCount + 1;
              }

            CountPerc = (RequiredCount / nonRequiredCount) * 100;
            this->btnWork->Text = CountPerc.ToString();


        }

} `

Thank you.

Dani AI

Generated

Several concrete problems in the original post make the scan both slow and incorrect. The for lines use commas instead of semicolons (logic bug), the counters are not shown initialized, the percentage is computed as (RequiredCount / nonRequiredCount) * 100 (wrong — should use total pixels and a floating-point divide), and the code updates the textbox and concatenates strings inside the inner loop (huge overhead). Also, calling GetPixel per pixel has large managed-call overhead and will not scale to megapixel microscope images.

A practical, fast approach is: convert the image to a known pixel format (24bpp or 32bpp), call LockBits, iterate the bitmap memory with a native pointer using the Stride and BytesPerPixel, count matches, then UnlockBits and compute percentage once. Do the heavy work on a background thread and only marshal the final result back to the UI. This avoids repeated managed calls, prevents UI freezes, and eliminates expensive per-pixel UI/string work. pointed to CodeProject resources for patterns; is correct that native GDI is an option but requires more plumbing; gave a useful LockBits example — the snippet below shows an alternative using direct pointer access (no Marshal::Copy).

// C++/CLI (example sketch)
Bitmap^ src = safe_cast<Bitmap^>(pictureBox1->Image);
int w = src->Width, h = src->Height;

// ensure simple pixel format (create a temporary if needed)
Bitmap^ bmp = (src->PixelFormat == PixelFormat::Format24bppRgb || src->PixelFormat == PixelFormat::Format32bppArgb) ? src
    : gcnew Bitmap(w, h, PixelFormat::Format24bppRgb);
if (bmp != src) { Graphics^ g = Graphics::FromImage(bmp); g->DrawImage(src, 0, 0, w, h); delete g; }

auto data = bmp->LockBits(Rectangle(0,0,w,h), ImageLockMode::ReadOnly, bmp->PixelFormat);
unsigned char* base = reinterpret_cast<unsigned char*>(data->Scan0.ToPointer());
int stride = data->Stride;
int bpp = System::Drawing::Image::GetPixelFormatSize(bmp->PixelFormat) / 8;
long long matches = 0, total = (long long)w * h;

for (int y = 0; y < h; ++y) {
  unsigned char* row = base + y * stride;
  for (int x = 0; x < w; ++x) {
    unsigned char* p = row + x * bpp;
    unsigned char B = p[0], G = p[1], R = p[2];
    if (R <= 75 && G <= 75 && B <= 75) ++matches;
  }
}

bmp->UnlockBits(data);
double percent = (double)matches * 100.0 / (double)total;

Additional tips: check Stride (can be negative for top-down bitmaps), choose RGB vs HSV thresholds for better color matching, avoid building large strings inside the loop (use StringBuilder or update UI once), consider downsampling or scanning every Nth pixel for a quick estimate, and consider OpenCV or Parallel::For for very large images. This approach fixes the logic bugs in the original post and keeps processing time and memory overhead reasonable for high-resolution images.

Recommended Answers

All 3 Replies

Check out some of these articles. Most are for C# but they should apply to CLR/C++ as well. You should bookmark that site because they have the largest repository of free code/examples on the net for MS-Windows programming.

You can use these GDI API functions:

GetObject
GetDIBits

They are faster than GetPixel but entails more work than the latter.

I have read on LockBits method but I dont understand how to use it properly on the picturebox as I am new to C++. Can anyone give me an example on how to go about doing this

An example of using LockBits.....

// 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;
}
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.