圖像灰度線性變換
1 概念
灰度線性變換是一種灰度變換,通過建立灰度映射來調整源圖像的灰度,達到圖像增強的目的。灰度映射通常使用灰度變換曲線來表示。
2 原理
灰度線性變換就是將圖像的像素值通過指定的線性函數進行變換,以此增強或減弱圖像的灰度。灰度線性變換的公式是常見的一維線性函數:
設爲原始灰度值,則變換後的灰度值爲:
表示直線的斜率,即傾斜程度,表示線性函數在軸的截距。
3 作用
取值 | 意義 |
---|---|
增大圖像的對比度,圖像的像素值在變換後全部增大,整體效果被增強 | |
通過調整,實現對圖像亮度的調整 | |
圖像的對比度被削弱 | |
原來圖像亮的區域變暗,原來圖像暗的區域變亮 |
4 Matlab實現
clc;
clear;
close all;
% 對灰度圖進行灰度線性變換
ori_img = imread('../images/6.jpg');
ori_img = rgb2gray(ori_img);
[oriHist,oriX] = imhist(ori_img);
k = 1.25;
d = 0;
gray1 = ori_img * k + d;
[g1Hist,g1X] = imhist(gray1);
k = 1;
d = 50;
gray2 = ori_img * k + d;
[g2Hist,g2X] = imhist(gray2);
k = 0.5;
d = 0;
gray3 = ori_img * k + d;
[g3Hist,g3X] = imhist(gray3);
k = -1;
d = 255;
ori_ = im2double(ori_img);
gray4 = ori_ * k + 1.0;
[g4Hist,g4X] = imhist(gray4);
figure(1),subplot(1,2,1),imshow(ori_img),title('原圖');subplot(1,2,2),imshow(gray1),title('k>0 d=0');
figure(2),subplot(1,2,1),stem(oriX,oriHist),title('原圖直方圖');subplot(1,2,2),stem(g1X,g1Hist),title('k>0 d=0直方圖');
figure(3),subplot(1,2,1),imshow(ori_img),title('原圖');subplot(1,2,2),imshow(gray2),title('k=1 d=50');
figure(4),subplot(1,2,1),stem(oriX,oriHist),title('原圖直方圖');subplot(1,2,2),stem(g2X,g2Hist),title('k=1 d=50直方圖');
figure(5),subplot(1,2,1),imshow(ori_img),title('原圖');subplot(1,2,2),imshow(gray3),title('k=0.5 d=0');
figure(6),subplot(1,2,1),stem(oriX,oriHist),title('原圖直方圖');subplot(1,2,2),stem(g3X,g3Hist),title('k=0.5 d=0直方圖');
figure(7),subplot(1,2,1),imshow(ori_img),title('原圖');subplot(1,2,2),imshow(gray4),title('k=-1 d=255');
figure(8),subplot(1,2,1),stem(oriX,oriHist),title('原圖直方圖');subplot(1,2,2),stem(g4X,g4Hist),title('k=-1 d=255直方圖');
5 OpenCV實現
#include <iostream>
#include <string>
#include "../include/opencv400/opencv2/opencv.hpp"
#include "windows.h"
std::string g_CurrentDirectory;
void SetCurrentDirectoryToExePath()
{
HMODULE hExe = GetModuleHandleA(NULL);
char nameBuf[MAX_PATH] = { 0 };
GetModuleFileNameA(hExe, nameBuf, MAX_PATH);
std::string sName(nameBuf);
sName = sName.substr(0, sName.rfind('\\'));
SetCurrentDirectoryA(sName.c_str());
g_CurrentDirectory = sName;
}
void calcHist1D(cv::Mat& input, cv::Mat& output)
{
int channels[] = { 0 };
int histsize[] = { 256 };
float grayRnage[] = { 0,256 };
const float* ranges[] = { grayRnage };
cv::MatND hist;
cv::calcHist(&input, 1, channels, cv::Mat(), hist, 1, histsize, ranges);
double maxVal = 0;
cv::minMaxLoc(hist, 0, &maxVal, 0, 0);
int scale = 10;
output = cv::Mat::zeros(500, 257 * 5, CV_8UC3);
std::cout << "-----------------------------------" << std::endl;
for (int i = 0; i < histsize[0]; i++)
{
float binVal = hist.at<float>(i, 0);
std::cout <<i <<" "<< binVal << std::endl;
int intensity = cvRound(binVal * 500 / maxVal);
rectangle(output, cv::Point(i * 5, 500 - intensity),
cv::Point((i + 1) * 5, 500),
cv::Scalar::all(255),
-1);
}
}
int main()
{
SetCurrentDirectoryToExePath();
cv::Mat ori_img = cv::imread("../images/6.jpg");
cv::Mat gray_img;
cv::cvtColor(ori_img, gray_img, cv::COLOR_BGR2GRAY);
//gray_img.convertTo(gray_img, CV_32FC1, 1.0 / 255);
cv::namedWindow("灰度圖");
cv::imshow("灰度圖", gray_img);
cv::Mat grayHist;
calcHist1D(gray_img, grayHist);
cv::imshow("hist", grayHist);
float k = 1.25;
int d = 0;
cv::Mat g1 = gray_img * k + d;
cv::Mat g1Hist;
calcHist1D(g1, g1Hist);
cv::imshow("g1", g1);
cv::imshow("g1Hist", g1Hist);
k = 1;
d = 30;
cv::Mat g2 = gray_img * k + d;
cv::Mat g2Hist;
calcHist1D(g2, g2Hist);
cv::imshow("g2", g2);
cv::imshow("g2Hist", g2Hist);
k = 0.5;
d = 0;
cv::Mat g3 = gray_img * k + d;
cv::Mat g3Hist;
calcHist1D(g3, g3Hist);
cv::imshow("g3", g3);
cv::imshow("g3Hist", g3Hist);
k = -1;
d = 255;
cv::Mat g4 = gray_img * k + d;
cv::Mat g4Hist;
calcHist1D(g4, g4Hist);
cv::imshow("g4", g4);
cv::imshow("g4Hist", g4Hist);
cv::waitKey();
return 0;
}
6 效果圖
原圖
6.1 效果圖
k> 1 b=0
7 討論
線性變換是一個有限的查表操作,在C++實現時可以在將圖像逐像素的計算過程轉換爲查表操作。由於灰度線性變換的查找表只需256字節,完全可以全部緩存到現代CPU的cache中,通過多線程的查表操作,可以加快整個圖像的變換過程。當然,這樣速度還是沒有GPU中進行速度快。灰度線性變換是與相鄰像素無關的操作,非常適合在GPU中並行計算。但需要根據圖像大小,考慮圖像從CPU到GPU再從GPU到CPU的時間損耗,時間加快只對很大的圖有效。