If you can assume the detection target area is white and its shape is (non-rotated) rectangle, simply doing labeling will achieve your detection (because of the assumption : all found white region should be rectangle).
You say you can access pixel data, so, you can implement simple labeling process for this purpose.
Very simple sample code:
#include <iostream>
#include <vector>
struct HorizontalLineSeg
{
int left, right, y;
HorizontalLineSeg( int left=0, int right=0, int y=0 )
: left(left), right(right), y(y)
{}
};
struct RectReg
{
HorizontalLineSeg Top, Bottom;
RectReg( const HorizontalLineSeg &Top ) : Top(Top),Bottom(Top) {}
bool ConnectToBottom( const HorizontalLineSeg &Seg )
{
if( Bottom.y+1 != Seg.y )return false;
if( Seg.right < Bottom.left || Bottom.right < Seg.left )return false;
Bottom = Seg;
return true;
}
};
void Update( std::vector<RectReg> &Regs, const HorizontalLineSeg &FoundSeg )
{
for( auto &Reg : Regs )
{
if( Reg.ConnectToBottom( FoundSeg ) )return;
}
Regs.emplace_back( FoundSeg);
}
int main()
{
//- Load Image as GrayScale
cv::Mat Img = cv::imread( "WhiteRects.png", cv::IMREAD_GRAYSCALE );
if( Img.empty() ){ std::cout << "imread() failed" << std::endl; return 0; }
//- Find white regions
std::vector<RectReg> Regs;
{
const unsigned char Thresh = 128;
for( int y=0; y<Img.rows; ++y )
{
const unsigned char *p = Img.ptr<unsigned char>( y );
int FoundLeft = -1;
for( int x=0; x<Img.cols; ++x, ++p )
{
if( *p >= Thresh )
{
if( FoundLeft<0 )FoundLeft = x;
}
else if( FoundLeft >= 0 )
{
Update( Regs, HorizontalLineSeg( FoundLeft, x-1, y ) );
FoundLeft = -1;
}
}
}
}
//- Visualize result
cv::Mat ShowImg = Img * 0.35;
if( !Regs.empty() )
{
std::vector< std::vector<cv::Point> > Pts;
Pts.reserve( Regs.size() );
for( const auto &Reg : Regs )
{
Pts.push_back(
{
{ Reg.Top.left, Reg.Top.y },
{ Reg.Top.right, Reg.Top.y },
{ Reg.Bottom.right, Reg.Bottom.y },
{ Reg.Bottom.left, Reg.Bottom.y }
}
);
}
cv::polylines( ShowImg, Pts, true, cv::Scalar(255) );
}
std::cout << Regs.size() << " regs found" << std::endl;
cv::imshow( "Result", ShowImg );
cv::waitKey();
return 0;
}
Added code that uses OpenCV functions because it seems to be criticized for not using OpenCV functions.
int main()
{
//- Load Image as GrayScale
cv::Mat Img = cv::imread( "WhiteRects.png", cv::IMREAD_GRAYSCALE );
if( Img.empty() ){ std::cout << "imread() failed" << std::endl; return 0; }
//- Main process with cv::connectedComponentsWithStats()
cv::Mat Stats;
int N = 0;
{
//- Pre-Binalize with OpenCV's function.
// Purpose of this is just to set the background value to 0.
// So, if you know the backgroud value is already 0, you can omit this step.
cv::Mat BinImg;
cv::threshold( Img, BinImg, 128, 255, cv::THRESH_BINARY );
//- Use connectedComponentsWithStats()
cv::Mat Labels; //Only needed to use the function.
cv::Mat Centroids; //Only needed to use the function. Or you can use this if you want.
N = cv::connectedComponentsWithStats( BinImg, Labels, Stats, Centroids );
}
//- Visualize result
cv::Mat ShowImg = Img * 0.35;
for( int i=1; i<N; ++i ) //be careful not to include 0
{
const int *pS = Stats.ptr<int>( i );
cv::rectangle(
ShowImg,
cv::Rect(
cv::Point{ pS[ cv::CC_STAT_LEFT ], pS[ cv::CC_STAT_TOP ] },
cv::Size{ pS[ cv::CC_STAT_WIDTH ], pS[ cv::CC_STAT_HEIGHT ] }
),
cv::Scalar(255)
);
}
std::cout << N-1 << " regs found" << std::endl; //not N
cv::imshow( "Result", ShowImg );
cv::waitKey();
return 0;
}