OpenCV中cvRNG基本隨機數
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
http://hi.baidu.com/bingshanzhu/blog/item/fefccce7d817af3fb938208d.html
#include "stdafx.h"#include "cv.h" #include "highgui.h"#include <stdlib.h>#include <stdio.h>#pragma comment(lib,"cv200.lib")#pragma comment(lib,"cxcore200.lib")#pragma comment(lib,"highgui200.lib")#pragma comment(lib,"cvaux200.lib")//隨機數簡單示例int _tmain(int argc, _TCHAR* argv[]){ CvRNG rng; rng= cvRNG(cvGetTickCount()); for (int i= 0;i<10;i++) { printf("%d/n",cvRandInt(&rng)%256);//如果%256出來的將會是0~255的正整數 printf("%.2f/n",cvRandReal(&rng)); } printf("Tick Frequency= %f/n",cvGetTickFrequency()); system("pause" ); return 0;}
cvGetTickCount()
cvGetTickFrequency() 返回系統時鐘頻率
cvRNG() 跟一般的C語言srand()使用方法一樣,要先給它一個種子,但srand()用到的是unsigned int的32位種子範圍,而cvRNG()用的是64位長整數種子。初始化CvRNG資料結構,假如seed給0,它將會自動轉成-1。
cvRandInt() 返回均勻分佈32位的隨機數,均勻分佈為統計學上的專有名詞,表示長時間下所有數字出現的概率都是一樣的,而cvRandint()在opencv裡使用的公式 temp = (uint64)(unsigned)temp*1554115554 + (temp >> 32); 這個公式的名稱叫Multiply-with-carry (MWC) generator,有興趣的話可以在網路上找“隨機數產生器”,Multiply-with-carry是將64位的種子去產生32位的隨機數。
cvRandReal() 返回均勻分佈,0~1之間的隨機小數,cvRandReal()的公式則是用 |
// Xoo.cpp : Defines the entry point for the console application. #include "cv.h" #include "highgui.h" int main( int argc, char** argv ) { // 結構中載入影象:影象也是BMP影象(cvLoadImage)或者其它格式 // XML/YAML (cvLoad) CvImage img(argc > 1 ? argv[1] : "lena.jpg", 0, CV_LOAD_IMAGE_COLOR), img_yuv, y, noise; // RNG //初始化隨機數生成器狀態 //CvRNG cvRNG( int64 seed=-1 ); //seed //64-bit 的值用來初始化一個隨機序列 //函式 cvRNG 初始化隨機數生成器並返回其狀態。 //指向這個狀態的指標可以傳遞給函式 cvRandInt, //cvRandReal 和 cvRandArr . //在通常的實現中使用一個 multiply-with-carry generator 。 CvRNG rng = cvRNG(-1); if( !img.data() ) // 檢查影象是否被載入 return -1; img_yuv = img.clone(); // 克隆影象 // 色彩空間轉換 //void cvCvtColor( const CvArr* src, CvArr* dst, int code ); //src==source //dst=destination //int code==轉換的具體格式 cvCvtColor( img, img_yuv, CV_BGR2YCrCb ); // 建立影象 //CvImage::create //void CvImage::create(CvSize size, int depth, int channels); //建立一個影象。 //size //影象大小 //depth //畫素深度 //channels //通道數 y.create( img.size(), IPL_DEPTH_8U, 1 ); noise.create( img.size(), IPL_DEPTH_32F, 1 ); //cvSplit 函式,分解影象到單個色彩通道上,然後單獨處理。 //void cvSplit( const CvArr* src, CvArr* dst0, CvArr* dst1, // CvArr* dst2, CvArr* dst3 ); //#define cvCvtPixToPlane cvSplit //src //原陣列. //dst0...dst3 //目標通道 //函式 cvSplit 分割多通道陣列成分離的單通道陣列d。 //可獲得兩種操作模式 . 如果原陣列有N通道且前N輸出陣列非NULL, //所有的通道都會被從原陣列中提取,如果前N個通道只有一個通道非NULL函式只提取該指定通道, //否則會產生一個錯誤,餘下的通道(超過前N個通道的以上的)必須被設定成NULL, //對於設定了COI的IplImage 結使用cvCopy 也可以從影象中提取單通道。 cvSplit( img_yuv, y, 0, 0, 0 ); // 正態分佈的隨機陣列 // 用隨機數填充陣列並更新 RNG 狀態 //void cvRandArr( CvRNG* rng, CvArr* arr, int dist_type, CvScalar param1, CvScalar param2 ); //rng //被 cvRNG 初始化的 RNG 狀態. //arr //輸出陣列 //dist_type //分佈型別: //CV_RAND_UNI - 均勻分佈 //CV_RAND_NORMAL - 正態分佈 或者 高斯分佈 //param1 //分佈的第一個引數。如果是均勻分佈它是隨機數範圍的閉下邊界。如果是正態分佈它是隨機數的平均值。 //param2 //分佈的第二個引數。如果是均勻分佈它是隨機數範圍的開上邊界。如果是正態分佈它是隨機數的標準差。 //函式 cvRandArr 用均勻分佈的或者正態分佈的隨機數填充輸出陣列。 //在下面的例子中該函式被用來新增一些正態分佈的浮點數到二維陣列的隨機位置。 cvRandArr( &rng, noise, CV_RAND_NORMAL, cvScalarAll(0), cvScalarAll(20) ); //各種方法的影象平滑 //void cvSmooth( const CvArr* src, CvArr* dst, // int smoothtype=CV_GAUSSIAN, // int param1=3, int param2=0, double param3=0, double param4=0 ); //src //輸入影象. //dst //輸出影象. //smoothtype //平滑方法: //CV_BLUR_NO_SCALE (簡單不帶尺度變換的模糊) - 對每個象素的 param1×param2 領域求和。 //如果鄰域大小是變化的,可以事先利用函式 cvIntegral 計算積分影象。 //CV_BLUR (simple blur) - 對每個象素param1×param2鄰域 求和並做尺度變換 1/(param1•param2). //CV_GAUSSIAN (gaussian blur) - 對影象進行核大小為 param1×param2 的高斯卷積 //CV_MEDIAN (median blur) - 對影象進行核大小為param1×param1 的中值濾波 (i.e. 鄰域是方的). //CV_BILATERAL (雙向濾波) - 應用雙向 3x3 濾波,彩色 sigma=param1,空間 sigma=param2. 關於雙向濾波, //可參考 http://www.dai.ed.ac.uk/CVonline/LOCAL_COPIES/MANDUCHI1/Bilateral_Filtering.html //param1 //平滑操作的第一個引數. //param2 //平滑操作的第二個引數. 對於簡單/非尺度變換的高斯模糊的情況,如果param2的值 為零,則表示其被設定為param1。 //param3 //對應高斯引數的 Gaussian sigma (標準差). 如果為零,則標準差由下面的核尺寸計算: //sigma = (n/2 - 1)*0.3 + 0.8, 其中 n=param1 對應水平核, // n=param2 對應垂直核. //對小的卷積核 (3×3 to 7×7) 使用如上公式所示的標準 sigma 速度會快。如果 param3 不為零, //而 param1 和 param2 為零,則核大小有 sigma 計算 (以保證足夠精確的操作). //函式 cvSmooth 可使用上面任何一種方法平滑影象。每一種方法都有自己的特點以及侷限。 //沒有縮放的影象平滑僅支援單通道影象,並且支援8位到16位的轉換(與cvSobel和cvaplace相似)和32位浮點數到32位浮點數的變換格式。 //簡單模糊和高斯模糊支援 1- 或 3-通道, 8-位元 和 32-位元 浮點影象。這兩種方法可以(in-place)方式處理影象。 //中值和雙向濾波工作於 1- 或 3-通道, 8-點陣圖像,但是不能以 in-place 方式處理影象. // //中值濾波 //中值濾波法是一種非線性平滑技術,它將每一象素點的灰度值設定為該點某鄰域視窗內的所有象素點灰度值的中值。實現方法: //通過從影象中的某個取樣視窗取出奇數個數據進行排序 //用排序後的中值取代要處理的資料即可 //中值濾波法對消除椒鹽噪音非常有效,在光學測量條紋圖象的相位分析處理方法中有特殊作用,但在條紋中心分析方法中作用不大。 //中值濾波在影象處理中,常用於用來保護邊緣資訊,是經典的平滑噪聲的方法 //中值濾波原理 //中值濾波是基於排序統計理論的一種能有效抑制噪聲的非線性訊號處理技術,中值濾波的基本原理是把數字影象 //或數字序列中一點的值用該點的一個拎域中各點值的中值代替,讓周圍的畫素值接近的值,從而消除孤立的噪聲點。 //方法是去某種結構的二維滑動模板,將板內畫素按照畫素值的大小進行排序,生成單調上升(或下降)的為二維資料序列。 //二維中值濾波輸出為g(x,y)=med{f(x-k,y-l),(k,l∈W)} ,其中,f(x,y),g(x,y)分別為原始影象和處理後圖像。 //W為二維模板,通常為2*2,3*3區域,也可以是不同的的形狀,如線狀,圓形,十字形,圓環形等。 //高斯濾波 //高斯濾波實質上是一種訊號的濾波器,其用途是訊號的平滑處理,我們知道數字影象用於後期應用,其噪聲是最大的問題, //由於誤差會累計傳遞等原因,很多影象處理教材會在很早的時候介紹Gauss濾波器,用於得到信噪比SNR較高的影象(反應真實訊號)。 //於此相關的有Gauss-Lapplace變換,其實就是為了得到較好的影象邊緣,先對影象做Gauss平滑濾波, //剔除噪聲,然後求二階導矢,用二階導的過零點確定邊緣,在計算時也是頻域乘積=>空域卷積。 //濾波器就是建立的一個數學模型,通過這個模型來將影象資料進行能量轉化,能量低的就排除掉,噪聲就是屬於低能量部分 //其實程式設計運算的話就是一個模板運算,拿影象的八連通區域來說,中間點的畫素值就等於八連通區的畫素值的均值,這樣達到平滑的效果 //若使用理想濾波器,會在影象中產生振鈴現象。採用高斯濾波器的話,系統函式是平滑的,避免了振鈴現象。 cvSmooth( noise, noise, CV_GAUSSIAN, 5, 5, 1, 1 ); // GAUSSIAN濾波做平衡 //將幀疊加到累積器(accumulator)中 //void cvAcc( const CvArr* image, CvArr* sum, const CvArr* mask=NULL ); //image //輸入影象, 1- 或 3-通道, 8-位元或32-位元浮點數. (多通道的每一個通道都單獨處理). //sum //同一個輸入影象通道的累積,32-位元或64-位元浮點數陣列. //mask //可選的運算 mask. // noise = noise + y cvAcc( y, noise ); //ConvertScale //使用線性變換轉換陣列 //void cvConvertScale( const CvArr* src, CvArr* dst, double scale=1, double shift=0 ); //#define cvCvtScale cvConvertScale //#define cvScale cvConvertScale //#define cvConvert( src, dst ) cvConvertScale( (src), (dst), 1, 0 ) //src //輸入陣列. //dst //輸出陣列 //scale //比例因子. //shift //該加數被加到輸入陣列元素按比例縮放後得到的元素上 //函式 cvConvertScale 有多個不同的目的因此就有多個同義函式(如上面的#define所示)。 //該函式首先對輸入陣列的元素進行比例縮放,然後將shift加到比例縮放後得到的各元素上, //即: dst(I)=src(I)*scale + (shift,shift,...),最後可選的型別轉換將結果拷貝到輸出陣列。 //多通道的陣列對各個通道是獨立處理的。 //型別轉換主要用舍入和溢位截斷來完成。 //也就是如果縮放+轉換後的結果值不能用輸出陣列元素型別值精確表達, //就設定成在輸出陣列資料軸上最接近該數的值。 //如果 scale=1, shift=0 就不會進行比例縮放. 這是一個特殊的優化, //相當於該函式的同義函式名:cvConvert 。如果原來陣列和輸出陣列的型別相同, //這是另一種特殊情形,可以被用於比例縮放和平移矩陣或影象, //此時相當於該函式的同義函式名:cvScale。 cvConvert( noise, y ); // y = noise * 1 + 0 //Merge //從幾個單通道陣列組合成多通道陣列或插入一個單通道陣列 //void cvMerge( const CvArr* src0, const CvArr* src1, // const CvArr* src2, const CvArr* src3, CvArr* dst ); //#define cvCvtPlaneToPix cvMerge //src0... src3 //輸入的通道. //dst //輸出陣列. //函式cvMerge 是前一個函式的反向操作。 //如果輸出陣列有N個通道並且前N個輸入通道非NULL, //就拷貝所有通道到輸出陣列,如果在前N個通道中只有一個單通道非NULL , //只拷貝這個通道到輸出陣列,否則 就會產生錯誤。 //除前N通道以外的餘下的通道必須置NULL。 //對於設定了COI的 IplImage結構使用 cvCopy也可以實現向影象中插入一個通道 。 cvMerge( y, 0, 0, 0, img_yuv ); // 圖層合併 cvCvtColor( img_yuv, img, CV_YCrCb2BGR ); // 影象色彩空間轉換 //cvNamedWindow // //建立視窗 //int cvNamedWindow( const char* name, int flags=CV_WINDOW_AUTOSIZE ); //name //視窗的名字,它被用來區分不同的視窗,並被顯示為視窗標題。 //flags //視窗屬性標誌,為1時表示會根據影象自動調整視窗大小。 //目前唯一支援的標誌是CV_WINDOW_AUTOSIZE。 //當這個標誌被設定後,使用者不能手動改變視窗大小, //視窗大小會自動調整以適合被顯示影象(參考cvShowImage)。 //函式cvNamedWindow建立一個可以放置影象和trackbar的視窗。 //被建立的視窗可以通過它們的名字被引用。 //如果已經存在這個名字的視窗,這個函式將不做任何事情。 cvNamedWindow( "image with grain", CV_WINDOW_AUTOSIZE ); //cvShowImage // //在指定視窗中顯示影象 //void cvShowImage( const char* name, const CvArr* image ); //name //視窗的名字。 //image //被顯示的影象。 //函式cvShowImage 在指定視窗中顯示影象。 //如果視窗建立的時候被設定標誌CV_WINDOW_AUTOSIZE, //那麼影象將以原始尺寸顯示;否則,影象將被伸縮以適合視窗大小。 img.show( "image with grain" ); // cvShowImage的另外一種形式 //cvWaitKey // //等待按鍵事件 //int cvWaitKey( int delay=0 ); //delay //延遲的毫秒數。 //函式cvWaitKey無限制的等待按鍵事件(delay<=0時);或者延遲"delay"毫秒。 //返回值為被按鍵的值,如果超過指定時間則返回-1。 //註釋:這個函式是HighGUI中唯一能夠獲取和操作事件的函式, //所以在一般的事件處理中,它需要週期地被呼叫, //除非HighGUI被用在某些能夠處理事件的環境中。 /*譯者注:比如在MFC環境下,這個函式不起作用。*/ cvWaitKey(); return 0; // 所有影象自動釋放,這是使用C++類比較方便的地方 }
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
知道了上面的以後,我的需求是: 隨機數可能重複. 因此希望使用一個隨機數做種子用它來確定一組"無規律"的自然數序列, 並且在這個序列中不會出現重複的自然數. 即
隨機數的產生不會出現重複.
方法1. 若重複,再次生成。
逐個產生這些隨機數,每產生一個,都跟前面的隨機數比較,如果重複,就重新產生:
這種演算法越到後面,遇到已使用過的元素的可能性越高,重複次數就越多,但是當m較小時還是很好的:)
方法2.演算法四(1)
for (i=0;i<n;i++)
a[i]=i+1;
for (i=n-1;i>0;i--)
{w=rand()%i;
t=a[i];
a[i]=a[w];
a[w]=t;
}
這個演算法很不錯,有人會懷疑其隨機性,但個人認為是沒問題的,首先第二行按順序用0到n填滿整個陣列;第三行,是隨機產生從0到n-2個數組下標,把這個下標的元素值跟n-1下標的元素值交換,一直進行到下標為1的元素。因此它只需要遍歷一次就能產生全部的隨機數。
上述演算法參考: c語言中的隨機函式分析與生成m個不重複隨機數演算法比較 http://s99f.blog.163.com/blog/static/35118365200811196445340/ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 同樣精彩的一篇文章: http://hi.baidu.com/darwinmarx/blog/item/2e41e9e9a2b24131b90e2d70.htmlCvRNG,RNG代表的意思就是Random Number Generation,隨機數產生,隨機的部份實際上用起來很簡單,但原理的部份就稍微難一些了,基本的函式為cvRNG(),初始化資料結構,cvRandInt(),32bits隨機及浮點數隨機的cvRandReal(),以下程式碼實作.
簡單的隨機程式製作1
#include <cv.h>#include <highgui.h>#include <stdio.h>#include <stdlib.h>int main(){ CvRNG rng; rng = cvRNG(cvGetTickCount()); for(int i=0;i<100;i++) { printf("%d/n",cvRandInt(&rng)); } printf("The Tick Frequency is %f/n",cvGetTickFrequency()); system("pause");}
這邊用cvGetTickCount()當種子與C語言time()不同的是,它雖然同樣用時間產生種子,但是它用的是int64長整數型別,而time()則是要看機器而定32位元電腦用32bits,64位元電腦則用64bits.這個程式產生了100個有正有負隨機均勻分佈的整數,cvRandInt()產生的是32bit隨機的數,用int去接的話會介於-2147483648~2147483647之間,而用unsign int去接的話會是0~4294967295,而printf()裡"%d"出來的型別是int,如果用printf("%d/n",cvRandInt(&
rng)%256)的話出來的將會是0~255的正整數.
改成cvRandInt(&
rng)%256之後
簡單的隨機程式製作2
#include <highgui.h>#include <stdio.h>#include <stdlib.h>int main(){ CvRNG rng; rng = cvRNG(cvGetTickCount()); for(int i=0;i<100;i++) { printf("%.2f/n",cvRandReal(&rng)); } system("pause");}
執行結果:
這裡cvRandReal()是拿cvRandInt()的結果乘以二的負三十二次方,因此兩個數會被轉成double去做除法運算而變成浮點數隨機的均勻分佈,如果這邊將printf("%.2f/n",cvRandReal(&
rng))改成printf("%d/n",cvRandReal(&
rng))亦會出現一堆整數數字,但會因為變數格式的關係隨機的意義將會失焦,因此,這邊程式不該用"%d"來列印,這個部份就要參考計算機組織與設計的計算機算數的部份.
cvGetTickCount()
回傳長整數64bits的時間資料,在OpenCV是為CvRNG而設的的專用種子.
cvGetTickFrequency()
回傳系統時脈頻率.
cvRNG()
跟一般C語言的srand()使用方法一樣,要先給它一個種子,但srand()用到的是unsigned int的32bits的種子範圍,而cvRNG()用的是LONGLONG型態,64bits大小的長整數種子,LONGLONG這個變數型別是在windows.h底下宣告的,也可以用long long表示.這邊初始化CvRNG資料結構,假如seed給0的話,它將會自動轉成-1.
cvRNG(64bits種子的數字)
cvRandInt()
傳回均勻分佈(Uniform Distribution),32bits的隨機數,均勻分佈為統計學上的專有名詞.表示長時間下所有數字出現的機率都是一樣的,而cvRandInt()在OpenCV裡使用的公式是
temp = (uint64)(unsigned)temp*1554115554 + (temp >> 32);
這公式(演演算法)的名稱叫做Multiply-with-carry (MWC) generator,有興趣的話可以在網路上找"隨機數產生器"(Random Number Generator)可以找到一些資料,Multiply-with-carry是將64bits的種子去產生32bits的隨機數
unsigned cvRandInt(CvRNG資料結構)
cvRandReal()
傳回均勻分佈,0~1之間的隨機小數.cvRandReal()的公式則是用
cvRandInt(rng)*2.3283064365386962890625e-10
的方法,其實就是cvRandInt(rng)*2^(-32),也就是將cvRandInt()隨機出來的結果(32bits)除以2的三十二次方,因此,出現的結果將會是0~1之間的小數,也就是機率0~1之間.
double cvRandReal(CvRNG資料結構)