Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Dynamic image to file generates empty file without errors or warnings. #8

Closed
dunkyp opened this issue Jan 12, 2019 · 3 comments

Comments

@dunkyp
Copy link

dunkyp commented Jan 12, 2019

I was experimenting with wrapping the SANE library and everything worked great; other than writing to a jpeg.
The "doesn't work" section in the code below produces an empty file. I took a look at the tests and saw the conversion to a typed image and then a typed image view, gave it a try and it worked. Just wondering if I missed something/if the library should give some sort of warning for the way I tried to use the file io.

#include <sane/sane.h>

#include <selene/base/io/FileUtils.hpp>
#include <selene/img/dynamic/DynImage.hpp>
#include <selene/img/pixel/PixelTypeAliases.hpp>
#include <selene/img/typed/ImageTypeAliases.hpp>
#include <selene/base/io/FileWriter.hpp>
#include <selene/img_io/IO.hpp>

#include <selene/img/interop/ImageToDynImage.hpp>
#include <selene/img/interop/DynImageToImage.hpp>

#include <iostream>

int main(int argc, char **argv) {
    sane_init(nullptr, nullptr);
    const SANE_Device** device_list;
    sane_get_devices(&device_list, true);
    auto device = device_list[0];
    SANE_Handle handle;
    sane_open(device->name, &handle);
    
    constexpr auto test_picture = 10;
    constexpr auto mode = 2;

    constexpr auto grid = 3;
    constexpr auto colours = 2;

    auto mode_option =  sane_get_option_descriptor(handle, mode);
    sane_control_option(handle, mode, SANE_ACTION_SET_VALUE, (void*) mode_option->constraint.string_list[1], nullptr);
    
    auto option = sane_get_option_descriptor(handle, test_picture);
    sane_control_option(handle, test_picture, SANE_ACTION_SET_VALUE, (void*) option->constraint.string_list[grid], nullptr);

    SANE_Parameters params;
    sane_get_parameters(handle, &params);

    auto height = sln::PixelLength{params.lines};
    auto width = sln::PixelLength{params.pixels_per_line};
    short depth = params.depth / 8;
    short channels = params.format==SANE_FRAME_RGB?3:1;
    
    sln::DynImage input_buffer{{width, height, channels, depth}, sln::ImageRowAlignment{0}};

    sane_start(handle);
    int total_read = 0;
    int read = 0;
    while(total_read < input_buffer.total_bytes()) {
        sane_read(handle, reinterpret_cast<unsigned char*>(input_buffer.data<sln::PixelRGB_8u>()) + total_read, input_buffer.total_bytes(), &read);
        total_read += read;
    }
    sane_close(handle);


    // Doesn't work
    auto output_empty_filename_jpg = "empty_test.jpg";
    sln::write_image(input_buffer, sln::ImageFormat::JPEG, sln::FileWriter(output_empty_filename_jpg));

    // WORKS
    const sln::Image<sln::PixelRGB_8u> img = sln::to_image<sln::PixelRGB_8u>(std::move(input_buffer));
    const auto img_data_1 = sln::to_dyn_image_view(img);
    
    auto output_filename_jpg = "test.jpg";
    sln::write_image(img_data_1, sln::ImageFormat::JPEG, sln::FileWriter(output_filename_jpg));
}
@kmhofmann
Copy link
Owner

Hi Duncan, thanks for using Selene! I don't have the SANE library or a suitable device set up, so I can't reproduce your exact code right now. But could you check a couple of things for me? I suspect I know where the problem lies.

Please check the output of the problematic write_image call, and also any potential messages emitted. Just change the call to something like:

sln::MessageLog mlog;
bool could_write = sln::write_image(input_buffer, sln::ImageFormat::JPEG, sln::FileWriter(output_empty_filename_jpg), &mlog);
std::cout << "could_write = " << could_write << '\n';
for (const auto& msg : mlog.messages)
{
    std::cout << msg.text << '\n';
}

By design, the written image is only valid if could_write is true. I think what happens is that the writing routine does not see any pixel format tag in the dynamic image instance, and thus ultimately fails here.
This pixel format is then added by the explicit conversion to a strongly typed image with PixelType sln::PixelRGB_8u, and back again.

Ideally, this problem should be signaled via a message of error type. Could you please check that this message is emitted?

If that is indeed the case, the easiest fix would be to simply add this information at creation time of the dynamic image:

const sln::UntypedImageSemantics semantics{sln::PixelFormat::RGB, sln::SampleFormat::UnsignedInteger};
sln::DynImage input_buffer{{width, height, channels, depth}, sln::ImageRowAlignment{0}, semantics};

Please let me know if this resolves the issue, or not.
(And you might have to correct some typos in my blindly typed code above...)

@dunkyp
Copy link
Author

dunkyp commented Jan 12, 2019

That worked perfectly, thanks very much!

The problem was indeed "Cannot determine JPEG color space from pixel format of image data".

@dunkyp dunkyp closed this as completed Jan 12, 2019
@kmhofmann
Copy link
Owner

You're welcome - great to hear the issue is resolved!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants