int j;
graySum = width * height; // 计算图象象素总数 for (i = 0; i < graygrade; i ++) { // 赋零值
grayfreq[i] = 0.0; }
//计算各个灰度值的计数 for (i = 0; i < height; i ++)
{ }
//计算各个灰度值出现的概率
for (i = 0; i < graygrade; i ++) { }
grayfreq[i] = grayfreq[i] / (double)graySum; for (j = 0; j < width; j ++)
{ //指向图象指针 lpSrc = (unsigned char*)lpDIBBits + width * i + j; }
//相应的灰度计数加1
grayfreq[*(lpSrc)] = grayfreq[*(lpSrc)] + 1;
//构建霍夫曼编码的码表,并用对话框显示霍夫曼码表 CHuffmanCoding coding;
// 创建对话框
coding.grayfreq = grayfreq; // 初始化变量值 coding.graygrade = graygrade;
coding.DoModal(); // 显示对话框 EndWaitCursor(); // 恢复光标
5.2 香农-范诺编码
5.2.1 香农-范诺编码算法实现
资源文件ShannonCoding.cpp是香农-范诺编码的算法程序。与哈夫曼算法相似地从视图类中得到每个灰度值出现的概率,针对这个概率进行编码。
首先用冒泡法从小到大对概率进行排序,从概率大于0处开始进行编码,对概率区间进行分割,然后对每个灰度值对应的编码数组进行追加字符,初始时追加为1,当超出概率总和的一半时,追加的字符改为0,接着继续对上半部分的区间进行分割,此时概率的总和就改为上半部分的概率总和了,重复上述操作,完成整幅图像的编码。具体代码如下所示:
BOOL CShannonCoding::OnInitDialog()
第 16 页 共 32 页
{ // 调用默认得OnInitDialog()函数 CDialog::OnInitDialog(); // 字符串变量
CString str; str = \
int i; int j;
//图像灰度出现概率中间结果的数组
double * temp;
//数组用来存放灰度值和其位置之间 int * turn; temp = new double[graygrade];
// 数组用来存放灰度值和其位置之间的映射 turn = new int[graygrade]; //临时变量
int temp2; // 当前编码区间的概率和
double effectcum;
//初始的概率和为1.0 effectcum = 1.0; // 已经编码灰度值概率的统计和 double
dSum;
//初始化为0
dSum = 0;
// 已编码灰度值 int lCount = 0;
// 起始位置
int lBegin;
//指示编码是否已经完成一段 BOOL * bFinished; bFinished = new BOOL[graygrade]; // 分配内存
m_strCode = new CString[graygrade]; for (i = 0; i < graygrade; i ++) { // 初始化为FALSE
bFinished[i] = FALSE; // 将概率赋值temp数组
temp[i] = grayfreq[i];
// 按灰度值大小顺序排列 turn[i] = i; }
// 中间变量
double
te;
第 17 页 共 32 页
// 用冒泡法对进行灰度值出现的概率排序 // 同时改变灰度值位置的映射关系 for (j = 0; j < graygrade - 1; j ++) { for (i = 0; i < graygrade - j - 1; i ++) { if (temp[i] > temp[i + 1]) { // 互换 te = temp[i]; temp[i] = temp[i + 1]; temp[i + 1] = te;
// 将i和i+1灰度的位置值互换 temp2 = turn[i]; turn[i] = turn[i+1]; turn[i+1] = temp2;
}
} }
// 计算香农-弗诺编码表,从概率大于0处开始编码 for (lBegin = 0; lBegin < graygrade - 1; lBegin ++) { if (temp[lBegin] > 0) {
break; }
}
// 开始编码
while(lCount < graygrade)
{ // 从概率大于零的灰度值开始编码 lCount = lBegin; // 对区间进行分割,对每个区间的灰度值编码 for (i = lBegin; i < graygrade; i ++) { // 判断是否编码完成 if (bFinished[i] == FALSE) { // 增加当前编码区间的概率综合 dSum += temp[i]; // 判断是否超出总和的一半 if (dSum > effectcum/2.0) { // 超出,追加的字符改为0 str = \ } // 追加字符
m_strCode[turn[i]] = m_strCode[turn[i]] + str; 第 18 页 共 32 页
// 判断是否编码完一段
if (dSum == effectcum)
{ // 完成一部分编码,重新计算effectcum
dSum = 0; // 初始化dSum为0 // 判断是不是对所有灰度值已经编码一遍 if (i == graygrade - 1) {
j = lBegin; }
else {
j = i + 1; }
temp2 = j; // 保存j值 str = m_strCode[turn[j]];
// 计算下一编码区间的概率总和 effectcum = 0;
for (; j < graygrade; j++)
{ // 判断是否是同一段编码 if ((m_strCode[turn[j]].Right(1) != str.Right(1)) || (m_strCode[turn[j]].GetLength() != str.GetLength())) }
break;
//当前区间的概率总和增加 effectcum += temp[j];
// 码字增加值为1 str = \ // 判断该段编码已经完成 if (temp2 + 1 == j)
{
bFinished[temp2] = TRUE; }
} else
}
{ // 开始下一轮编码 lCount ++; // 重新赋dSum为0 dSum = 0;
// 判断是不是对所有灰度值已经编码一遍 if (i == graygrade - 1)
{
j = lBegin; }
第 19 页 共 32 页
else }
{
j = i + 1;
temp2 = j; // 保存j值 str = m_strCode[turn[j]];
// 计算下一编码区间的概率总和 effectcum = 0;
for (; j < graygrade; j++) {
// 判断是否是同一段编码 if ((m_strCode[turn[j]].Right(1) != str.Right(1)) }
|| (m_strCode[turn[j]].GetLength() != str.GetLength())) { // 退出循环 break; }
effectcum += temp[j]; // 累加
str = \
// 判断该段编码已经完成 if (temp2 + 1 == j) }
bFinished[temp2] = TRUE;
} }
// 计算图像熵
for (i = 0; i < graygrade; i ++) { // 判断概率是否大于0 if (grayfreq[i] > 0)
{ // 计算图像熵 m_shang -= grayfreq[i] * log(grayfreq[i]) / log(2.0);
} }
// 计算平均码字长度
for (i = 0; i < graygrade; i ++)
{ // 累加 m_length +=grayfreq[i] * m_strCode[i].GetLength(); }
// 计算编码效率
m_effect = m_shang / m_length; // 保存变动
UpdateData(FALSE); //输出编码结果
// 字符串变量,列表项目的显示
第 20 页 共 32 页