kopia lustrzana https://gitlab.com/sane-project/backends
genesys: Implement support for host-side calibration
rodzic
cf5f30dec3
commit
323f37753a
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue