文章目录
前言
本blog记录一下自己在学习医学图像处理这门课做的作业时容易忘记的部分,以后忘了可以有查阅的地方
整个实验是用c++ qt做的,踩了不少坑,看着最后代码被整合成一个个界面,最后有一个可以使用的界面还是挺开心的,希望以后能保持这份初心
实验一共九个,点击对应实验看自己踩的坑
一、实验一和二 读取图片,显示rgb和hsv并修改hsv
这个实验主要就是学习下怎么使用qt了,在这个实验坑都踩一遍,后面就快多了。
图像的显示
本来课程要使用mfc,mfc属实有点老了,老师说也可以用qt就直接用qt了,qt方便在很多函数可以通过设计类直接"画图"解决,像是槽函数直接在ui类上点击转到槽,框架就搭好了,直接实现功能就好了。
刚开始找网上的教程时用的都是Label控件,后面添加一个鼠标操作需要获取图片坐标,才了解到QGrapihcsView控件,这个更方便一点
QGraphicsScene *scene = new QGraphicsScene;
scene->clear();
scene->clearFocus();
scene->addPixmap(QPixmap::fromImage(img));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
鼠标事件
添加鼠标事件要记得在对应的头文件里面重构虚函数
protected:
void mousePressEvent(QMouseEvent *event);
void mouseMoveEvent(QMouseEvent *event);
鼠标的event会留下坐标,应用到GraphicsView的坐标系需要使用mapToScene
auto pos_img = ui->graphicsView->mapToScene(event->pos()); //将鼠标所指的坐标转换到图片里的坐标
滑动条的显示
滑动条slider的数值只能设置为整数,但可以在使用的时候除以或乘上对应的数来变成小数来使用
void test012::on_horizontalSlider_3_valueChanged(int value)
{
//获得的value只能是整数,但可以获取后除以某个数再显示成小数
ui->label_getV->setText(QString::number(value/100.0));
...
}
再放张界面
二、实验三 图像增强(线性变换、空域滤波、伪彩色增强等)
这部分没什么难度,主要就是要实现的比较多,跟着公式一顿猛敲
直方图的实现
老师不让用其余外部库,只通过c++和qt实现画直方图,查阅后才知道有QPainter支持画图,具体的网上都有,这里贴一下自己的代码
void test03::drawHisto(QImage& image)
{
//显示直方图
const int histogramSize = 256; // 256 gray levels
int histogram[histogramSize] = {0};
// Convert image to grayscale and calculate histogram
QImage grayImage = image.convertToFormat(QImage::Format_Grayscale8);
for (int y = 0; y < grayImage.height(); ++y)
{
uchar *line = grayImage.scanLine(y);
for (int x = 0; x < grayImage.width(); ++x)
{
int grayValue = line[x];
histogram[grayValue]++;
}
}
int maxHistogramValue = 0;
for (int i = 0; i < histogramSize; ++i)
{
if (histogram[i] > maxHistogramValue)
maxHistogramValue = histogram[i];
}
double scale = (double)maxHistogramValue / 100.0; // 使直方图不超过100px高
QImage histogramImage(histogramSize, 100, QImage::Format_ARGB32);
histogramImage.fill(Qt::white);
QPainter painter(&histogramImage);
painter.setBrush(Qt::black);
// 绘制直方图
for (int i = 0; i < histogramSize; ++i) {
int rectHeight = static_cast<int>(histogram[i] / scale);
painter.drawRect(i, 100 - rectHeight, 1, rectHeight);
}
// 绘制坐标轴
const int tickSize = 20;
for (int i = 0; i < histogramSize; i += tickSize) {
painter.drawLine(i, 100, i, 105);
painter.drawText(i-10, 115, QString::number(i));
}
QGraphicsScene *histogramScene = new QGraphicsScene; //直方图的scene
histogramScene->clear();
histogramScene->clearFocus();
histogramScene->addPixmap(QPixmap::fromImage(histogramImage));
ui->graphicsView_2->setScene(histogramScene);
ui->graphicsView_2->show();
}
均衡化和规格化
难点就在均衡化和规格化了,具体的流程其他博客都有,这里主要记录下自己的实现过程
//均衡化实现
void test03::on_pushButton_9_clicked()
{
if(fileName_test03.isEmpty())
{
qDebug() << "没有图片";
return;
}
else
{
QImage img_gNew = img_gray;
// 直方图均衡化
QVector<int> histogram;
histogram.resize(256);
for (int i = 0; i < img_gNew.height(); ++i) {
for (int j = 0; j < img_gNew.width(); ++j) {
int gray = qGray(img_gNew.pixel(j, i));
histogram[gray]++;
}
}
QVector<double> cdf(histogram.size());
cdf[0] = static_cast<double>(histogram[0]) / (img_gNew.width() * img_gNew.height());
for (int i = 1; i < histogram.size(); ++i) {
cdf[i] = cdf[i - 1] + static_cast<double>(histogram[i]) /
(img_gNew.width() * img_gNew.height());
}
QImage equalizedImage = img_gNew.convertToFormat(QImage::Format_ARGB32);
for (int i = 0; i < equalizedImage.height(); ++i) {
for (int j = 0; j < equalizedImage.width(); ++j) {
int gray = qGray(equalizedImage.pixel(j, i));
int equalizedGray = static_cast<int>(cdf[gray] * 255);
equalizedImage.setPixel(j, i, qRgb(equalizedGray, equalizedGray, equalizedGray));
}
}
QGraphicsScene *scene = new QGraphicsScene;
scene->clear();
scene->clearFocus();
scene->addPixmap(QPixmap::fromImage(equalizedImage));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
drawHisto(equalizedImage);
}
}
//规格化的实现
void test03::on_pushButton_10_clicked()
{
if(fileName_test03.isEmpty())
{
qDebug() << "没有图片";
return;
}
else
{
QString fileName_con = QFileDialog::getOpenFileName(
this,
tr("选择图片."),
"C:",
tr("whatever(*.*)")
);
QImage *img_con = new QImage(fileName_con);
QImage img_gNew = img_gray;
//获取原图灰度直方图的cdf
QVector<int> histogram;
histogram.resize(256);
for (int i = 0; i < img_gNew.height(); ++i) {
for (int j = 0; j < img_gNew.width(); ++j) {
int gray = qGray(img_gNew.pixel(j, i));
histogram[gray]++;
}
}
QVector<double> cdf(histogram.size());
cdf[0] = static_cast<double>(histogram[0]) / (img_gNew.width() * img_gNew.height());
for (int i = 1; i < histogram.size(); ++i) {
cdf[i] = cdf[i - 1] + static_cast<double>(histogram[i]) /
(img_gNew.width() * img_gNew.height());
}
//获取对比图灰度直方图的cdf
QVector<int> histogram_con;
histogram_con.resize(256);
for (int i=0; i < img_con->width(); i++)
{
for (int j=0; j < img_con->height(); j++)
{
int gray = qGray(img_con->pixel(i, j));
histogram_con[gray]++;
}
}
QVector<double> cdf_con(histogram_con.size());
cdf_con[0] = static_cast<double>(histogram_con[0]) / (img_con->width() * img_con->height());
for (int i=1; i < histogram_con.size(); i++)
{
cdf_con[i] = cdf_con[i - 1] + static_cast<double>(histogram_con[i])
/ (img_con->width() * img_con->height());
}
double min = 1; //设置成一个≥1的数即可
uchar map[256]; //输入->输出的映射关系 //输入->输出的映射关系
uchar groop[256]; //分组序号
for (int i = 0; i < 256; i++)
{
groop[i] = -1; //初始化
}
//比较大小寻找映射关系
for (int i=0; i < histogram_con.size(); i++)
{
if(histogram_con[i] == 0) //如果该位置的直方图为0,可以跳出这次循环了,因为不会有点映射到这里来
{
groop[i] = groop[i - 1];
continue;
}
min = 1;
for (int j=0; j < histogram_con.size(); j++) //遍历原图像,寻找两个直方图距离最接近的点
{
if(std::abs(cdf[j] - cdf_con[i]) < min)
{
min = std::abs(cdf[j] - cdf_con[i]);
groop[i] = j; //最接近的直方图位置(原图像),记录分组序号
}
}
if (i == 0) //灰度值为0的情况有点特殊
{
for (int pos = 0; pos <= groop[i]; pos++)
map[pos] = 0;
}
else
{
for (int pos = groop[i - 1] + 1; pos <= groop[i]; pos++) //属于同一组内的元素,映射到同一个灰度值
map[pos] = i; //建立映射关系
}
}
QImage equalizedImage = img_gNew.convertToFormat(QImage::Format_ARGB32);
for (int i=0; i < equalizedImage.width(); i++)
{
for (int j=0; j < equalizedImage.height(); j++)
{
int gray = qGray(equalizedImage.pixel(i,j));
//qDebug() << "old " << gray;
int new_gray = map[gray];
//qDebug() << "new " << new_gray;
equalizedImage.setPixel(i, j, qRgb(new_gray, new_gray, new_gray));
}
}
QGraphicsScene *scene = new QGraphicsScene;
scene->clear();
scene->clearFocus();
scene->addPixmap(QPixmap::fromImage(equalizedImage));
ui->graphicsView->setScene(scene);
ui->graphicsView->show();
drawHisto(equalizedImage);
}
}
伪彩色增强
因为是看医学图像,老师给了专门的增强模式,直接算rgb对应的变换就行了
就是只给了样式没给坐标,只能自己看个大概了然后算一下线性变换长什么样。。
同样贴一个界面
三、实验四 图像的运算(加减运算、逻辑运算、镜像、缩放)
这部分也是没什么难点,用运算符号一顿猛敲就完事
缩放一开始比较难理解,后面理解了就是扩大的话,原图没有的像素点就用0填充,缩小就按比例找对应的点放到缩小的图里面就行了
//缩放或扩大
void scale(QImage& img, double scale_factor)
{
QImage canvas(img.width() * scale_factor, img.height() * scale_factor, QImage::Format_ARGB32);
for (int y = 0; y < canvas.height(); y++) {
QRgb *row = (QRgb*)canvas.scanLine(y);
for (int x = 0; x < canvas.width(); x++) {
int px = x / scale_factor, py = y / scale_factor;
if (px >= img.width() || py >= img.height()) {
row[x] = qRgb(0, 0, 0);
} else {
QRgb c = img.pixel(px, py);
row[x] = c;
}
}
}
img = canvas;
}
后面的实验五到实验九基本和傅里叶变换有关,就留到后面吧