Skip to content

Commit

Permalink
Add a tone map tab and a button to calculate a tone map.
Browse files Browse the repository at this point in the history
  • Loading branch information
steveicarus committed Mar 12, 2011
1 parent 6df227f commit 9d21301
Show file tree
Hide file tree
Showing 5 changed files with 336 additions and 4 deletions.
113 changes: 111 additions & 2 deletions src/AstrobenchMain.cpp
Expand Up @@ -18,6 +18,7 @@
*/

# include <qapplication.h>
# include <QDoubleSpinBox>
# include <QFileDialog>
# include <QGraphicsPixmapItem>
# include <QGraphicsScene>
Expand All @@ -35,15 +36,29 @@ AstrobenchMain::AstrobenchMain(QWidget*parent)

ui.setupUi(this);

// Get the initial stack bock count so that we know where to
// add new stack images.
stack_box_count_ = ui.stack_box->count();

display_pixmap_ = 0;
display_scene_ = new QGraphicsScene(ui.image_display_view);
ui.image_display_view ->setScene(display_scene_);

tone_map_lut_pixmap_ = 0;
tone_map_lut_scene_ = new QGraphicsScene(ui.tone_map_graph);
ui.tone_map_graph->setScene(tone_map_lut_scene_);

// Signals from the Stack Processing tab
connect(ui.open_next_image_button, SIGNAL(clicked()),
SLOT(open_next_image_button_slot_()));
connect(ui.stack_next_image_button, SIGNAL(clicked()),
SLOT(stack_next_image_button_slot_()));

// Signals from the Tone map tab
connect(ui.tone_map_calculate, SIGNAL(clicked()),
SLOT(tone_map_calculate_slot_()));
connect(ui.tone_map_apply, SIGNAL(clicked()),
SLOT(tone_map_apply_slot_()));
}

AstrobenchMain::~AstrobenchMain()
Expand Down Expand Up @@ -105,7 +120,7 @@ void AstrobenchMain::stack_next_image_button_slot_()
label = QString("Stack item %1").arg(pos);
}

ui.stack_box->insertItem(1, cur, label);
ui.stack_box->insertItem(stack_box_count_, cur, label);
cur->show();

if (base != 0) {
Expand All @@ -115,6 +130,99 @@ void AstrobenchMain::stack_next_image_button_slot_()
}
}

void AstrobenchMain::tone_map_calculate_slot_()
{
/* If the image stack is empty, then clear some of the tone
map calculations and give up. */
if (stack_.size() == 0) {
ui.tone_map_input_max->setText(QString("N/A"));
return;
}

StackItemWidget*item = stack_.front();

QString text;

unsigned long pixel_max = item->accum_pixel_max();
text = QString("%1").arg(pixel_max);
ui.tone_map_input_max->setText(text);

double gamma = ui.tone_map_gamma->value();
if (gamma == 0.0)
gamma = 1.0;
else
gamma = 1.0/gamma;

// Create a LUT that is big enough to map the range of the
// accumulated image, to the 16bit target range.
unsigned short*lut_data = new unsigned short[3 * pixel_max];

for (unsigned idx = 0 ; idx < pixel_max ; idx += 1) {
unsigned short*lut_ptr = lut_data + 3*idx;

unsigned long tmp = pow((double)idx / (double)pixel_max, gamma) * 0x10000;
if (tmp > 0xffff) tmp = 0xffff;
lut_ptr[0] = tmp;
lut_ptr[1] = tmp;
lut_ptr[2] = tmp;
}

// Put the lut into a VImage in a way that it can be garbate
// collected. Then drop the malually allocated lut buffer.
tone_map_lut_ = VImage(lut_data, pixel_max, 1, 3, VImage::FMTUSHORT);

QSize lut_image_size = ui.tone_map_graph->size();
int use_width = lut_image_size.width() - 2;
int use_height = lut_image_size.height() - 2;
QImage lut_image (use_width, use_height, QImage::Format_RGB32);

for (int idx = 0 ; idx < use_width ; idx += 1) {
int ptr = idx * pixel_max / use_width;
if (ptr >= pixel_max) ptr = pixel_max-1;
ptr *= 3;

int valr = lut_data[ptr+0];
int valg = lut_data[ptr+1];
int valb = lut_data[ptr+2];

valr = valr * use_height / 0x10000;
valg = valg * use_height / 0x10000;
valb = valb * use_height / 0x10000;

if (valr >= use_height) valr = use_height-1;
if (valg >= use_height) valg = use_height-1;
if (valb >= use_height) valb = use_height-1;

valr = use_height - 1 - valr;
valg = use_height - 1 - valg;
valb = use_height - 1 - valb;

QRgb tmp;
tmp = lut_image.pixel(idx, valr);
tmp = qRgb(255, 0, 0);
lut_image.setPixel(idx, valr, tmp);

tmp = lut_image.pixel(idx, valg);
tmp = qRgb(qRed(tmp), 255, 0);
lut_image.setPixel(idx, valg, tmp);

tmp = lut_image.pixel(idx, valb);
tmp = qRgb(qRed(tmp), qGreen(tmp), 255);
lut_image.setPixel(idx, valb, tmp);
}

if (tone_map_lut_pixmap_)
delete tone_map_lut_pixmap_;

tone_map_lut_pixmap_ = tone_map_lut_scene_->addPixmap(QPixmap::fromImage(lut_image));
tone_map_lut_pixmap_->show();
delete[]lut_data;
}

void AstrobenchMain::tone_map_apply_slot_()
{
}

static void draw_from_rgb(QImage&dst, vips::VImage&img)
{
const unsigned char*data = (const unsigned char*)img.data();
Expand Down Expand Up @@ -143,7 +251,8 @@ static void draw_from_gray(QImage&dst, vips::VImage&img)

void AstrobenchMain::display_image(vips::VImage&img)
{
vips::VImage display_image = img.scale();
double display_gamma = 1 / ui.tools_gamma->value();
vips::VImage display_image = img.scale().gammacorrect(display_gamma);
QImage tmp (display_image.Xsize(), display_image.Ysize(), QImage::Format_RGB32);

int planes = display_image.Bands();
Expand Down
11 changes: 11 additions & 0 deletions src/AstrobenchMain.h
Expand Up @@ -44,17 +44,28 @@ class AstrobenchMain : public QMainWindow {
// The user interface...
Ui::AstrobenchMainWidget ui;

// This is the initial number of pages in the stack box. It is
// used to know where stack images can go.
int stack_box_count_;

// Variables for managing the image display window.
QGraphicsScene*display_scene_;
QGraphicsPixmapItem*display_pixmap_;

vips::VImage tone_map_lut_;
QGraphicsScene*tone_map_lut_scene_;
QGraphicsPixmapItem*tone_map_lut_pixmap_;

vips::VImage*next_image_;
QString next_path_;
std::list<StackItemWidget*> stack_;

private slots:
void open_next_image_button_slot_();
void stack_next_image_button_slot_();

void tone_map_calculate_slot_();
void tone_map_apply_slot_();
};

#endif
8 changes: 8 additions & 0 deletions src/StackItemWidget.cpp
Expand Up @@ -44,6 +44,7 @@ void StackItemWidget::set_image(const QString&path, const vips::VImage&img)
image_ = img;
processed_ = image_;
accumulated_ = image_;
accumulated_stats_ = accumulated_.stats();
ui.stack_item_path->setText(path);
ui.stack_item_path->setToolTip(path);
}
Expand Down Expand Up @@ -126,11 +127,18 @@ void StackItemWidget::calculate_offset_from(StackItemWidget*that)
processed_ = image_.extract_area(tx, ty, tw, th)
.embed(0, px, py, image_.Xsize(), image_.Ysize());
accumulated_ = processed_;
accumulated_stats_ = accumulated_.stats();
}

void StackItemWidget::calculate_stack_from(StackItemWidget*that)
{
accumulated_ = processed_ + that->accumulated_;
accumulated_stats_ = accumulated_.stats();
}

unsigned StackItemWidget::accum_pixel_max()
{
return accumulated_stats_(1,0);
}

void StackItemWidget::display_raw_slot_(void)
Expand Down
4 changes: 4 additions & 0 deletions src/StackItemWidget.h
Expand Up @@ -43,6 +43,9 @@ class StackItemWidget : public QWidget {
// argument, which is expected to the the previous top of the stack.
void calculate_stack_from(StackItemWidget*that);

// Statistics about the accumulated image.
unsigned accum_pixel_max();

private slots:
void display_raw_slot_(void);
void display_aligned_slot_(void);
Expand All @@ -68,6 +71,7 @@ class StackItemWidget : public QWidget {
// The accumulated is the processed_ image added to the
// previously accumulated images. It is this point in the stack.
vips::VImage accumulated_;
vips::VDMask accumulated_stats_;
};

#endif

0 comments on commit 9d21301

Please sign in to comment.