用C++实现RGB转256色

news/2024/12/29 3:30:41/

前言

RGB图片转256色,我知道的有两种方法:

①用Windows系统自带的画图工具打开图片,再另存为256色bmp图片

②用Photoshop打开图片,然后图像模式索引颜色,选择局部(可感知)确定存储为bmp图片

方法① 转出来的256色图片,会有明显的掉色(如下图)。

方法② 不会掉色,因为photoshop的局部(可感知)会给图片定制最合适的颜色表,而不是像画图工具一样使用系统颜色表。但每次都要打开photoshop,就有点麻烦。

RGB转256色的步骤,可以参考真彩色转256色算法,统计图片中所有颜色出现的次数,选取出现次数最多的256种颜色作为颜色表,就不会感觉到掉色。

思路:定义一个很大的数组,对应所有可能的颜色,遍历图片的每个像素,每遇到一种颜色,就在数组的对应位置加一,最后给数组从大到小排序,取数组开头的256种颜色作为颜色表。完美。

但是RGB图片有255*255*255=16581375种可能的颜色,不想开辟这么长的数组,而且很多相近的、人眼难以看出差别的颜色,完全合并为一种颜色,于是RGB分量原本有8位,统计的时候只取高4位,这样就只有16*16*16 = 4096种可能的颜色,一下就节省99%的空间。

首要问题:如何用C++操作图片

我找到了一个很适合我(新手)的开源库 EasyBMP ,可以读写1位、4位、8位、16位、24位、32位的BMP图片,可以访问图片中任一像素的颜色;可以很容易地读进来一个24位的图片,转换成8位,以256色保存出去。

#include "EasyBMP.h"
using namespace std;int main( int argc, char* argv[] )
{BMP myPicture;myPicture.ReadFromFile("24bitTest.bmp");     // 读进来一个24位的图片myPicture.SetBitDepth( 8 );                  // 转换成8位myPicture.WriteToFile("24bitTest-8bit.bmp"); // 以256色保存出去return 0;
}

只不过转换成8位再保存(保存成256色),用的颜色表是windows系统颜色表,保存出来会掉色。不过 EasyBMP 提供了修改颜色表的接口,还带一个RGB转灰度的函数可以参考。

开始写RGB转256色

首先在EasyBMP的 EasyBMP_VariousBMPutilities.h 头文件里,声明定制颜色表函数。其中用到的std::sort()排序,需要引用<algorithm>:

/*  FILE : EasyBMP_VariousBMPutilities.h  */
#include <algorithm>
bool CreateBestColorTable(BMP&InputImage);

然后在EasyBMP.cpp里写定制颜色表函数的实现:

/*  FILE : EasyBMP.cpp  */
struct Nm{  int num;   //数字  int count=0; //个数    
};
bool cmp (const Nm &A,const Nm &B){ return A.count > B.count;
}
bool CreateBestColorTable(BMP&InputImage)
{using namespace std;// 检查当前的色彩深度有没有颜色表int BitDepth = InputImage.TellBitDepth();if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 ){if( EasyBMPwarnings ){cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl<< "                 depth that does not require a color table." << endl<< "                 Ignoring request." << endl;}return false;}int i;// 选取RGB颜色前4bit,组成12bit,统计频率。struct Nm Num[4096];for(i = 0; i < 4096; i++) Num[i].num=i;for(int row = 0; row < InputImage.TellHeight(); row++){for(int col = 0; col < InputImage.TellWidth(); col++){i = 0;i |= (InputImage(col,row)->Red&0b11110000)<<4;i |= (InputImage(col,row)->Green&0b11110000);i |= (InputImage(col,row)->Blue&0b11110000)>>4;Num[i].count++;}}sort(Num,Num+4096,cmp);// 设置颜色表int NumberOfColors = InputImage.TellNumberOfColors();for( i=0 ; i < NumberOfColors ; i++ ){RGBApixel TempColor;TempColor.Red   = (ebmpBYTE)((Num[i].num&0b111100000000)>>4);TempColor.Green = (ebmpBYTE)( Num[i].num&0b000011110000);TempColor.Blue  = (ebmpBYTE)((Num[i].num&0b000000001111)<<4);TempColor.Alpha = 0;InputImage.SetColor( i , TempColor );  }return true;
}

在主函数里使用:

/*  FILE : EasyBMPsample.cpp  */
#include "EasyBMP.h"
using namespace std;int main( int argc, char* argv[] )
{BMP myPicture;myPicture.ReadFromFile("24bitTest.bmp");myPicture.SetBitDepth( 8 );CreateBestColorTable(myPicture);myPicture.WriteToFile("24bitTest-8bit-best.bmp");return 0;
}

结果:

转换效果不太好,在渐变的地方能看到“等高线”。这可能是因为统计颜色的时候,只统计高4位太少了。

改成统计高6位

统计高6位的话,有64*64*64=262144种可能的颜色,不想开辟这么长的数组,况且图片不一定有这么多颜色。于是改用链表。用链表的话,统计到多少种颜色,链表就有多长,对于色彩单一的图片来说,可以节省很多不必要的空间开销。但是对于大尺寸图片,会在查找颜色是否存在的过程中消耗更多时间。

思路:新建一个链表,遍历图片的每个像素,如果此像素的颜色在链表中存在,此颜色的记录值加一;否则把新颜色加入链表。最后给链表逆序排序,取链表的前256种颜色作为颜色表。

/*  FILE : EasyBMP.cpp  */
bool CreatePerfectColorTable(BMP&InputImage)
{using namespace std;int BitDepth = InputImage.TellBitDepth();if( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 ){if( EasyBMPwarnings ){cout << "EasyBMP Warning: Attempted to create color table at a bit" << endl<< "                 depth that does not require a color table." << endl<< "                 Ignoring request." << endl;}return false;}int i;list<struct Nm> Num;for(int row = 0; row < InputImage.TellHeight(); row++){for(int col = 0; col < InputImage.TellWidth(); col++){i = 0;i |= (InputImage(col,row)->Red&0b11111100)<<10;i |= (InputImage(col,row)->Green&0b11111100)<<4;i |= (InputImage(col,row)->Blue&0b11111100)>>2;bool is_find = false;for(list<struct Nm>::iterator itor = Num.begin();itor!=Num.end();itor++){if(itor->num == i){is_find = true;itor->count++;break;}}if(!is_find){struct Nm newNum;newNum.num = i;newNum.count = 1;Num.push_back(newNum);}}}Num.sort(cmp);int NumberOfColors = InputImage.TellNumberOfColors();list<struct Nm>::iterator itor = Num.begin();for( i=0 ; i < NumberOfColors ; i++ ){RGBApixel TempColor;TempColor.Red   = (ebmpBYTE)((itor->num&0b111111000000000000)>>10);TempColor.Green = (ebmpBYTE)((itor->num&0b000000111111000000)>>4);TempColor.Blue  = (ebmpBYTE)((itor->num&0b000000000000111111)<<2);TempColor.Alpha = 0;InputImage.SetColor( i , TempColor ); itor++; }return true;
}

结果:

效果好多了。

 


http://www.ppmy.cn/news/556352.html

相关文章

256色图像不一定是灰度图像。

256色图像属于伪彩色图像&#xff0c;但是他也是8位的呀&#xff0c;他和256灰度图有什么差别&#xff1f;256色图像是如何表示颜色的&#xff1f; 灰度图像&#xff1a;每个像素由一个字节&#xff0c;即是八位&#xff0c;共256种色&#xff0c;且红绿蓝三原色分量相等。&…

32位色彩的真实含义 -- 24位色彩, 8位透明度

http://itbbs.pconline.com.cn/diy/9142730.html ----------------------------------------------------------------- 32位色彩的真实含义 -- 24位色彩, 8位透明度 32位色比24位色的色彩没有区别, 多出来的8位是用来体现半透明的程度的, GUI系统称之为Alpha Blending的.如…

诊断测试工具CANoe.DiVa从入门到精通系列——开门见山

我是穿拖鞋的汉子,魔都中坚持长期主义的工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 人们会在生活中不断攻击你。他们的主要武器是向你灌输对自己的怀疑:你的价值、你的能力、你的潜力。他们往往会将此伪装成客观意见,但无一例外的是,他们想…

Nginx 的学习与实战【一】

Nginx 的学习与实战【一】 一、Nginx 简介 1.、概述 Nginx 是俄罗斯人编写的十分轻量级的 HTTP 服务器,Nginx&#xff0c;它的发音为“engine X”&#xff0c;是一个高性能的HTTP和反向代理服务器&#xff0c;同时也是一个 IMAP/POP3/SMTP 代理服务器。 Nginx是开源、高性能…

密码破解全能工具:Hashcat密码破解攻略

来源 https://www.freebuf.com/sectool/164507.html Hashcat密码破解 hashcat号称世界上最快的密码破解&#xff0c;世界上第一个和唯一的基于GPGPU规则引擎&#xff0c;免费多GPU&#xff08;高达128个GPU&#xff09;&#xff0c;多哈希&#xff0c;多操作系统&#xff08;L…

密码破解—Hashcat

一、Hashcat简介 hashcat号称世界上最快的密码破解&#xff0c;世界上第一个唯一的基于GPU、CPU规则引擎&#xff0c;免费多GPU&#xff08;高达128个GPU&#xff09;&#xff0c;多哈希&#xff0c;多操作系统&#xff08;Linux和Windows本地二进制文件&#xff09;&#xff…

Centos 7系统密码破解

我们有的机器可能经过好几个人手之后&#xff0c;密码忘记了&#xff0c;需要破解密码 话不多说&#xff0c;开始 破解CentOS7密码详细过程 一、开机重启&#xff0c;按任意键停住&#xff0c;有时没停住&#xff0c;是鼠标键未在服务器中&#xff0c;点一下就好 二、按e键进…

Kali渗透测试:散列密码破解

Kali渗透测试&#xff1a;散列密码破解 某些网站的安全机制设置有缺陷&#xff0c;导致自身的关键数据库被渗透。很多用户在不同网站使用的是相同的用户名和密码&#xff0c;因此黑客可以通过获取通过获取用户在A网站的用户名和密码从而尝试登录B网站&#xff0c;这就是“撞库…