【问题标题】:Why the output image will be rotated?为什么输出图像会被旋转?
【发布时间】:2018-06-17 01:06:24
【问题描述】:

我通过 OpenCV 构建了一个水印创建器。该程序可以在源图像的 4 个角落创建水印。我没有改变源图像的方向,输出图像也是从源图像和水印的合并中复制而来的。当我进行测试时,图像被旋转并且水印在不正确的位置。但是,当我使用该图像的缩略图进行测试时,方向是正确的。所以我想问题在于图像的大小。

这是我的代码。

#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 \n");
            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\n";
        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\n";
        cout << "example: /home/disk/0_raw.jpg /home/disk/logo.png 0 0 /home/disk/logo_0_test.jpg \n";
        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\n"
        << "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 \n";
        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 \n";
        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\n";

        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\n");
        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;

}

我想在图片的 RIGHT_DOWN 位置创建一个水印,但是输出的图片被旋转了。所以水印位置不对。

由于stackoverflow的限制,我只能上传小于2MB的图片。原始源图像为 6MB。这是源图像的缩略图。

这是原始源图像输出的缩略图。

注意:当我使用原始图像的缩略图作为输入时,结果是正确的。

这是我使用源图像的缩略图作为输入时的输出。

【问题讨论】:

  • 相机通常会在图像文件的 EXIF 中添加旋转符号。有些程序读取该旋转以正确显示它,而有些程序则忽略它。输出新图像的程序可以复制该旋转标志或丢失它。在这种情况下,看起来 OpenCV 不会自动为您旋转图像,也不会将旋转标志复制到输出。
  • 非常感谢。看来你是对的。源图像的方向是 270 度。 OpenCV 自动读取方向。下一个困扰我的问题是如何防止 OpenCV 读取方向信息。你有好的理想吗?再次感谢您。

标签: c++ image opencv image-processing


【解决方案1】:

正如@MarkRansom 所说,一些相机在图像标题中存储了一个 EXIF “方向” 字段。

您有几个选择,使用 exiftooljheadImageMagick,所有这些都安装在许多 Linux 发行版上并且可用于 macOS和窗户。

所以,用 exiftool 查看字段:

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

或者用jhead

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

或者使用 ImageMagick

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

所以,您可以这样做,并使用 rotate() 使用 OpenCV 进行更正。或者您可以在 OpenCV 之外使用 ImageMagick 进行更正:

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

请注意,mogrify 会不可逆地更改您的所有图像,因此请务必在测试前进行备份。


请注意,jhead 可能是 Windows 中三个选项中最容易安装、重量最轻的选项。在 Linux 或 macOS 中,exiftool 是最轻量级的,因为它只是一个 Perl 脚本。 ImageMagick 功能更强大,重量更重,安装起来也有些困难 - 恕我直言。


有用的链接

Tutorial

Wikipedia

【讨论】:

  • 是的!谢谢你。我检查图像的方向,它是 270 度。但是,您有什么好的理想来防止 OpenCV 自动读取方向信息吗?由于无法删除所有exif信息,所以需要使用一些技巧来保持图像方向。
  • 如果你想mogrify -strip *.jpgmogrify -strip *.jpg,你可以删除(即剥离)所有EXIF信息
  • 我需要保留 EXIF 信息并且我使用 OpenCV 2.4.10。 OpenCV 2.4.X 不能使用 CV_LOAD_IMAGE_IGNORE_ORIENTATION 标志,而 OpenCV 不能读取 exif 信息。我要崩溃了
  • 您可以使用 exiftool -Orientation=1 -n image.jpg 或使用 ImageMagickmogrify -orient top-left image.jpg 仅更改方向字段
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-01-20
  • 1970-01-01
  • 2020-06-09
  • 1970-01-01
  • 2021-01-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多