mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Implement normalize()
This commit is contained in:
parent
813fc81623
commit
dd9e27fdf7
@ -10,6 +10,7 @@
|
|||||||
#include <QColor>
|
#include <QColor>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
|
// Macros {{{
|
||||||
#define SQUARE(x) (x)*(x)
|
#define SQUARE(x) (x)*(x)
|
||||||
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
|
#define MAX(x, y) ((x) > (y)) ? (x) : (y)
|
||||||
#define MIN(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); \
|
img = img.convertToFormat(img.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32); \
|
||||||
if (img.isNull()) throw std::bad_alloc(); \
|
if (img.isNull()) throw std::bad_alloc(); \
|
||||||
} \
|
} \
|
||||||
|
// }}}
|
||||||
|
|
||||||
|
// Structs {{{
|
||||||
typedef struct
|
typedef struct
|
||||||
{
|
{
|
||||||
float red, green, blue, alpha;
|
float red, green, blue, alpha;
|
||||||
} FloatPixel;
|
} 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) {{{
|
// 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) {
|
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 gaussian_blur(const QImage &img, const float radius, const float sigma);
|
||||||
QImage despeckle(const QImage &image);
|
QImage despeckle(const QImage &image);
|
||||||
void overlay(const QImage &image, QImage &canvas, unsigned int left, unsigned int top);
|
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);
|
overlay(*a0, *a1, a2, a3);
|
||||||
IMAGEOPS_SUFFIX
|
IMAGEOPS_SUFFIX
|
||||||
%End
|
%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)
|
raise RuntimeError(imageops_err)
|
||||||
return imageops.despeckle(image_from_data(img))
|
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):
|
def run_optimizer(file_path, cmd, as_filter=False, input_data=None):
|
||||||
file_path = os.path.abspath(file_path)
|
file_path = os.path.abspath(file_path)
|
||||||
cwd = os.path.dirname(file_path)
|
cwd = os.path.dirname(file_path)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user