genesys: Implement support for host-side calibration

merge-requests/195/head
Povilas Kanapickas 2019-09-18 03:54:52 +03:00
rodzic cf5f30dec3
commit 323f37753a
7 zmienionych plików z 173 dodań i 1 usunięć

Wyświetl plik

@ -1022,6 +1022,11 @@ void sanei_genesys_init_shading_data(Genesys_Device* dev, const Genesys_Sensor&
int pixels_per_line)
{
DBG_HELPER(dbg);
if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) {
return;
}
int channels;
int i;
@ -2400,6 +2405,11 @@ compute_shifted_coefficients (Genesys_Device * dev,
static void genesys_send_shading_coefficient(Genesys_Device* dev, const Genesys_Sensor& sensor)
{
DBG_HELPER(dbg);
if (dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) {
return;
}
uint32_t pixels_per_line;
uint8_t channels;
int o;

Wyświetl plik

@ -1058,7 +1058,8 @@ static void gl843_init_optical_regs_scan(Genesys_Device* dev, const Genesys_Sens
r = sanei_genesys_get_address (reg, REG01);
r->value &= ~REG01_SCAN;
if ((session.params.flags & SCAN_FLAG_DISABLE_SHADING) ||
(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION ||
(dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE)))
{
r->value &= ~REG01_DVDSET;
}

Wyświetl plik

@ -653,6 +653,55 @@ bool ImagePipelineNodeExtract::get_next_row_data(std::uint8_t* out_data)
return got_data;
}
ImagePipelineNodeCalibrate::ImagePipelineNodeCalibrate(ImagePipelineNode& source,
const std::vector<std::uint16_t>& bottom,
const std::vector<std::uint16_t>& top) :
source_{source}
{
auto size = std::min(bottom.size(), top.size());
offset_.reserve(size);
multiplier_.reserve(size);
for (std::size_t i = 0; i < size; ++i) {
offset_.push_back(bottom[i] / 65535.0f);
multiplier_.push_back(65535.0f / (top[i] - bottom[i]));
}
}
bool ImagePipelineNodeCalibrate::get_next_row_data(std::uint8_t* out_data)
{
bool ret = source_.get_next_row_data(out_data);
auto format = get_format();
auto depth = get_pixel_format_depth(format);
std::size_t max_value = 1;
switch (depth) {
case 8: max_value = 255; break;
case 16: max_value = 65535; break;
default:
throw SaneException("Unsupported depth for calibration %d", depth);
}
unsigned channels = get_pixel_channels(format);
std::size_t max_calib_i = offset_.size();
std::size_t curr_calib_i = 0;
for (std::size_t x = 0, width = get_width(); x < width && curr_calib_i < max_calib_i; ++x) {
for (unsigned ch = 0; ch < channels && curr_calib_i < max_calib_i; ++ch) {
std::int32_t value = get_raw_channel_from_row(out_data, x, ch, format);
float value_f = static_cast<float>(value) / max_value;
value_f = (value_f - offset_[curr_calib_i]) * multiplier_[curr_calib_i];
value_f = std::round(value_f * max_value);
value = clamp<std::int32_t>(static_cast<std::int32_t>(value_f), 0, max_value);
set_raw_channel_to_row(out_data, x, ch, value, format);
curr_calib_i++;
}
}
return ret;
}
ImagePipelineNodeDebug::ImagePipelineNodeDebug(ImagePipelineNode& source,
const std::string& path) :
source_(source),

Wyświetl plik

@ -474,6 +474,29 @@ private:
std::vector<uint8_t> cached_line_;
};
// A pipeline node that mimics the calibration behavior on Genesys chips
class ImagePipelineNodeCalibrate : public ImagePipelineNode
{
public:
ImagePipelineNodeCalibrate(ImagePipelineNode& source, const std::vector<std::uint16_t>& bottom,
const std::vector<std::uint16_t>& top);
std::size_t get_width() const override { return source_.get_width(); }
std::size_t get_height() const override { return source_.get_height(); }
PixelFormat get_format() const override { return source_.get_format(); }
bool eof() const override { return source_.eof(); }
bool get_next_row_data(std::uint8_t* out_data) override;
private:
ImagePipelineNode& source_;
std::vector<float> offset_;
std::vector<float> multiplier_;
};
class ImagePipelineNodeDebug : public ImagePipelineNode
{
public:

Wyświetl plik

@ -1670,6 +1670,19 @@ void build_image_pipeline(Genesys_Device* dev, const ScanSession& session)
"_2_after_stagger.pnm");
}
if ((dev->model->flags & GENESYS_FLAG_CALIBRATION_HOST_SIDE) &&
!(dev->model->flags & GENESYS_FLAG_NO_CALIBRATION))
{
dev->pipeline.push_node<ImagePipelineNodeCalibrate>(dev->dark_average_data,
dev->white_average_data);
if (DBG_LEVEL >= DBG_io2) {
dev->pipeline.push_node<ImagePipelineNodeDebug>("gl_pipeline_" +
std::to_string(s_pipeline_index) +
"_3_after_calibrate.pnm");
}
}
if (session.output_pixels != session.params.get_requested_pixels()) {
dev->pipeline.push_node<ImagePipelineNodeScaleRows>(session.params.get_requested_pixels());
}

Wyświetl plik

@ -136,6 +136,8 @@
#define GENESYS_FLAG_FULL_HWDPI_MODE (1 << 19) /**< scanner always use maximum hw dpi to setup the sensor */
// scanner has infrared transparency scanning capability
#define GENESYS_FLAG_HAS_UTA_INFRARED (1 << 20)
// scanner calibration is handled on the host side
#define GENESYS_FLAG_CALIBRATION_HOST_SIDE (1 << 21)
#define GENESYS_HAS_NO_BUTTONS 0 /**< scanner has no supported button */
#define GENESYS_HAS_SCAN_SW (1 << 0) /**< scanner has SCAN button */

Wyświetl plik

@ -424,6 +424,78 @@ void test_node_pixel_shift_lines()
ASSERT_EQ(out_data, expected_data);
}
void test_node_calibrate_8bit()
{
using Data = std::vector<std::uint8_t>;
Data in_data = {
0x20, 0x38, 0x38
};
std::vector<std::uint16_t> bottom = {
0x1000, 0x2000, 0x3000
};
std::vector<std::uint16_t> top = {
0x3000, 0x4000, 0x5000
};
ImagePipelineStack stack;
stack.push_first_node<ImagePipelineNodeArraySource>(1, 1, PixelFormat::RGB888,
std::move(in_data));
stack.push_node<ImagePipelineNodeCalibrate>(bottom, top);
ASSERT_EQ(stack.get_output_width(), 1u);
ASSERT_EQ(stack.get_output_height(), 1u);
ASSERT_EQ(stack.get_output_row_bytes(), 3u);
ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB888);
auto out_data = stack.get_all_data();
Data expected_data = {
// note that we don't handle rounding properly in the implementation
0x80, 0xc1, 0x41
};
ASSERT_EQ(out_data, expected_data);
}
void test_node_calibrate_16bit()
{
using Data = std::vector<std::uint8_t>;
Data in_data = {
0x00, 0x20, 0x00, 0x38, 0x00, 0x38
};
std::vector<std::uint16_t> bottom = {
0x1000, 0x2000, 0x3000
};
std::vector<std::uint16_t> top = {
0x3000, 0x4000, 0x5000
};
ImagePipelineStack stack;
stack.push_first_node<ImagePipelineNodeArraySource>(1, 1, PixelFormat::RGB161616,
std::move(in_data));
stack.push_node<ImagePipelineNodeCalibrate>(bottom, top);
ASSERT_EQ(stack.get_output_width(), 1u);
ASSERT_EQ(stack.get_output_height(), 1u);
ASSERT_EQ(stack.get_output_row_bytes(), 6u);
ASSERT_EQ(stack.get_output_format(), PixelFormat::RGB161616);
auto out_data = stack.get_all_data();
Data expected_data = {
// note that we don't handle rounding properly in the implementation
0x00, 0x80, 0xff, 0xbf, 0x00, 0x40
};
ASSERT_EQ(out_data, expected_data);
}
void test_image_pipeline()
{
test_image_buffer_genesys_usb();
@ -438,4 +510,6 @@ void test_image_pipeline()
test_node_split_mono_lines();
test_node_component_shift_lines();
test_node_pixel_shift_lines();
test_node_calibrate_8bit();
test_node_calibrate_16bit();
}