矩陣上的掩碼操作非常簡(jiǎn)單。這個(gè)想法是,我們根據(jù)掩碼矩陣(也稱(chēng)為內(nèi)核)重新計(jì)算圖像中的每個(gè)像素值。該掩碼保存將調(diào)整相鄰像素(和當(dāng)前像素)對(duì)新像素值有多大影響的值。從數(shù)學(xué)的角度來(lái)看,我們用加權(quán)平均值與我們指定的值進(jìn)行比較。
讓我們考慮圖像對(duì)比度增強(qiáng)方法的問(wèn)題?;旧衔覀円獮閳D像的每個(gè)像素應(yīng)用以下公式:
第一個(gè)符號(hào)是使用公式,而第二個(gè)是通過(guò)使用掩碼的第一個(gè)壓縮版本。通過(guò)將掩模矩陣的中心(在零值索引的大寫(xiě)表示)放在要計(jì)算的像素上,并使用疊加的矩陣值乘以像素值,并使用掩碼。這是同樣的事情,但是在大型矩陣的情況下,后一種符號(hào)更容易查看。
現(xiàn)在讓我們看看如何通過(guò)使用基本的像素訪問(wèn)方法或使用cv :: filter2D函數(shù)來(lái)實(shí)現(xiàn)這一點(diǎn)。
這里有一個(gè)功能:
void Sharpen(const Mat& myImage,Mat& Result)
{
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
const int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());
for(int j = 1 ; j < myImage.rows-1; ++j)
{
const uchar* previous = myImage.ptr<uchar>(j - 1);
const uchar* current = myImage.ptr<uchar>(j );
const uchar* next = myImage.ptr<uchar>(j + 1);
uchar* output = Result.ptr<uchar>(j);
for(int i= nChannels;i < nChannels*(myImage.cols-1); ++i)
{
*output++ = saturate_cast<uchar>(5*current[i]
-current[i-nChannels] - current[i+nChannels] - previous[i] - next[i]);
}
}
Result.row(0).setTo(Scalar(0));
Result.row(Result.rows-1).setTo(Scalar(0));
Result.col(0).setTo(Scalar(0));
Result.col(Result.cols-1).setTo(Scalar(0));
}
首先我們確保輸入圖像數(shù)據(jù)是unsigned char格式。為此,我們使用cv :: CV_Assert函數(shù),當(dāng)其中的表達(dá)式為false時(shí),該函數(shù)會(huì)引發(fā)錯(cuò)誤。
CV_Assert(myImage.depth() == CV_8U); // accept only uchar images
我們創(chuàng)建一個(gè)與我們的輸入相同大小和相同類(lèi)型的輸出圖像。您可以在存儲(chǔ)部分看到,根據(jù)通道數(shù)量,我們可能有一個(gè)或多個(gè)子列。
我們將通過(guò)指針迭代它們,因此元素的總數(shù)取決于這個(gè)數(shù)字。
const int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());
我們將使用普通C []運(yùn)算符來(lái)訪問(wèn)像素。因?yàn)槲覀冃枰谕粫r(shí)間訪問(wèn)多行,我們將獲取每個(gè)行的指針(前一個(gè),當(dāng)前和下一行)。我們需要另一個(gè)指向我們要保存計(jì)算的指針。然后只需使用[]運(yùn)算符訪問(wèn)正確的項(xiàng)目。為了在前面移動(dòng)輸出指針,我們?cè)诿總€(gè)操作之后簡(jiǎn)單地增加一個(gè)(一個(gè)字節(jié)):
for(int j = 1; j <myImage.rows-1; ++ j)
{
const uchar * previous = myImage.ptr < uchar >(j - 1);
const uchar * current = myImage.ptr < uchar >(j);
const uchar * next = myImage.ptr < uchar >(j + 1);
uchar * output = Result.ptr < uchar >(j);
for(int i = nChannels; i <nChannels *(myImage.cols-1); ++ i)
{
* output ++ = saturate_cast < uchar >(5 * current [i]
-current [i-nChannels] - current [i + nChannels] - 上一個(gè)[i] - next [i]);
}
}
在圖像的邊框上,上面的符號(hào)會(huì)導(dǎo)致像素位置不一致(如減去一個(gè)減去一個(gè))。在這些點(diǎn)上,我們的公式是未定義的。一個(gè)簡(jiǎn)單的解決方案是在這些點(diǎn)上不應(yīng)用內(nèi)核,例如,將邊框上的像素設(shè)置為零:
Result.row(0).setTo(Scalar(0));
Result.row(Result.rows-1).setTo(Scalar(0));
Result.col(0).setTo(Scalar(0));
Result.col(Result.cols-1).setTo(Scalar(0));
應(yīng)用這樣的過(guò)濾器在圖像處理中是常見(jiàn)的,在OpenCV中存在著將應(yīng)用掩碼(在某些地方也稱(chēng)為內(nèi)核)的功能。為此,您首先需要定義一個(gè)保存掩碼的對(duì)象:
Mat kernel = (Mat_<char>(3,3) << 0, -1, 0,
-1, 5, -1,
0, -1, 0);
然后調(diào)用cv :: filter2D函數(shù),指定輸入,輸出圖像和內(nèi)核使用:
filter2D(src,dst1,src.depth(),kernel);
該函數(shù)甚至有第五個(gè)可選參數(shù)來(lái)指定內(nèi)核的中心,第六個(gè)可選參數(shù),用于在將其存儲(chǔ)在K中之前添加可選值,然后將其存儲(chǔ)在K中,第七個(gè)用于確定在操作未定義的區(qū)域中要執(zhí)行的操作(國(guó)界)。
此功能較短,較少冗長(zhǎng),因?yàn)橛幸恍﹥?yōu)化,通常比手工編碼方法更快。例如在我的測(cè)試中,第二個(gè)只花了13毫秒,第一次花費(fèi)了大約31毫秒。有一些區(qū)別。
例如:
您可以從這里下載此源代碼,或查看OpenCV源代碼庫(kù)示例目錄samples/cpp/tutorial_code/core/mat_mask_operations/mat_mask_operations.cpp
。
讓我們考慮圖像對(duì)比度增強(qiáng)方法的問(wèn)題?;旧衔覀円獮閳D像的每個(gè)像素應(yīng)用以下公式:
第一個(gè)符號(hào)是使用公式,而第二個(gè)是通過(guò)使用掩碼的第一個(gè)壓縮版本。通過(guò)將掩模矩陣的中心(在零值索引的大寫(xiě)表示)放在要計(jì)算的像素上,并使用疊加的矩陣值乘以像素值,并使用掩碼。這是同樣的事情,但是在大型矩陣的情況下,后一種符號(hào)更容易查看。
現(xiàn)在讓我們看看如何通過(guò)使用基本的像素訪問(wèn)方法或者使用Imgproc.filter2D()函數(shù)來(lái)實(shí)現(xiàn)這一點(diǎn)。
這里有一個(gè)功能:
public static double saturate(double x) {
return x > 255.0 ? 255.0 : (x < 0.0 ? 0.0 : x);
}
public Mat sharpen(Mat myImage, Mat Result) {
myImage.convertTo(myImage, CvType.CV_8U);
int nChannels = myImage.channels();
Result.create(myImage.size(), myImage.type());
for (int j = 1; j < myImage.rows() - 1; ++j) {
for (int i = 1; i < myImage.cols() - 1; ++i) {
double sum[] = new double[nChannels];
for (int k = 0; k < nChannels; ++k) {
double top = -myImage.get(j - 1, i)[k];
double bottom = -myImage.get(j + 1, i)[k];
double center = (5 * myImage.get(j, i)[k]);
double left = -myImage.get(j, i - 1)[k];
double right = -myImage.get(j, i + 1)[k];
sum[k] = saturate(top + bottom + center + left + right);
}
Result.put(j, i, sum);
}
}
Result.row(0).setTo(new Scalar(0));
Result.row(Result.rows() - 1).setTo(new Scalar(0));
Result.col(0).setTo(new Scalar(0));
Result.col(Result.cols() - 1).setTo(new Scalar(0));
return Result;
}
首先我們確保輸入圖像數(shù)據(jù)以無(wú)符號(hào)8位格式。
myImage.convertTo(myImage,CvType .CV_8U);
我們創(chuàng)建一個(gè)與我們的輸入相同大小和相同類(lèi)型的輸出圖像。您可以在存儲(chǔ)部分看到,根據(jù)通道數(shù)量,我們可能有一個(gè)或多個(gè)子列。
int nChannels = myImage.channels();
Result.create(myImage.size(),myImage.type());
我們需要訪問(wèn)多個(gè)行和列,可以通過(guò)向當(dāng)前中心(i,j)添加或減去1來(lái)完成。然后我們應(yīng)用總和并將新值放在結(jié)果矩陣中。
for(int j = 1; j <myImage.rows() - 1; ++ j){
for(int i = 1; i <myImage.cols() - 1; ++ i){
double sum [] = new double [nChannels];
for(int k = 0; k <nChannels; ++ k){
double top = -myImage.get(j - 1,i)[k];
double bottom = -myImage.get(j + 1,i)[k];
double center =(5 * myImage.get(j,i)[k]);
double left = -myImage.get(j,i-1)[k];
double right = -myImage.get(j,i + 1)[k];
sum [k] =飽和(頂+底+中+左+右);
}
Result.put(j,i,sum);
}
}
在圖像的邊框上,上面的符號(hào)會(huì)導(dǎo)致像素位置不存在(如(-1,-1))。在這些點(diǎn)上,我們的公式是未定義的。一個(gè)簡(jiǎn)單的解決方案是在這些點(diǎn)上不應(yīng)用內(nèi)核,例如,將邊框上的像素設(shè)置為零:
Result.row(0).setTo(new Scalar(0));
Result.row(Result.rows() - 1).setTo(new Scalar(0));
Result.col(0).setTo(new Scalar(0));
Result.col(Result.cols() - 1).setTo(new Scalar(0));
應(yīng)用這樣的過(guò)濾器在圖像處理中是常見(jiàn)的,在OpenCV中存在著將應(yīng)用掩碼(在某些地方也稱(chēng)為內(nèi)核)的功能。為此,您首先需要定義一個(gè)保存掩碼的對(duì)象:
Mat kern = new Mat(3,3,CvType .CV_8S);
int row = 0,col = 0;
kern.put(row,col,0,-1,0,-1,5,-1,0,-1,0);
然后調(diào)用Imgproc.filter2D()函數(shù),指定要使用的輸入,輸出圖像和內(nèi)核:
Imgproc.filter2D(src,dst1,src.depth(),kern);
該函數(shù)甚至有第五個(gè)可選參數(shù)來(lái)指定內(nèi)核的中心,第六個(gè)可選參數(shù),用于在將其存儲(chǔ)在K中之前添加可選值,然后將其存儲(chǔ)在K中,第七個(gè)用于確定在操作未定義的區(qū)域中要執(zhí)行的操作(國(guó)界)。
此功能較短,較少冗長(zhǎng),因?yàn)橛幸恍﹥?yōu)化,通常比手工編碼方法更快。例如在我的測(cè)試中,第二個(gè)只花了13毫秒,第一次花費(fèi)了大約31毫秒。有一些區(qū)別。
例如:
您可以在OpenCV源代碼庫(kù)示例目錄中查看samples/java/tutorial_code/core/mat_mask_operations/MatMaskOperations.java
。
我們考慮圖像對(duì)比度增強(qiáng)方法的問(wèn)題?;旧衔覀円獮閳D像的每個(gè)像素應(yīng)用以下公式:
第一個(gè)符號(hào)是使用公式,而第二個(gè)是通過(guò)使用掩碼的第一個(gè)壓縮版本。通過(guò)將掩模矩陣的中心(在零值索引的大寫(xiě)表示)放在要計(jì)算的像素上,并使用疊加的矩陣值乘以像素值,并使用掩碼。這是同樣的事情,但是在大型矩陣的情況下,后一種符號(hào)更容易查看。
現(xiàn)在讓我們看看如何通過(guò)使用基本的像素訪問(wèn)方法或使用cv2.filter2D()函數(shù)來(lái)實(shí)現(xiàn)這一點(diǎn)。
這里有一個(gè)功能:
def is_grayscale(my_image):
return len(my_image.shape) < 3
def saturated(sum_value):
if sum_value > 255:
sum_value = 255
if sum_value < 0:
sum_value = 0
return sum_value
def sharpen(my_image):
if is_grayscale(my_image):
height, width = my_image.shape
else:
my_image = cv2.cvtColor(my_image, cv2.CV_8U)
height, width, n_channels = my_image.shape
result = np.zeros(my_image.shape, my_image.dtype)
for j in range(1, height - 1):
for i in range(1, width - 1):
if is_grayscale(my_image):
sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \
- my_image[j, i + 1] - my_image[j, i - 1]
result[j, i] = saturated(sum_value)
else:
for k in range(0, n_channels):
sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] - my_image[j - 1, i, k] \
- my_image[j, i + 1, k] - my_image[j, i - 1, k]
result[j, i, k] = saturated(sum_value)
return result
首先我們確保輸入圖像數(shù)據(jù)以無(wú)符號(hào)8位格式。
my_image = cv2.cvtColor(my_image,cv2.CV_8U)
我們創(chuàng)建一個(gè)與我們的輸入相同大小和相同類(lèi)型的輸出圖像。您可以在存儲(chǔ)部分看到,根據(jù)通道數(shù)量,我們可能有一個(gè)或多個(gè)子列。
height,width,n_channels = my_image.shape
result = np.zeros(my_image.shape,my_image.dtype)
我們需要訪問(wèn)多個(gè)行和列,可以通過(guò)向當(dāng)前中心(i,j)添加或減去1來(lái)完成。然后我們應(yīng)用總和并將新值放在結(jié)果矩陣中。
for j in range(1, height - 1):
for i in range(1, width - 1):
if is_grayscale(my_image):
sum_value = 5 * my_image[j, i] - my_image[j + 1, i] - my_image[j - 1, i] \
- my_image[j, i + 1] - my_image[j, i - 1]
result[j, i] = saturated(sum_value)
else:
for k in range(0, n_channels):
sum_value = 5 * my_image[j, i, k] - my_image[j + 1, i, k] - my_image[j - 1, i, k] \
- my_image[j, i + 1, k] - my_image[j, i - 1, k]
result[j, i, k] = saturated(sum_value)
應(yīng)用這樣的過(guò)濾器在圖像處理中是常見(jiàn)的,在OpenCV中存在著將應(yīng)用掩碼(在某些地方也稱(chēng)為內(nèi)核)的功能。為此,您首先需要定義一個(gè)保存掩碼的對(duì)象:
kernel = np.array([[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]], np.float32) # kernel should be floating point type
然后調(diào)用cv2.filter2D()函數(shù),指定要使用的輸入,輸出圖像和kernell:
dst1 = cv2.filter2D(src, -1, kernel) # ddepth = -1, means destination image has depth same as input image
此功能較短,較少冗長(zhǎng),因?yàn)橛幸恍﹥?yōu)化,通常比手工編碼方法更快。例如在我的測(cè)試中,第二個(gè)只花了13毫秒,第一次花費(fèi)了大約31毫秒。有一些區(qū)別。
例如:
您可以在OpenCV源代碼庫(kù)示例目錄中查看samples/python/tutorial_code/core/mat_mask_operations/mat_mask_operations.py。
更多建議: