图像处理线性变换

2012-06-13 12:43:30 fuyun_613 阅读数 4805
线性变换是灰度变换的一种,图像的灰度变换通过建立灰度映射来调整源图像的灰度从而达到图像增强的目的。

     其公式可以表示为y(x, y) = k * x(x, y) + b;其中y(x, y)表示目标像素值,x(x, y)表示源像素值,k表示斜率,b表示截距。 

    1)当k>1时,可用于增加图像的对比度。图像的像素值在变换后全部增大,整体显示效果被增强。

    2)当k=1时,常用于调节图像亮度。

    3)当0<k<1时,效果与k>1时刚刚相反,图像的对比度和整体效果都被削弱。

    4)当k<0时,源图像较亮的区域变暗,而较暗的区域会变亮。此时可以使函数中的k=-1,d=255让图像实现反色效果。

     代码如下:

  1. /******************************************************************************    
  2. *   作用:     线性变换函数
  3. *   参数: pDst     输出图像的像素数组
  4. *   参数: pSrc     原始图像的像素数组
  5. *   参数: nWidth   原始图像宽度
  6. *   参数: nHeight  原始图像高度
  7. *   参数: slope      线性函数的斜率
  8. *   参数: inter      线性函数的截距
  9. *   备注: 此函数对于彩色图同样适用
  10. ******************************************************************************/ 
  11. int LineTrans(BYTE* pDst, BYTE* pSrc, int nWidth, int nHeight, double slope, double inter) 
  12.     if (!pSrc || !pDst) 
  13.     { 
  14.         return EXIT_FAILURE; 
  15.     } 
  16.  
  17.     // 灰度映射表 
  18.     BYTE map[256]; 
  19.  
  20.     // 保存运算后的临时值 
  21.     double dTemp; 
  22.     int i, j; 
  23.     for (i = 0; i < 256; i++) 
  24.     { 
  25.         // 计算当前像素变换后的值 
  26.         dTemp = slope * i + inter; 
  27.  
  28.         // 如果超界则修改其值 
  29.         if (dTemp < 0) 
  30.             dTemp = 0.0; 
  31.         else if (dTemp > 255) 
  32.             dTemp = 255; 
  33.  
  34.         // 四舍五入 
  35.         map[i] = int(dTemp + 0.5); 
  36.     } 
  37.  
  38.     // 线性变换后的值直接在映射表中查找 
  39.     for (i = 0; i < nWidth * nHeight; i++) 
  40.     {    
  41.         for (j = 0; j < 4; j++) 
  42.             pDst[i*4 + j] = map[pSrc[i*4 + j]]; 
  43.     } 
  44.     return EXIT_SUCCESS; 

  

2015-12-11 08:14:57 liu_xiao_cheng 阅读数 3469

1.灰度线性变换:F(x)=a*f(x)+b

int IMGChange(IplImage *src,IplImage *dest, float slope,float intercept)
{
 if(NULL == src)
  return -1;

 if(src->nChannels!=1 || dest->nChannels!=1)
 {
  cout<<"It's not gray image!"<<endl;
  return -1;
 }
 float gray = 0; 
 for(int i = 0;i < src->height;i++){ 
  for(int j = 0; j < src->width; j++){ 
 
   gray = (float)src->imageData[src->widthStep * i + j ];
   gray = slope*gray + intercept;
   dest->imageData[dest->widthStep * i + j ]=gray;
        } 
    } 
 return 0;
}

当slope=-1   intercept=255即为图像反转


2.伽马变换,s=c*(r)γ,可用于图像增强等预处理。

int IMGgammaChange(IplImage *src,IplImage *dest, double gamma,double comp)
{
 if(NULL == src)
  return -1;

 if(src->nChannels!=1 || dest->nChannels!=1)
 {
  cout<<"It's not gray image!"<<endl;
  return -1;
 }
 double gray = 0; 
 for(int i = 0;i < src->height;i++){ 
  for(int j = 0; j < src->width; j++){ 
 
   gray = (double)src->imageData[src->widthStep * i + j ];
   gray =pow((gray+comp)/255.0,gamma)*255;
   dest->imageData[dest->widthStep * i + j ]=gray;
        } 
    } 
 return 0;
}




2016-06-13 14:05:59 wenhao_ir 阅读数 1582

图像的线性变换是图像处理的基本运算,通常应用在调整图像的画面质量方面,如图像对比度、亮度及反转等操作。对于输入图像f(x,y),输出图像g(x,y),其线性变换表达式为:

其中参数a表示图像对比度变化,b表示图像亮度变化。当a<0时,图像变换代表反转操作,如a=-1、b=255,这是常见的8位灰度图像的反转操作设置参数;当|a|>1时,图像变换代表对比度增加操作;当|a|<1时,图像变换代表对比度减少操作。当b>0时,表示图像变换操作是亮度增加操作;b<0时,表示图像变换操作是亮度减少操作。

线性变换的OpenCV源代码如下图所示:

源码中用到的图像的下载链接为:http://pan.baidu.com/s/1i4Dvm2h

图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!

 

//OpenCV版本2.4.9  
//图像处理开发资料、图像处理开发需求、图像处理接私活挣零花钱,可以搜索公众号"qxsf321",并关注!

#include <opencv2/core/core.hpp>        
#include <opencv2/highgui/highgui.hpp> 
#include <opencv2/imgproc/imgproc.hpp>  
#include <iostream> 
using namespace cv;
// 图像线性变换操作
cv::Mat linearTransform(cv::Mat srcImage, float a, int b)
{
    if(srcImage.empty()){  
        std::cout<< "No data!" <<std::endl;  
    } 
    const int nRows = srcImage.rows;
    const int nCols = srcImage.cols;
    cv::Mat resultImage = 
        cv::Mat::zeros(srcImage.size(), srcImage.type());
    // 图像元素遍历
    for( int i = 0; i < nRows; i++ )
    {
        for( int j = 0; j < nCols; j++ )
        {
            for( int c = 0; c < 3; c++ )//如果源图像是灰度图,那么把这里改为c<1即可
            {
                // 矩阵at操作,检查下标防止越界
                resultImage.at<Vec3b>(i,j)[c] = 
                  saturate_cast<uchar>(a * 
                  (srcImage.at<Vec3b>(i,j)[c]) + b);
            }
        }
    }
    return resultImage;
}
int main()
{
    // 图像获取及验证
    cv::Mat srcImage = cv::imread("lakeWater.jpg"); 
    if(!srcImage.data) 
       return -1;
    cv::imshow("srcImage", srcImage);
    //cv::waitKey(0);
    // 线性变换
    float a = 1.2;
    int b = 50;
    cv::Mat new_image  = linearTransform(srcImage, a, b);  
    cv::imshow("dst", new_image);
    cv::waitKey(0);
    return 0;
}

 

运行结果如下图所示:

 

2015-06-04 20:40:45 u014030117 阅读数 24391

注:本系列来自于图像处理课程实验,用Matlab实现最基本的图像处理算法

图像点处理是图像处理系列的基础,主要用于让我们熟悉Matlab图像处理的编程环境。灰度线性变换和灰度拉伸是对像素灰度值的变换操作,直方图是对像素灰度值的统计,直方图均衡是对灰度值分布的变换。


1.灰度线性变换

(1)线性变换函数

原图向灰度值为g,通过线性函数f(x)=kx+b转换为f(g)得到灰度的线性变换。

(2)代码实现

Matlab中支持矩阵作为函数参数传入,定义一个线性转换函数,利用Matlab矩阵操作,用一行代码即可对整个二维图像矩阵中所有点的灰度进行线性变换:

function [ new ] = LinearTransformFunc( original, k, d )
    new = original * k + d;
end

其中k和d是线性函数的斜率和截距,由用户输入指定,用户输入为空时赋予默认值:

  • input函数获取用户输入
  • isempty判断用户输入是否为空:
k = input('please input the slope(k) of grayscale linear transformation function:\n');

b = input('please input the intercept(b) of grayscale linear transformation function:\n');

if isempty(k)
    k = 1;
end

if isempty(b)
    b = 0;
end

变换图像名也可以由用户input指定,默认为lena图:

  • imread读出图片,返回值第一个是我们需要的灰度图(二维矩阵)
  • 对变换后的灰度图,用imshowfigure中显示图像
name = input('please input the name of image:\n');

if isempty(name) 
    name = 'lena'; 
end

original = imread(strcat('../exp/', name, '.bmp'));

transformed = LinearTransformFunc(original, k, b);

figure
imshow(transformed)

在这个实验的操作中说明如何读入、显示,后面实验不在赘述

(3)运行结果

利用subplot作图,把原图和线性变换后的图像对比,线性变换函数是f(x)=2x+10:

这里写图片描述

左图是原图像,右图是线性变换后图像。


2.灰度拉伸变换

(1)灰度拉伸变换和线性分段函数

灰度拉伸变换和线性变换相似,只是是将灰度值做分段线性变换。分段函数控制点(x1,y1)(x2,y2)

这里写图片描述

(2)代码实现

整个程序用户接口和流程和线性变换相同,只是需要用户输入两个控制点,并传入以下的分段线性变换函数:

function [ new ] = StretchFunc(original, x1, y1, x2, y2 )
    new = original;

    w = size(new, 1);
    h = size(new, 2);

    k1 = y1 / x1;

    dk1 = (y2 - y1) / (x2 - x1);
    dk2 = (255 - y2) / (255 - x2);

    for i = 1 : w
        for j = 1 : h
            x = new(i, j);
            if x < x1
                new(i, j) = k1 * x;
            elseif x < x2
                new(i, j) = dk1 * (x - x1) + y1;
            else
                new(i, j) = dk2 * (x - x2) + y2;
            end
        end
    end
end

这里不可避免要使用到for循环。

(3)运行结果

同样对比原图,默认控制点选取(-100,20)和(100,180)

这里写图片描述


3.灰度直方图

(1)灰度直方图

灰度直方图就是对图像中每个像素点的灰度值出现的频数或频率(归一化)的统计,那么我们直接遍历整个图像统计出每个灰度值出现次数再做相应处理即可。

(2)代码实现

首先需要遍历统计灰度,我在GrayScaleStatistic函数里完成统计,区间[low, high]是目标灰度统计区间,默认是[0,255]:

function [ result ] = GrayScaleStatistic( original, low, high )

    w = size(original, 1);
    h = size(original, 2);
    result = zeros(1, high - low + 1);

    for i = 1 : w
        for j = 1 : h
            g = original(i, j);
            if g >= low && g <= high
                g = g - low + 1;
                result(g) = result(g) + 1; 
            end
        end
    end

end

然后就使用Matlab条形图作图函数bar完成灰度图作图:

y = GrayScaleStatistic(original, low, high);
x = low : 1 : high;
bar(x, y)

对于题目要求的可输入灰度区间显示,我们要么不统计区间[low, high]以外的灰度值,要么直接全部统计但在作图时用xlim函数限制x轴取值范围:

xlim([low, high])

(3)运行结果

对比Matlab标准直方图作图函数histogram,结果如下:

这里写图片

也可以通过input输入限定区间,这里是[20,150]区间的灰度直方图:

这里写图片描述

左右对比,效果一致。


4.直方图均衡化

(1)直方图均衡算法

直方图均衡主要用于增强动态范围偏小的图像的反差,其基本思想是把原始图像的直方图变换为均匀分布,从而增强灰度值的动态范围,以达到增强对比度的效果。

直方图均衡化算法如下

  1. 归一化灰度频数直方图,得到频率直方图sk
  2. sk计算频率累计直方图tk
  3. tk做取整扩展:tk = int[(L - 1) * tk + 0.5],将直方图灰度映射尽量满整个灰度取值空间L
  4. 确定变换映射关系k->tk
  5. 根据映射关系变换图像灰度值

(2)代码实现

在脚本中调用Normalize函数直接得到均衡化后的图像,再统计直方图并显示。

Normalize函数如下:

function [ new ] = Normalize( original, v )

    s = sum(v);
    tv = v / s;

    l = length(v);

    for i = 2 : l
        tv(i) = tv(i) + tv(i - 1);
    end

    tk = uint8(255 * tv + 0.5);

    w = size(original, 1);
    h = size(original, 2);

    new = original;

    for i = 1 : w
        for j = 1 : h
            new(i, j) = tk(original(i, j) + 1);
        end
    end

end

说明:

  • tv先计算频率直方图,再通过累加得到累计直方图
  • tk根据累计直方图计算新的灰度映射关系
  • 最后遍历整个图像把原灰度转换成均衡化后的灰度值

其中有一下几点需要注意,也是Matlab图操作的注意点:

  • Matlab默认类型是double,对灰度值赋值时注意强制转换类型,保证类型一致
  • Matlab坐标起始从1开始,而灰度值是uint8的0-255,因此映射数组tk把原始灰度映射到变换后灰度时需要加1

(3)结果展示

pout.bmp是一副灰度分布较为集中的图像,因此图像对比度不高,显示较为模糊。使用直方图均值化,分散灰度分布从而增强对比度:

j

通过对比均衡先后直方图分布,可以发现:

  • 灰度分布不能完全平均化,是由于均值化算法中运用了取整运算,而不是离散值的完全均衡化
  • 得到的均衡化后直方图走势没有发生变化,因此图像没有失真

2018-12-09 19:41:37 freehawkzk 阅读数 1932

图像灰度线性变换

1 概念

  灰度线性变换是一种灰度变换,通过建立灰度映射来调整源图像的灰度,达到图像增强的目的。灰度映射通常使用灰度变换曲线来表示。

2 原理

  灰度线性变换就是将图像的像素值通过指定的线性函数进行变换,以此增强或减弱图像的灰度。灰度线性变换的公式是常见的一维线性函数:
g(x,y)=kf(x,y)+b g(x,y) = k \cdot f(x,y) + b
xx为原始灰度值,则变换后的灰度值yy为:
y=kx+b(0y255) y = k \cdot x + b \dots\dots(0 \leq y \leq 255)
kk表示直线的斜率,即倾斜程度,bb表示线性函数在yy轴的截距。

3 作用

  

kk bb取值 意义
k&gt;1k&gt;1 增大图像的对比度,图像的像素值在变换后全部增大,整体效果被增强
k=1k=1 通过调整bb,实现对图像亮度的调整
0&lt;k&lt;10 &lt; k &lt; 1 图像的对比度被削弱
k&lt;0k&lt;0 原来图像亮的区域变暗,原来图像暗的区域变亮

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的时间损耗,时间加快只对很大的图有效。