Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
144 views
in Technique[技术] by (71.8m points)

c++ - Why the output image will be rotated?

I build a watermark creator by OpenCV. The program could create watermark in the 4 corner of a source image. I haven't change the source image direction and the output image is also copied from the merge of source image and watermark. When I make a test, a image was rotated and the watermark was in the incorrect place. However when I use the thumbnail of that image for test, the direction is correct. So I guess the problem is in the size of image.

Here is my code.

#include "test.h"

#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

#define LEFT_TOP 0
#define LEFT_DOWN 1
#define MIDDLE 2
#define RIGHT_TOP 3
#define rIGHT_DOWN 4

//watermark position offset
#define off_ratio 40

//the ratio of watermark and the source image 

#define length_ratio 6

int main_test(int argc , char * argv[])
{

    //Read from alpha channel ,Transparent

    Mat img_src = imread(argv[1], CV_LOAD_IMAGE_UNCHANGED);
    cout << (int)img_src.at<Vec4b>(0,0)[0] << endl
    << (int)img_src.at<Vec4b>(0,0)[1] << endl
    << (int)img_src.at<Vec4b>(0,0)[2] << endl
    << (int)img_src.at<Vec4b>(0,0)[3] << endl;

    cout << (int)img_src.at<Vec4b>(10,10)[0] << endl
    << (int)img_src.at<Vec4b>(10,10)[1] << endl
    << (int)img_src.at<Vec4b>(10,10)[2] << endl
    << (int)img_src.at<Vec4b>(10,10)[3] << endl;
    return 0;
}



int add_watermask(Mat & img_roi, Mat img_water_mask, float scale_ratio)
{

    vector<Mat>src_channels;
    vector<Mat>watermark_channels;

    //Split the channel
    split(img_roi, src_channels);
    split(img_water_mask, watermark_channels);

    if (scale_ratio < 1)
    {
        watermark_channels[3] *= scale_ratio;
        scale_ratio = 1;
    }

    for (int i = 0; i < 3; i++)
    {
        //dstt_channels[i] = dstt_channels[i].mul(255.0 / scale - scr_channels[3], scale / 255.0);
        //dstt_channels[i] += scr_channels[i].mul(scr_channels[3], scale / 255.0);
        src_channels[i] = src_channels[i].mul(255.0 / scale_ratio - watermark_channels[3], scale_ratio / 255.0);
        src_channels[i] += watermark_channels[i].mul(watermark_channels[3], scale_ratio / 255.0);
    }

    merge(src_channels, img_roi);
    return 0;
}


//Calculate the watermark position in the source image
int process_pose(Mat & src, Mat & water, Mat & roi, Mat & water_resize, int pose)
{

    int width_src = src.cols;
    int height_src = src.rows;

    int width_water = water.cols;
    int height_water = water.rows;

    int max_length_src = max(width_src, height_src);

    //5% offset of the image length
    int off = max_length_src / off_ratio;

    //Watermark length is 1/6 of the source image
    int length_1_6 = max_length_src / length_ratio;

    int scale_width_water = length_1_6;
    int scale_height_water = length_1_6 * height_water / width_water;


    //if the source image width larger than height,then keep the scale of watermark's height and source image' height
    if(width_src > height_src) {
        if ((off * 2 + scale_height_water) > height_src) {
            off = height_src / off_ratio;
            scale_height_water = height_src / length_ratio;
            scale_width_water = scale_height_water * width_water / height_water;
        }
    }
        //if the source image height larger than width,then keep the scale of watermark's width and source image' width
    else{
        if((off * 2 + scale_width_water) > width_src)
        {
            off = width_src / off_ratio;
            scale_width_water = width_src / length_ratio;
            scale_height_water = scale_width_water * height_water / width_water;
        }
    }

    int ret = 0 ;

    Rect pose_water = {0,0,0,0};

    pose_water.width = scale_width_water;
    pose_water.height = scale_height_water;


    switch(pose)
    {
        case 0:
            pose_water.x = off;
            pose_water.y = off;
            break;

        case 1:
            pose_water.x = off;
            pose_water.y = height_src - off - scale_height_water;
            break;

        case 2:
            pose_water.x = (width_src - scale_width_water)/2;
            pose_water.y = (height_src - scale_height_water)/2;
            break;

        case 3:
            pose_water.x = width_src - scale_width_water - off;
            pose_water.y = off;
            break;

        case 4:
            pose_water.x = width_src - scale_width_water - off;
            pose_water.y = height_src - scale_height_water - off;
            break;
        default:
            printf("wrong pose 
");
            ret = -1;
            break;

    }

    if(ret == -1)
    {
        return  ret;
    }

    roi = src(pose_water);

    resize(water, water_resize, Size(scale_width_water, scale_height_water));
    return  0;
}


int main(int argc , char * argv[])
{


    if(argc < 6)
    {
        cout << "used error
";
        cout <<"used follow: " << argv[0] << " img1(jpg) img2(png) Tranparacy(0-100) Position(LEFT_TOP(0), LEFT_DOWN(1), MIDDLE(2), RIGHT_TOP(3),RIGHT_DOWN(4)), the result
";
        cout << "example: /home/disk/0_raw.jpg /home/disk/logo.png 0 0 /home/disk/logo_0_test.jpg 
";
        return 103;
    }
    Mat img_src = imread(argv[1]);
    Mat img_water_mark = imread(argv[2], -1);

    //Check the argument
    if(img_src.data == NULL || img_water_mark.data == NULL)
    {
        cout << "read img failed check it
"
        << "img1: " << argv[1] << endl
        << "img1: " << argv[1] << endl;

        return 101;
    }

    //Grey convert to color
    if(img_src.channels() != 3)
    {
        cvtColor(img_src, img_src, COLOR_GRAY2BGR);

    }
    //Check the channel is correct

    if(img_src.channels() != 3 || img_water_mark.channels() != 4)
    {
        cout << "img 's channnel is error ,check it 
";
        return 102;
    }

    //Default watermark width is larger than height

    if(img_water_mark.cols < img_water_mark.rows)
    {
        cout << "Watermark height large than width, check it 
";
        return 104;
    }

    //Transparency(0-100)

    int scale = atoi(argv[3]);
    float scale_ratio = (1- scale / 100.0);

    if (scale_ratio > 1.0 || scale_ratio < 0.0)
    {
        cout << "scale is error , put scale in 1~100
";

        return 105;
    }


    int pose = atoi(argv[4]);

    Mat src_copy  = img_src.clone();
    Mat water_copy = img_water_mark.clone();

    Mat roi, water_resize ;

    int ret = process_pose(src_copy, water_copy, roi, water_resize, pose);

    if(ret == -1)
    {
        printf("other pose not in LEFT_TOP(0), LEFT_DOWN(1), MIDDLE(2), RIGHT_TOP(3),RIGHT_DOWN(4), check it
");
        return 106;
    }

//    Mat img_roi(img_src, cvRect(0, 0, img_water_mark.cols, img_water_mark.rows));
//    add_watermask(img_roi, img_water_mark, scale_ratio);

    add_watermask(roi, water_resize, scale_ratio);

    //imshow("img", src_copy);
    imwrite(argv[5], src_copy);
    //waitKey();
    return  0;

}

I want to create a watermark in the RIGHT_DOWN position of the image, but the output image was rotated. So the watermark position is wrong.

Due to the limit of stackoverflow I can only upload a image less than 2MB. The original source image is 6MB. Here is the thumbnail of the source image. enter image description here

Here is the thumbnail of the original source image output. enter image description here

Caution: When I use the thumbnail of original image as input, the result is correct.

Here is the output when I use thumbnail of source image as input.enter image description here

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

As @MarkRansom says, some cameras store an EXIF "Orientation" field in the headers of images.

You have a few options, using exiftool, jhead or ImageMagick all of which are installed on many Linux distros and are available for macOS and Windows.

So, to see the field with exiftool:

exiftool -Orientation image.jpg
   Orientation                     : Horizontal (normal)

Or with jhead:

jhead -v image.jpg | grep -i orien
Orientation = 1

Or with ImageMagick:

identify -verbose image.jpg | grep -i orient
   Orientation: TopLeft
   exif:Orientation: 1

So, you could do that, and correct for it with OpenCV using a rotate(). Or you could correct it with ImageMagick outside of OpenCV:

mogrify -auto-orient image.jpg             # correct orientation of single image
convert image.jpg -auto-orient result.jpg  # alternative, more verbose but same as above  
mogrify -auto-orient *.jpg                 # correct orientation of all images

Beware that mogrify will change all your images irreversibly, so be sure that you make a backup before testing.


Note that jhead is probably the easiest to install, lightest weight option of the three in Windows. In Linux or macOS, exiftool is the lightest weight as it is just a Perl script. ImageMagick is vastly more powerful, and heavier weight and somewhat harder to install - IMHO.


Useful links

Tutorial

Wikipedia


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...