mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 10:44:09 -04:00
Implement normalize()
This commit is contained in:
parent
813fc81623
commit
dd9e27fdf7
@ -10,6 +10,7 @@
|
||||
#include <QColor>
|
||||
#include <cmath>
|
||||
|
||||
// Macros {{{
|
||||
#define SQUARE(x) (x)*(x)
|
||||
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
|
||||
#define MIN(x, y) ((x) < (y)) ? (x) : (y)
|
||||
@ -24,12 +25,36 @@
|
||||
img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); \
|
||||
if (img.isNull()) throw std::bad_alloc(); \
|
||||
} \
|
||||
// }}}
|
||||
|
||||
// Structs {{{
|
||||
typedef struct
|
||||
{
|
||||
float red, green, blue, alpha;
|
||||
} FloatPixel;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
quint32 red, green, blue, alpha;
|
||||
} IntegerPixel;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
quint16 red, green, blue, alpha;
|
||||
} ShortPixel;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
// Yes, a normal pixel can be used instead but this is easier to read
|
||||
// and no shifts to get components.
|
||||
quint8 red, green, blue, alpha;
|
||||
} CharPixel;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
quint32 red, green, blue, alpha;
|
||||
} HistogramListItem;
|
||||
// }}}
|
||||
|
||||
// Remove borders (auto-trim) {{{
|
||||
unsigned int read_border_row(const QImage &img, const unsigned int width, const unsigned int height, int *reds, const double fuzz, const bool top) {
|
||||
@ -634,3 +659,126 @@ void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned in
|
||||
}
|
||||
|
||||
} // }}}
|
||||
|
||||
QImage normalize(const QImage &image) { // {{{
|
||||
IntegerPixel intensity;
|
||||
HistogramListItem *histogram;
|
||||
CharPixel *normalize_map;
|
||||
ShortPixel high, low;
|
||||
uint threshold_intensity;
|
||||
int i, count;
|
||||
QRgb pixel, *dest;
|
||||
unsigned char r, g, b;
|
||||
QImage img(image);
|
||||
|
||||
ENSURE32(img);
|
||||
|
||||
count = img.width()*img.height();
|
||||
histogram = new HistogramListItem[256];
|
||||
normalize_map = new CharPixel[256];
|
||||
Py_BEGIN_ALLOW_THREADS;
|
||||
|
||||
// form histogram
|
||||
memset(histogram, 0, 256*sizeof(HistogramListItem));
|
||||
dest = (QRgb *)img.bits();
|
||||
|
||||
for(i=0; i < count; ++i){
|
||||
pixel = *dest++;
|
||||
histogram[qRed(pixel)].red++;
|
||||
histogram[qGreen(pixel)].green++;
|
||||
histogram[qBlue(pixel)].blue++;
|
||||
histogram[qAlpha(pixel)].alpha++;
|
||||
}
|
||||
|
||||
// find the histogram boundaries by locating the .01 percent levels.
|
||||
threshold_intensity = count/1000;
|
||||
|
||||
memset(&intensity, 0, sizeof(IntegerPixel));
|
||||
for(low.red=0; low.red < 256; ++low.red){
|
||||
intensity.red += histogram[low.red].red;
|
||||
if(intensity.red > threshold_intensity)
|
||||
break;
|
||||
}
|
||||
memset(&intensity, 0, sizeof(IntegerPixel));
|
||||
for(high.red=256; high.red >= 1; --high.red){
|
||||
intensity.red += histogram[high.red-1].red;
|
||||
if(intensity.red > threshold_intensity)
|
||||
break;
|
||||
}
|
||||
memset(&intensity, 0, sizeof(IntegerPixel));
|
||||
for(low.green=low.red; low.green < high.red; ++low.green){
|
||||
intensity.green += histogram[low.green].green;
|
||||
if(intensity.green > threshold_intensity)
|
||||
break;
|
||||
}
|
||||
memset(&intensity, 0, sizeof(IntegerPixel));
|
||||
for(high.green=high.red; high.green != low.red; --high.green){
|
||||
intensity.green += histogram[high.green].green;
|
||||
if(intensity.green > threshold_intensity)
|
||||
break;
|
||||
}
|
||||
memset(&intensity, 0, sizeof(IntegerPixel));
|
||||
for(low.blue=low.green; low.blue < high.green; ++low.blue){
|
||||
intensity.blue += histogram[low.blue].blue;
|
||||
if(intensity.blue > threshold_intensity)
|
||||
break;
|
||||
}
|
||||
memset(&intensity, 0, sizeof(IntegerPixel));
|
||||
for(high.blue=high.green; high.blue != low.green; --high.blue){
|
||||
intensity.blue += histogram[high.blue].blue;
|
||||
if(intensity.blue > threshold_intensity)
|
||||
break;
|
||||
}
|
||||
|
||||
delete[] histogram;
|
||||
|
||||
// stretch the histogram to create the normalized image mapping.
|
||||
for(i=0; i < 256; i++){
|
||||
if(i < low.red)
|
||||
normalize_map[i].red = 0;
|
||||
else{
|
||||
if(i > high.red)
|
||||
normalize_map[i].red = 255;
|
||||
else if(low.red != high.red)
|
||||
normalize_map[i].red = (255*(i-low.red))/
|
||||
(high.red-low.red);
|
||||
}
|
||||
|
||||
if(i < low.green)
|
||||
normalize_map[i].green = 0;
|
||||
else{
|
||||
if(i > high.green)
|
||||
normalize_map[i].green = 255;
|
||||
else if(low.green != high.green)
|
||||
normalize_map[i].green = (255*(i-low.green))/
|
||||
(high.green-low.green);
|
||||
}
|
||||
|
||||
if(i < low.blue)
|
||||
normalize_map[i].blue = 0;
|
||||
else{
|
||||
if(i > high.blue)
|
||||
normalize_map[i].blue = 255;
|
||||
else if(low.blue != high.blue)
|
||||
normalize_map[i].blue = (255*(i-low.blue))/
|
||||
(high.blue-low.blue);
|
||||
}
|
||||
}
|
||||
|
||||
// write
|
||||
dest = reinterpret_cast<QRgb *>(img.bits());
|
||||
for(i=0; i < count; ++i){
|
||||
pixel = *dest;
|
||||
r = (low.red != high.red) ? normalize_map[qRed(pixel)].red :
|
||||
qRed(pixel);
|
||||
g = (low.green != high.green) ? normalize_map[qGreen(pixel)].green :
|
||||
qGreen(pixel);
|
||||
b = (low.blue != high.blue) ? normalize_map[qBlue(pixel)].blue :
|
||||
qBlue(pixel);
|
||||
*dest++ = qRgba(r, g, b, qAlpha(pixel));
|
||||
}
|
||||
|
||||
delete[] normalize_map;
|
||||
Py_END_ALLOW_THREADS;
|
||||
return img;
|
||||
} // }}}
|
||||
|
@ -16,4 +16,4 @@ QImage gaussian_sharpen(const QImage &img, const float radius, const float sigma
|
||||
QImage gaussian_blur(const QImage &img, const float radius, const float sigma);
|
||||
QImage despeckle(const QImage &image);
|
||||
void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned int top);
|
||||
|
||||
QImage normalize(const QImage &image);
|
||||
|
@ -58,3 +58,10 @@ void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned in
|
||||
overlay(*a0, *a1, a2, a3);
|
||||
IMAGEOPS_SUFFIX
|
||||
%End
|
||||
|
||||
QImage normalize(const QImage &image);
|
||||
%MethodCode
|
||||
IMAGEOPS_PREFIX
|
||||
sipRes = new QImage(normalize(*a0));
|
||||
IMAGEOPS_SUFFIX
|
||||
%End
|
||||
|
@ -244,6 +244,11 @@ def despeckle(img):
|
||||
raise RuntimeError(imageops_err)
|
||||
return imageops.despeckle(image_from_data(img))
|
||||
|
||||
def normalize(img):
|
||||
if imageops is None:
|
||||
raise RuntimeError(imageops_err)
|
||||
return imageops.normalize(image_from_data(img))
|
||||
|
||||
def run_optimizer(file_path, cmd, as_filter=False, input_data=None):
|
||||
file_path = os.path.abspath(file_path)
|
||||
cwd = os.path.dirname(file_path)
|
||||
|
Loading…
x
Reference in New Issue
Block a user