本博文内容是博文基于MFC框架的图像缩放算法示例的一部分(返回目录)。
博文03. 用C++类和对象封装BMP显示的相关代码引入C++类的概念,将所用BMP处理相关的代码,包括读取BMP,分离R,G,B通道和绘制BMP等用一个CBmp类进行封装,实现了对BMP图像的读取,分离和绘制的功能。接下来我们介绍图像缩放算法的实现,并用BMP展示。
经典的图像缩放算法有最近邻算法、双线性算法和双三次算法三类,依次复杂度增加,缩放效果也依次改善。本博文系列为逐个介绍这三种算法。本文实现最简单的最近邻算法。
step 01: 为工程添加一个名为CScaleNearst的类
在名为MFC_SD_03的工程依次点击菜单项【项目】->【添加类】,会出现添加新的界面,填好类名CScaleNearst
和文件名Scale_Nearst.h
和Scale_Nearst.cpp
点击【确定】按钮即可。
将Scale_Nearst.h
修改内容如下:
#pragma once
class CScaleNearst
{
public:
CScaleNearst();
~CScaleNearst();
public:
int src_pix_pos_nearest(int in_width, int out_width, int dst_pos, int* src_pos);
int line_scale_nearest(unsigned char* in_line, int in_width,
unsigned char* out_line, int out_width);
int col_scale_nearest(unsigned char* in_line, int in_height,
unsigned char* out_line, int out_height, int out_width);
int frame_scale_nearest(int in_width, int in_height, unsigned char* ch_img_in,
int dst_width, int dst_height,unsigned char* ch_img_out);
};
将Scale_Nearst.cpp
修改内容如下:
#include "pch.h"
#include "Scale_nearst.h"
CScaleNearst::CScaleNearst() { }
CScaleNearst::~CScaleNearst() { }
// get source pixel position from destination postion
int CScaleNearst::src_pix_pos_nearest(int in_width, int out_width, int dst_pos, int* src_pos) {
float scale = (float)(in_width) / (float)out_width;
float srcpos_f; //int srcpos_i;
//get float position
srcpos_f = dst_pos * scale;
//change interger type
*src_pos = (int)(srcpos_f + 0.5);
//Boundary Pixel Consideration
for (int i = 0; i < 4; i++) {
if (*src_pos < 0) *src_pos = 0;
if (*src_pos > (in_width - 1)) *src_pos = in_width - 1;
}
return 0;
}
//
int CScaleNearst::line_scale_nearest(unsigned char* in_line, int in_width,
unsigned char* out_line, int out_width) {
int src_pos;
//float tmp = 0;
for (int i = 0; i < out_width; i++)
{
src_pix_pos_nearest(in_width, out_width, i, &src_pos);
out_line[i] = in_line[src_pos];
}
return 0;
}
int CScaleNearst::col_scale_nearest(unsigned char* in_line, int in_height,
unsigned char* out_line, int out_height, int out_width) {
int src_pos;
for (int i = 0; i < out_height; i++)
{
src_pix_pos_nearest(in_height, out_height, i, &src_pos);
out_line[i * out_width] = in_line[src_pos * out_width];
}
return 0;
}
int CScaleNearst::frame_scale_nearest(
int in_width, int in_height,
unsigned char* ch_img_in,
int dst_width, int dst_height,
unsigned char* ch_img_out
) {
unsigned char* tmp_frame = new unsigned char[1920*1080];
for (int i = 0; i < in_height; i++) {
line_scale_nearest(ch_img_in + i * in_width, in_width, tmp_frame + i * dst_width, dst_width);
}
for (int i = 0; i < dst_width; i++) {
col_scale_nearest(tmp_frame + i, in_height, ch_img_out + i, dst_height, dst_width);
}
delete[] tmp_frame;
return 0;
}
step 02: 为工程添加鼠标右键消息并进行图像缩放
前一篇博文我们已经实现了鼠标点击左键时绘制BMP原图,为了比对缩放图像和原图,我们再次添加鼠标右键的消息,在其消息响应函数内编写图像缩放和显示的代码。
有了CBmp和CScaleNearst两个类,我们就可以分步实现图像缩放了。即
- 读取图像
CBmp::readBmp()
- 分离通道
CBmp::separateRGB()
- 缩放每个通道
CScaleNearst::frame_scale_nearest
- 合并绘制BMP图像
CBmp::print_matrix
#include "Scale_nearst.h"
void CMFCSD03View::OnRButtonDown(UINT nFlags, CPoint point)
{
// TODO: 在此添加消息处理程序代码和/或调用默认值
CBmp bmp;
CScaleNearst scaler;
//要绘制的图像文件名
char bmpName[] = "1.bmp";
unsigned char* red_channel = new unsigned char[1920 * 1080];
unsigned char* green_channel = new unsigned char[1920 * 1080];
unsigned char* blue_channel = new unsigned char[1920 * 1080];
unsigned char* img_data = new unsigned char[1920 * 1080 * 3];
int bmpWidth = 0;
int bmpHeight = 0;
int biBitCount = 0;
int lineByte = 0;
//read bmp image
bmp.readBmp(bmpName, img_data, &bmpWidth, &bmpHeight, &biBitCount, &lineByte);
//separate to three channels
bmp.separateRGB(img_data,
red_channel, green_channel, blue_channel,
bmpWidth, bmpHeight, lineByte);
//scale the image
int dstWidth = bmpWidth / 2;
int dstHeight = bmpHeight / 2;
scaler.frame_scale_nearest(
bmpWidth, bmpHeight, red_channel,
dstWidth, dstHeight, red_channel);
scaler.frame_scale_nearest(
bmpWidth, bmpHeight, green_channel,
dstWidth, dstHeight, green_channel);
scaler.frame_scale_nearest(
bmpWidth, bmpHeight, blue_channel,
dstWidth, dstHeight, blue_channel);
//合并绘制三个通道的图像
CClientDC dc(this);
CDC* pDC = &dc;
int offset_left = point.x;
int offset_top = point.y;
bmp.print_matrix(pDC,
red_channel, green_channel, blue_channel,
dstWidth, dstHeight,
offset_left, offset_top);
//important to clear the memory used
delete[] red_channel;
delete[] green_channel;
delete[] blue_channel;
delete[] img_data;
CView::OnRButtonDown(nFlags, point);
}
编译执行MFC工程,会发现每次点击鼠标左键都会以点击的位置为左上角绘制指定的BMP图像原图,而点击右键时会绘制缩放后的BMP图像。