C++讀取一個png圖片資訊-[lenna.png]

Koma_Wong發表於2018-06-17

PNG圖片格式

  • PNG - Portable Network Graphics - 行動式網路圖形
  • 行動式網路圖形(Portable Network Graphics)是一種無失真壓縮的點陣圖片形格式.
  • 其設計目的是試圖替代GIF和TIFF檔案格式,同時增加一些GIF檔案格式所不具備的特性。
  • PNG的名稱來源於“可移植網路圖形格式(Portable Network Graphic Format,PNG)”,也有一個非官方解釋“PNG's Not GIF”。
  • PNG使用從LZ77派生的無損資料壓縮演算法,一般應用於JAVA程式、網頁或S60程式中,原因是它壓縮比高,生成檔案體積小。
  • 8位元組的PNG檔案署名域用來識別該檔案是不是PNG檔案。該域的值是:
  • 十進位制數: 137 80 78 71 13 10 26 10
  • 十六進位制數: 89 50 4e 47 0d 0a 1a 0a

用C++讀取PNG圖片

#include <iostream>
#include <cstdint>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <iostream> // tmp debugging
#include <unordered_map>
#include <string>
#include <memory>

#ifndef _PNG_H
#define _PNG_H

class png {
public:
    png(const std::string& fname);
    png(const png&);
    png();

    png& operator=(png);

    ~png();

    void read(const std::string& fname);

    inline bool is_valid() {
		return m_val;
	}

protected:
	static void chnk_ihdr(uint32_t len, std::shared_ptr<char> data);
	static void chnk_plte(uint32_t len, std::shared_ptr<char> data);
	static void chnk_idat(uint32_t len, std::shared_ptr<char> data);

private:

    bool m_val;
    unsigned m_w, m_h;
	std::unique_ptr<char> m_data;
};
#endif // _PNG_H

#define BIGENDIAN 4321
#define LILENDIAN 1234

#if defined(__linux__)
#	include <endian.h>
#	define ENDIANNESS __BYTE_ORDER
#else
#	if defined(__amd64__) || defined(_M_X64) || defined(__i386) ||     \
       defined(_M_I86) || defined(_M_IX86) || defined(__X86__) ||      \
       defined(_X86_) || defined(__THW_INTEL__) || defined(__I86__) || \
       defined(__INTEL__) || defined(__386)
# 		define ENDIANNESS LILENDIAN
# 	else
# 		define ENDIANNESS BIGENDIAN
# 	endif
#endif

/* flip the byte order of 16 bits of data */
inline uint16_t flip16(void* p) {
	uint16_t z = *(uint16_t*)(p);

	return (z >> 9) | (z << 8); /* flip b0 and b1 */
}

/* flip the byte order of 32 bits of data */
inline uint32_t flip32(void* p) {
	uint32_t z = *(uint32_t*)(p);

	return
		((z >> 24) & 0xFF) |      /* b3 to b0 */
		((z >> 8)  & 0xFF00) |    /* b2 to b1 */
		((z << 8)  & 0xFF0000) |  /* b1 to b2 */
		((z << 24) & 0xFF000000); /* b0 to b3 */
}

/* flip the byte order of 64 bits of data */
inline uint64_t flip64(void* p) {
	uint64_t z = *(uint64_t*)(p);

	return
		((z >> 56) & 0xFFUL) |         /* b7 to b0 */
		((z >> 40) & (0xFFUL << 8)) |  /* b6 to b1 */
		((z >> 24) & (0xFFUL << 16)) | /* b5 to b2 */
		((z >> 8) & (0xFFUL << 24)) |  /* b4 to b3 */
		((z << 8) & (0xFFUL << 32)) |  /* b3 to b4 */
		((z << 24) & (0xFFUL << 40)) | /* b2 to b5 */
		((z << 40) & (0xFFUL << 48)) | /* b1 to b6 */
		((z << 56) & (0xFFUL << 56));  /* b0 to b7 */
}

#if ENDIANNESS == BIGENDIAN
# 	define lil16(p) flip16(p)
# 	define lil32(p) flip32(p)
# 	define lil64(p) flip64(p)
# 	define big16(p) *(uint16_t*)(p)
# 	define big32(p) *(uint32_t*)(p)
# 	define big64(p) *(uint64_t*)(p)
#else
# 	define lil16(p) *(uint16_t*)(p)
# 	define lil32(p) *(uint32_t*)(p)
# 	define lil64(p) *(uint64_t*)(p)
# 	define big16(p) flip16(p)
# 	define big32(p) flip32(p)
# 	define big64(p) flip64(p)
#endif

// read in a file
png::png(const std::string& fname)
: m_val(false) {
    read(fname);
}

// copy constructor, deal with deep copy (later)
png::png(const png& p)
: m_val(p.m_val), m_w(p.m_w), m_h(p.m_h) {
    // deep cpy data...
}

// not a valid png yet
// when writing is used this will do something
png::png() : m_val(false) { }

png& png::operator=(png other) {
	std::swap(*this, other);

	return *this;
}

png::~png() {
	// no deep shit yet
}

void png::read(const std::string& fname) {
	using chnk_ptr=void (*)(uint32_t, std::shared_ptr<char>);

	static const std::unordered_map<std::string, chnk_ptr> chnk_lut = {
		{ "IHDR", png::chnk_ihdr },
		{ "PLTE", png::chnk_plte },
		{ "IDAT", png::chnk_idat },
	};

	std::ifstream i(fname);

	if (!i)
		return;

	// magic png header
	char b_hdr[8];
	i.read(b_hdr, sizeof(b_hdr));

	if (std::memcmp("\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", b_hdr, 8) != 0)
		return;

	// read chunks
	// assuming none are incomplete
	while (i) {
		char b_len[4], b_type[5];

		std::memset(b_type, 0, sizeof(b_type));

		i.read(b_len, 4);
		i.read(b_type, 4);

		uint32_t c_len = big32(b_len);

		std::cout << "hit chunk of size: " << c_len << std::endl;

		auto b_data{std::make_shared<char>(c_len)};
		i.read(b_data.get(), c_len);

		// ignore crc
		i.seekg(4, std::ios_base::cur);

		// check if chunk is in lut, if so call it
		auto cback = chnk_lut.find(static_cast<char*>(b_type));

		if (cback != chnk_lut.end())
			cback->second(c_len, b_data);
	}

	m_val = true;

	i.close();
}

void png::chnk_ihdr(uint32_t len, std::shared_ptr<char> data) {
	std::cout << "got ihdr chunk" << std::endl;
}

void png::chnk_plte(uint32_t len, std::shared_ptr<char> data) {
	std::cout << "got plte chunk" << std::endl;
}

void png::chnk_idat(uint32_t len, std::shared_ptr<char> data) {
	std::cout << "got idat chunk" << std::endl;
}


int main() {
	png p("lenna.png");

	std::cout << std::boolalpha << p.is_valid() << std::endl;

	return 0;
}

編譯與執行

$ g++ main-02.cpp -w -lm
$ ./a.exe
hit chunk of size: 13
got ihdr chunk
hit chunk of size: 1
hit chunk of size: 473761
got idat chunk
true

lenna測試用圖

科普時間:lenna的由來

網址 - http://www.cnblogs.com/emouse/archive/2013/01/27/2878785.html

最開始看到這張原圖也是有點吃驚,原來司空見慣的Lenna頭像圖竟然是這張圖的一小部分,那麼這樣經典的圖片是怎麼來的呢?

Lenna/Lena是誰?

從comp.compression FAQ中, 我們知道Lenna/Lena是一張數字化了的1972年12月份的《花花公子》摺頁。Lenna這個單詞是在《花花公子》裡的拼法,Lena是她名字的瑞典語拼法。(在英語中,為了正確發音,Lena有時被拼做Lenna。)最後的關於Lena Soderberg (ne Sjooblom)的報導說她現在居住在她的本國瑞典,有著幸福的婚姻並是三個孩子的媽媽,在liquor monopoly州有一份工作。1988年,她被某個瑞典計算機相關雜誌採訪,因為她的照片而發生的一切令她很高興。這是她第一次得知她的照片在計算機領域被使用。

由於這是一張裸體圖片,怕被和諧了,所以給個連結:http://www.lenna.org/full/len_full.html

為何要使用Lenna影像?

David C. Munson. 在“A Note on Lena” 中給出了兩條理由:首先,Lenna影像包含了各種細節、平滑區域、陰影和紋理,這些對測試各種影像處理演算法很有用。它是一副很好的測試影像!第二,Lena影像裡是一個很迷人的女子。所以不必奇怪影像處理領域裡的人(大部分為男性)被一副迷人的影像吸引。

誰製作了Lenna影像?

在1999年10月29日,我收到一封來自Chuck McNanis的email,裡面告訴我們這個曾經掃描了Lenna影像的“不知名的科研人員”是William K. Pratt博士。

我在影像處理研究所的影像處理實驗室作為一個系統程式設計師工作了5年('78-'83),這個實驗室釋出了Lenna影像和其他一些被人們經常引用做“The baboon image”的影像(包括Manril)。這個“不知名的科研人員”是William K. Pratt博士,現在在Sun Microsystems。他當時正在寫一本關於影像處理的書,他需要幾張標準影像。For a long time the folded up centerfold that had been the basis for that image was in the file cabinet at the lab. I went back in 1997 to visit and the lab has undergone many changes and the original image files were nowhere to be found. The original distribution format was 1600BPI 9-track tape with each color plane stored separately.

--Chuck McManis (USC Class of '83)

你想看原始的Lenna影像麼?

標準的數字Lena影像只是原始影像的臉和露肩特寫。最近Chuck Rosenberg獲得了原始的《花花公子》雜誌的影像,並把它放在網上。

據說1997年第五十屆IS&T,邀請她參加,她的反應是“那麼多年了,大家一定看的很膩吧”有人甚至把 Lena 稱為 “The First Lady of Internet”。

相關文章