1 /* This file is part of the Pangolin Project.
2  * http://github.com/stevenlovegrove/Pangolin
3  *
4  * Copyright (c) 2013 Steven Lovegrove
5  *
6  * Permission is hereby granted, free of charge, to any person
7  * obtaining a copy of this software and associated documentation
8  * files (the "Software"), to deal in the Software without
9  * restriction, including without limitation the rights to use,
10  * copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the
12  * Software is furnished to do so, subject to the following
13  * conditions:
14  *
15  * The above copyright notice and this permission notice shall be
16  * included in all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  * OTHER DEALINGS IN THE SOFTWARE.
26  */
27 
28 #pragma once
29 
30 #include <limits>
31 #include <utility>
32 
33 #include <pangolin/image/image.h>
34 #include <pangolin/plot/range.h>
35 #include <pangolin/gl/gl.h>
36 #include <pangolin/gl/glpixformat.h>
37 
38 namespace pangolin
39 {
40 
41 namespace internal
42 {
43 
44 template <typename T>
GetMinMax(const Image<T> & img,size_t channels)45 std::pair<float, float> GetMinMax(const Image<T>& img, size_t channels)
46 {
47     const size_t max_channels = 3;
48     const size_t colour_channels = std::min(channels, max_channels);
49     std::pair<float, float> chan_mm[max_channels];
50     for(size_t c = 0; c < max_channels; ++c)
51     {
52         chan_mm[c].first = +std::numeric_limits<float>::max();
53         chan_mm[c].second = -std::numeric_limits<float>::max();
54     }
55 
56     for(size_t y = 0; y < img.h; ++y)
57     {
58         T* pix = (T*)((char*)img.ptr + y * img.pitch);
59         for(size_t x = 0; x < img.w; ++x)
60         {
61             for(size_t c = 0; c < colour_channels; ++c)
62             {
63                 if(pix[c] < chan_mm[c].first)
64                     chan_mm[c].first = (float)pix[c];
65                 if(pix[c] > chan_mm[c].second)
66                     chan_mm[c].second = (float)pix[c];
67             }
68 
69             pix += channels;
70         }
71     }
72 
73     // Find min / max of all channels, ignoring 4th alpha channel
74     std::pair<float, float> mm = chan_mm[0];
75     for(size_t c = 1; c < colour_channels; ++c)
76     {
77         mm.first = std::min(mm.first, chan_mm[c].first);
78         mm.second = std::max(mm.second, chan_mm[c].second);
79     }
80 
81     return mm;
82 }
83 
84 template<typename T>
GetImageRoi(pangolin::Image<T> img,size_t channels,const pangolin::XYRangei & roi)85 pangolin::Image<T> GetImageRoi( pangolin::Image<T> img, size_t channels, const pangolin::XYRangei& roi )
86 {
87     return pangolin::Image<T>(
88         img.RowPtr(std::min(roi.y.min,roi.y.max)) + channels*std::min(roi.x.min,roi.x.max),
89         roi.x.AbsSize(), roi.y.AbsSize(),
90         img.pitch
91     );
92 }
93 
94 template<typename T>
GetOffsetScale(const pangolin::Image<T> & img,size_t channels,float type_max,float format_max)95 std::pair<float,float> GetOffsetScale(const pangolin::Image<T>& img, size_t channels, float type_max, float format_max)
96 {
97     // Find min / max of all channels, ignoring 4th alpha channel
98     const std::pair<float,float> mm = internal::GetMinMax<T>(img,channels);
99     const float type_scale = format_max / type_max;
100     const float offset = -type_scale* mm.first;
101     const float scale = type_max / (mm.second - mm.first);
102     return std::pair<float,float>(offset, scale);
103 }
104 
105 template<typename T>
GetScaleOnly(const pangolin::Image<T> & img,size_t channels,float type_max,float)106 float GetScaleOnly(const pangolin::Image<T>& img, size_t channels, float type_max, float /*format_max*/)
107 {
108     // Find min / max of all channels, ignoring 4th alpha channel
109     const std::pair<float,float> mm = internal::GetMinMax<T>(img,channels);
110     const float scale = type_max / mm.second;
111     return scale;
112 }
113 
114 } // internal
115 
GetMinMax(const Image<unsigned char> & img,XYRangei iroi,const GlPixFormat & glfmt)116 inline std::pair<float, float> GetMinMax(
117     const Image<unsigned char>& img,
118     XYRangei iroi, const GlPixFormat& glfmt
119 ) {
120     using namespace internal;
121 
122     iroi.Clamp(0, (int)img.w - 1, 0, (int)img.h - 1);
123 
124     const size_t num_channels = pangolin::GlFormatChannels(glfmt.glformat);
125 
126     if(glfmt.gltype == GL_UNSIGNED_BYTE) {
127         return GetMinMax(GetImageRoi(img.template UnsafeReinterpret<unsigned char>(), num_channels, iroi), num_channels);
128     } else if(glfmt.gltype == GL_UNSIGNED_SHORT) {
129         return GetMinMax(GetImageRoi(img.template UnsafeReinterpret<unsigned short>(), num_channels, iroi), num_channels);
130     } else if(glfmt.gltype == GL_FLOAT) {
131         return GetMinMax(GetImageRoi(img.template UnsafeReinterpret<float>(), num_channels, iroi), num_channels);
132     } else if(glfmt.gltype == GL_DOUBLE) {
133         return GetMinMax(GetImageRoi(img.template UnsafeReinterpret<double>(), num_channels, iroi), num_channels);
134     } else {
135         return std::pair<float, float>(std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest());
136     }
137 }
138 
GetOffsetScale(const pangolin::Image<unsigned char> & img,pangolin::XYRangei iroi,const pangolin::GlPixFormat & glfmt)139 inline std::pair<float,float> GetOffsetScale(
140     const pangolin::Image<unsigned char>& img,
141     pangolin::XYRangei iroi, const pangolin::GlPixFormat& glfmt
142 ) {
143     using namespace internal;
144 
145     iroi.Clamp(0, (int)img.w-1, 0, (int)img.h-1 );
146 
147     const size_t num_channels = pangolin::GlFormatChannels(glfmt.glformat);
148 
149     if(glfmt.gltype == GL_UNSIGNED_BYTE) {
150         return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret<unsigned char>(), num_channels, iroi), num_channels, 255.0f, 1.0f);
151     }else if(glfmt.gltype == GL_UNSIGNED_SHORT) {
152         return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret<unsigned short>(), num_channels, iroi), num_channels, 65535.0f, 1.0f);
153     }else if(glfmt.gltype == GL_FLOAT) {
154         return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret<float>(), num_channels, iroi), num_channels, 1.0f, 1.0f);
155     }else if(glfmt.gltype == GL_DOUBLE) {
156         return GetOffsetScale(GetImageRoi(img.template UnsafeReinterpret<double>(), num_channels, iroi), num_channels, 1.0f, 1.0f);
157     }else{
158         return std::pair<float,float>(0.0f, 1.0f);
159     }
160 }
161 
GetScaleOnly(const pangolin::Image<unsigned char> & img,pangolin::XYRangei iroi,const pangolin::GlPixFormat & glfmt)162 inline float GetScaleOnly(
163     const pangolin::Image<unsigned char>& img,
164     pangolin::XYRangei iroi, const pangolin::GlPixFormat& glfmt
165 ) {
166     using namespace internal;
167 
168     iroi.Clamp(0, (int)img.w-1, 0, (int)img.h-1 );
169 
170     const size_t num_channels = pangolin::GlFormatChannels(glfmt.glformat);
171 
172     if(glfmt.gltype == GL_UNSIGNED_BYTE) {
173         return GetScaleOnly(GetImageRoi(img.template UnsafeReinterpret<unsigned char>(), num_channels, iroi), num_channels, 255.0f, 1.0f);
174     }else if(glfmt.gltype == GL_UNSIGNED_SHORT) {
175         return GetScaleOnly(GetImageRoi(img.template UnsafeReinterpret<unsigned short>(), num_channels, iroi), num_channels, 65535.0f, 1.0f);
176     }else if(glfmt.gltype == GL_FLOAT) {
177         return GetScaleOnly(GetImageRoi(img.template UnsafeReinterpret<float>(), num_channels, iroi), num_channels, 1.0f, 1.0f);
178     }else if(glfmt.gltype == GL_DOUBLE) {
179         return GetScaleOnly(GetImageRoi(img.template UnsafeReinterpret<double>(), num_channels, iroi), num_channels, 1.0f, 1.0f);
180     }else{
181         return 1.0f;
182     }
183 }
184 
185 }
186