Wednesday, September 14, 2005

 

Some notes on OpenCV - 4

1.
OTSU方法计算图像二值化的自适应阈值
From http://blog.csdn.net/hunnish/archive/2004/09/02/92086.aspx

/*
OTSU 算法可以说是自适应计算单阈值(用来转换灰度图像为二值图像)的简单高效方法。下面的代码最早由 Ryan Dibble提供,此后经过多人Joerg.Schulenburg, R.Z.Liu 等修改,补正。

转自:http://forum.assuredigit.com/display_topic_threads.asp?ForumID=8&TopicID=3480

算法对输入的灰度图像的直方图进行分析,将直方图分成两个部分,使得两部分之间的距离最大。划分点就是求得的阈值。

parameter: *image --- buffer for image
rows, cols --- size of image
x0, y0, dx, dy --- region of vector used for computing threshold
vvv --- debug option, is 0, no debug information outputed
*/
/*======================================================================*/
/* OTSU global thresholding routine */
/* takes a 2D unsigned char array pointer, number of rows, and */
/* number of cols in the array. returns the value of the threshold */
/*======================================================================*/
int otsu (unsigned char *image, int rows, int cols, int x0, int y0, int dx, int dy, int vvv)
{

unsigned char *np; // 图像指针
int thresholdValue=1; // 阈值
int ihist[256]; // 图像直方图,256个点

int i, j, k; // various counters
int n, n1, n2, gmin, gmax;
double m1, m2, sum, csum, fmax, sb;

// 对直方图置零...
memset(ihist, 0, sizeof(ihist));

gmin=255; gmax=0;
// 生成直方图
for (i = y0 + 1; i < y0 + dy - 1; i++) {
np = &image[i*cols+x0+1];
for (j = x0 + 1; j < x0 + dx - 1; j++) {
ihist[*np]++;
if(*np > gmax) gmax=*np;
if(*np < gmin) gmin=*np;
np++; /* next pixel */
}
}

// set up everything
sum = csum = 0.0;
n = 0;

for (k = 0; k <= 255; k++) {
sum += (double) k * (double) ihist[k]; /* x*f(x) 质量矩*/
n += ihist[k]; /* f(x) 质量 */
}

if (!n) {
// if n has no value, there is problems...
fprintf (stderr, "NOT NORMAL thresholdValue = 160\n");
return (160);
}

// do the otsu global thresholding method
fmax = -1.0;
n1 = 0;
for (k = 0; k < 255; k++) {
n1 += ihist[k];
if (!n1) { continue; }
n2 = n - n1;
if (n2 == 0) { break; }
csum += (double) k *ihist[k];
m1 = csum / n1;
m2 = (sum - csum) / n2;
sb = (double) n1 *(double) n2 *(m1 - m2) * (m1 - m2);
/* bbg: note: can be optimized. */
if (sb > fmax) {
fmax = sb;
thresholdValue = k;
}
}

// at this point we have our thresholding value

// debug code to display thresholding values
if ( vvv & 1 )
fprintf(stderr,"# OTSU: thresholdValue = %d gmin=%d gmax=%d\n",
thresholdValue, gmin, gmax);

return(thresholdValue);
}

2.
彩色图像分割的FLOOD FILL方法(源代码)
From http://blog.csdn.net/hunnish/archive/2004/10/11/131783.aspx

下面是OPENCV B4.0 附带的 FLOOD FILL 算法的源代码样例,可以实现简单的彩色图像分割。

#ifdef _CH_
#pragma package
#endif

#ifndef _EiC
#include "cv.h"
#include "highgui.h"
#include
#include
#endif

IplImage* color_img0;
IplImage* mask;
IplImage* color_img;
IplImage* gray_img0 = NULL;
IplImage* gray_img = NULL;
int ffill_case = 1;
int lo_diff = 20, up_diff = 20;
int connectivity = 4;
int is_color = 1;
int is_mask = 0;
int new_mask_val = 255;

void on_mouse( int event, int x, int y, int flags )
{
if( !color_img )
return;

switch( event )
{
case CV_EVENT_LBUTTONDOWN:
{
CvPoint seed = cvPoint(x,y);
int lo = ffill_case == 0 ? 0 : lo_diff;
int up = ffill_case == 0 ? 0 : up_diff;
int flags = connectivity + (new_mask_val << 8) +
(ffill_case == 1 ? CV_FLOODFILL_FIXED_RANGE : 0);
int b = rand() & 255, g = rand() & 255, r = rand() & 255;
CvConnectedComp comp;

if( is_mask )
cvThreshold( mask, mask, 1, 128, CV_THRESH_BINARY );

if( is_color )
{
CvScalar color = CV_RGB( r, g, b );
cvFloodFill( color_img, seed, color, CV_RGB( lo, lo, lo ),
CV_RGB( up, up, up ), &comp, flags, is_mask ? mask : NULL );
cvShowImage( "image", color_img );
}
else
{
CvScalar brightness = cvRealScalar((r*2 + g*7 + b + 5)/10);
cvFloodFill( gray_img, seed, brightness, cvRealScalar(lo),
cvRealScalar(up), &comp, flags, is_mask ? mask : NULL );
cvShowImage( "image", gray_img );
}

printf("%g pixels were repainted\n", comp.area );

if( is_mask )
cvShowImage( "mask", mask );
}
break;
}
}

int main( int argc, char** argv )
{
char* filename = argc >= 2 ? argv[1] : (char*)"fruits.jpg";

if( (color_img0 = cvLoadImage(filename,1)) == 0 )
return 0;

printf( "Hot keys: \n"
"\tESC - quit the program\n"
"\tc - switch color/grayscale mode\n"
"\tm - switch mask mode\n"
"\tr - restore the original image\n"
"\ts - use null-range floodfill\n"
"\tf - use gradient floodfill with fixed(absolute) range\n"
"\tg - use gradient floodfill with floating(relative) range\n"
"\t4 - use 4-connectivity mode\n"
"\t8 - use 8-connectivity mode\n" );

color_img = cvCloneImage( color_img0 );
gray_img0 = cvCreateImage( cvSize(color_img->width, color_img->height), 8, 1 );
cvCvtColor( color_img, gray_img0, CV_BGR2GRAY );
gray_img = cvCloneImage( gray_img0 );
mask = cvCreateImage( cvSize(color_img->width + 2, color_img->height + 2), 8, 1 );

cvNamedWindow( "image", 0 );
cvCreateTrackbar( "lo_diff", "image", &lo_diff, 255, NULL );
cvCreateTrackbar( "up_diff", "image", &up_diff, 255, NULL );

cvSetMouseCallback( "image", on_mouse );

for(;;)
{
int c;

if( is_color )
cvShowImage( "image", color_img );
else
cvShowImage( "image", gray_img );

c = cvWaitKey(0);
switch( c )
{
case '\x1b':
printf("Exiting ...\n");
goto exit_main;
case 'c':
if( is_color )
{
printf("Grayscale mode is set\n");
cvCvtColor( color_img, gray_img, CV_BGR2GRAY );
is_color = 0;
}
else
{
printf("Color mode is set\n");
cvCopy( color_img0, color_img, NULL );
cvZero( mask );
is_color = 1;
}
break;
case 'm':
if( is_mask )
{
cvDestroyWindow( "mask" );
is_mask = 0;
}
else
{
cvNamedWindow( "mask", 0 );
cvZero( mask );
cvShowImage( "mask", mask );
is_mask = 1;
}
break;
case 'r':
printf("Original image is restored\n");
cvCopy( color_img0, color_img, NULL );
cvCopy( gray_img0, gray_img, NULL );
cvZero( mask );
break;
case 's':
printf("Simple floodfill mode is set\n");
ffill_case = 0;
break;
case 'f':
printf("Fixed Range floodfill mode is set\n");
ffill_case = 1;
break;
case 'g':
printf("Gradient (floating range) floodfill mode is set\n");
ffill_case = 2;
break;
case '4':
printf("4-connectivity mode is set\n");
connectivity = 4;
break;
case '8':
printf("8-connectivity mode is set\n");
connectivity = 8;
break;
}
}

exit_main:

cvDestroyWindow( "test" );
cvReleaseImage( &gray_img );
cvReleaseImage( &gray_img0 );
cvReleaseImage( &color_img );
cvReleaseImage( &color_img0 );
cvReleaseImage( &mask );

return 1;
}

#ifdef _EiC
main(1,"ffilldemo.c");
#endif

3.
单通道图像的直方图(C/C++源代码)
From http://blog.csdn.net/hunnish/archive/2004/10/13/134501.aspx

计算并绘制单通道图像的直方图。在MATLAB中绘制直方图是一件非常简单的事情,可是到了C环境下,竟然变成了一个问题。各种实现方法都有,而且要自己动手重新编程。幸好有了OPENCV。下面的代码要求OPENCV4.0的支持,并在VC6中编译通过。

转自阿须数码

//
// 对单通道图像做直方图
//

#include "cv.h"
#include "highgui.h"
#include
#include

int main( int argc, char** argv )
{
IplImage *src = 0;
IplImage *histimg = 0;
CvHistogram *hist = 0;

int hdims = 50; // 划分HIST的个数,越高越精确
float hranges_arr[] = {0,255};
float* hranges = hranges_arr;
int bin_w;
float max_val;
int i;

if( argc != 2 || (src=cvLoadImage(argv[1], 0)) == NULL) // force to gray image
return -1;

cvNamedWindow( "Histogram", 1 );
hist = cvCreateHist( 1, &hdims, CV_HIST_ARRAY, &hranges, 1 ); // 计算直方图
histimg = cvCreateImage( cvSize(320,200), 8, 3 );

cvZero( histimg );

cvCalcHist( &src, hist, 0, 0 ); // 计算直方图
cvGetMinMaxHistValue( hist, 0, &max_val, 0, 0 ); // 只找最大值
cvConvertScale( hist->bins, hist->bins, max_val ? 255. / max_val : 0., 0 ); // 缩放 bin 到区间 [0,255]

cvZero( histimg );
bin_w = histimg->width / hdims; // hdims: 条的个数,则 bin_w 为条的宽度

// 画直方图
for( i = 0; i < hdims; i++ )
{
double val = ( cvGetReal1D(hist->bins,i)*histimg->height/255 );
CvScalar color = CV_RGB(255,255,0); //(hsv2rgb(i*180.f/hdims);
cvRectangle( histimg, cvPoint(i*bin_w,histimg->height),
cvPoint((i+1)*bin_w,(int)(histimg->height - val)),
color, 1, 8, 0 );
}

cvShowImage( "Histogram", histimg );
cvWaitKey(0);

cvDestroyWindow("Histogram");
cvReleaseImage( &src );
cvReleaseImage( &histimg );
cvReleaseHist ( &hist );

return 0;
}

4.
数字图像的直方图均衡化(C/C++源代码)
From http://blog.csdn.net/hunnish/archive/2004/10/14/136003.aspx

数字图像的直方图均衡化是常用的图像增强方法,因为均衡化是自动完成的,无需人工干预,而且常常得到比较满意的结果。下面的程序是利用OPENCV提供的函数,实现这个功能。需要OPENCV B4.0的支持,在VC6下编译通过。

//
// perform histgram equalization for single channel image
// AssureDigit Sample code
//


#include "cv.h"
#include "highgui.h"

#define HDIM 256 // bin of HIST, default = 256

int main( int argc, char** argv )
{
IplImage *src = 0, *dst = 0;
CvHistogram *hist = 0;

int n = HDIM;
double nn[HDIM];
uchar T[HDIM];
CvMat *T_mat;

int x;
int sum = 0; // sum of pixels of the source image 图像中象素点的总和
double val = 0;

if( argc != 2 || (src=cvLoadImage(argv[1], 0)) == NULL) // force to gray image
return -1;

cvNamedWindow( "source", 1 );
cvNamedWindow( "result", 1 );

// calculate histgram 计算直方图
hist = cvCreateHist( 1, &n, CV_HIST_ARRAY, 0, 1 );
cvCalcHist( &src, hist, 0, 0 );

// Create Accumulative Distribute Function of histgram
val = 0;
for ( x = 0; x < n; x++)
{
val = val + cvGetReal1D (hist->bins, x);
nn[x] = val;
}

// Compute intensity transformation 计算变换函数的离散形式
sum = src->height * src->width;
for( x = 0; x < n; x++ )
{
T[x] = (uchar) (255 * nn[x] / sum); // range is [0,255]
}

// Do intensity transform for source image
dst = cvCloneImage( src );
T_mat = cvCreateMatHeader( 1, 256, CV_8UC1 );
cvSetData( T_mat, T, 0 );
// directly use look-up-table function 直接调用内部函数完成 look-up-table 的过程
cvLUT( src, dst, T_mat );

cvShowImage( "source", src );
cvShowImage( "result", dst );
cvWaitKey(0);

cvDestroyWindow("source");
cvDestroyWindow("result");
cvReleaseImage( &src );
cvReleaseImage( &dst );
cvReleaseHist ( &hist );

return 0;
}

5.
图像灰度值调整(C/C++源代码)
From http://blog.csdn.net/hunnish/archive/2004/09/23/114398.aspx

图像的象素值变换,包括亮度、对比度和GAMMA校正算法,环境是OPENCV4.0,VC6.0。算法参考了MATLAB函数 imadjust 。

//
// perform histgram equalization for single channel image
//

#include "cv.h"
#include "highgui.h"

/*
Reference for correspondent MATLAB function: imadjust
IMADJUST Adjust image intensity values or colormap.
J = IMADJUST(I,[LOW_IN HIGH_IN],[LOW_OUT HIGH_OUT],GAMMA) maps the
values in intensity image I to new values in J such that values between
LOW_IN and HIGH_IN map to values between LOW_OUT and HIGH_OUT. Values
below LOW_IN and above HIGH_IN are clipped; that is, values below LOW_IN
map to LOW_OUT, and those above HIGH_IN map to HIGH_OUT. You can use an
empty matrix ([]) for [LOW_IN HIGH_IN] or for [LOW_OUT HIGH_OUT] to
specify the default of [0 1]. GAMMA specifies the shape of the curve
describing the relationship between the values in I and J. If GAMMA is
less than 1, the mapping is weighted toward higher (brighter) output
values. If GAMMA is greater than 1, the mapping is weighted toward lower
(darker) output values. If you omit the argument, GAMMA defaults to 1
(linear mapping).

Note that if HIGH_OUT < LOW_OUT, the output image is reversed, as in a
photographic negative.
====
src and dst are grayscale, 8-bit images;
Default input value:
[low, high] = [0,1];
[bottom, top] = [0,1];
gamma = 1;
if adjust successfully, return 0, otherwise, return non-zero.
Author: R.Z.Liu, 18/09/04
====
*/
int ImageAdjust(IplImage* src, IplImage* dst,
double low, double high, // low and high are the intensities of src
double bottom, double top, // mapped to bottom and top of dst
double gamma )
{
double low2 = low*255;
double high2 = high*255;
double bottom2 = bottom*255;
double top2 = top*255;
double err_in = high2 - low2;
double err_out = top2 - bottom2;

int x,y;
double val;

if( low<0 && low>1 && high <0 && high>1 && bottom<0 && bottom>1 && top<0 && top>1)
return 1;

// intensity transform
for( y = 0; y < src->height; y++)
{
for (x = 0; x < src->width; x++)
{
val = ((uchar*)(src->imageData + src->widthStep*y))[x];
val = pow((val - low2)/err_in, gamma) * err_out + bottom2;
if(val>255) val=255; if(val<0) val=0; // Make sure src is in the range [low,high]
((uchar*)(dst->imageData + dst->widthStep*y))[x] = (uchar) val;
}
}
return 0;
}

效率实在太低,只是表明算法的思想



<< Home

This page is powered by Blogger. Isn't yours?