1 /*
2 * imagesource_unsharpmask.cpp - Applies an Unsharp Mask filter to an image.
3 *
4 * Supports Greyscale, RGB and CMYK data
5 * Doesn't (yet) support random access
6 *
7 * Copyright (c) 2008 by Alastair M. Robinson
8 * Distributed under the terms of the GNU General Public License -
9 * see the file named "COPYING" for more details.
10 *
11 */
12
13 #include <iostream>
14 #include <stdlib.h>
15 #include <string.h>
16 #include <math.h>
17
18 #include "imagesource_unsharpmask.h"
19 #include "convkernel_unsharpmask.h"
20 #include "convkernel_gaussian_1D.h"
21
22 using namespace std;
23
24 // The row cache is just a simplistic ring-buffer type cache which handles
25 // the details of tracking several rows of "support" data.
26
27 class ISUnsharpMask_RowCache
28 {
29 public:
30 ISUnsharpMask_RowCache(ImageSource_UnsharpMask *source);
31 ~ISUnsharpMask_RowCache();
32 ISDataType *GetRawRow(int row);
33 float *GetConvRow(int row);
34 private:
35 ImageSource_UnsharpMask *source;
36 float *convcache;
37 ISDataType *rawcache;
38 int cachewidth,cachehoffset;
39 int bufferrows;
40 int rawcurrentrow;
41 int convcurrentrow;
42 };
43
44
~ISUnsharpMask_RowCache()45 ISUnsharpMask_RowCache::~ISUnsharpMask_RowCache()
46 {
47 if(convcache)
48 free(convcache);
49 if(rawcache)
50 free(rawcache);
51 }
52
53
ISUnsharpMask_RowCache(ImageSource_UnsharpMask * source)54 ISUnsharpMask_RowCache::ISUnsharpMask_RowCache(ImageSource_UnsharpMask *source)
55 : source(source), rawcurrentrow(-1), convcurrentrow(-1)
56 {
57 cachewidth=source->width+source->hextra*2;
58 cachehoffset=source->hextra;
59 bufferrows=source->vextra*2+1;
60 convcache=(float *)malloc(sizeof(float)*source->samplesperpixel*cachewidth*bufferrows);
61 rawcache=(ISDataType *)malloc(sizeof(float)*source->samplesperpixel*source->width*bufferrows);
62 }
63
64
GetConvRow(int row)65 inline float *ISUnsharpMask_RowCache::GetConvRow(int row)
66 {
67 if(row<0)
68 row=0;
69 if(row>=source->source->height)
70 row=source->source->height-1;
71 int crow=row%(source->vextra*2+1);
72 float *rowptr=convcache+crow*source->samplesperpixel*cachewidth;
73 if(row>convcurrentrow)
74 {
75 convcurrentrow=row;
76 ISDataType *src=GetRawRow(row);
77
78 // Convolve the temp row into the cache;
79 for(int x=0;x<cachewidth;++x)
80 {
81 float t[5]={0.0,0.0,0.0,0.0,0.0};
82 for(int kx=0;kx<source->kernel->GetWidth();++kx)
83 {
84 int sx=x+kx-cachehoffset*2;
85 if(sx<0) sx=0;
86 if(sx>source->width) sx=source->width-1;
87 for(int s=0;s<source->samplesperpixel;++s)
88 {
89 t[s]+=source->kernel->Kernel(kx,0)*src[sx*source->samplesperpixel+s];
90 }
91 }
92 for(int s=0;s<source->samplesperpixel;++s)
93 {
94 rowptr[x*source->samplesperpixel+s]=t[s];
95 }
96 }
97 }
98 return(rowptr+cachehoffset*source->samplesperpixel);
99 }
100
101
GetRawRow(int row)102 inline ISDataType *ISUnsharpMask_RowCache::GetRawRow(int row)
103 {
104 if(row<0)
105 row=0;
106 if(row>=source->source->height)
107 row=source->source->height-1;
108 int crow=row%(source->vextra*2+1);
109 ISDataType *rowptr=rawcache+crow*source->samplesperpixel*source->width;
110 if(row>rawcurrentrow)
111 {
112 rawcurrentrow=row;
113 ISDataType *src=source->source->GetRow(row);
114
115 // Store the row to be cached in a temporary buffer...
116 for(int s=0;s<source->width*source->samplesperpixel;++s)
117 {
118 rowptr[s]=src[s];
119 }
120 }
121 return(rowptr);
122 }
123
124
~ImageSource_UnsharpMask()125 ImageSource_UnsharpMask::~ImageSource_UnsharpMask()
126 {
127 if(tmprows)
128 free(tmprows);
129 if(cache)
130 delete cache;
131 if(source)
132 delete source;
133 if(kernel)
134 delete kernel;
135 }
136
137
GetRow(int row)138 ISDataType *ImageSource_UnsharpMask::GetRow(int row)
139 {
140 if(row==currentrow)
141 return(rowbuffer);
142
143 int kh=kernel->GetWidth(); // Using a 1D kernel!
144
145 for(int r=0;r<kh;++r)
146 {
147 tmprows[r]=cache->GetConvRow(row+(r-vextra));
148 }
149 ISDataType *srcrow=cache->GetRawRow(row);
150 for(int x=0;x<width;++x)
151 {
152 float t[5]={0.0,0.0,0.0,0.0,0.0};
153 for(int ky=0;ky<kh;++ky)
154 {
155 for(int s=0;s<samplesperpixel;++s)
156 {
157 t[s]+=kernel->Kernel(ky,0) * tmprows[ky][x*samplesperpixel+s];
158 }
159 }
160 for(int s=0;s<samplesperpixel;++s)
161 {
162 // float out=t[s];
163 // float out=tmprows[vextra][x*samplesperpixel+s];
164 float out=(1+amount)*srcrow[x*samplesperpixel+s]-amount*t[s];
165 float d=srcrow[x*samplesperpixel+s]-out;
166 if((d*d)<threshold)
167 {
168 out=srcrow[x*samplesperpixel+s];
169 }
170 if(out<0.0) out=0.0;
171 if(out>IS_SAMPLEMAX) out=IS_SAMPLEMAX;
172 rowbuffer[x*samplesperpixel+s]=ISDataType(out);
173 }
174 }
175
176 currentrow=row;
177
178 return(rowbuffer);
179 }
180
181
ImageSource_UnsharpMask(struct ImageSource * source,float radius,float amount,float threshold)182 ImageSource_UnsharpMask::ImageSource_UnsharpMask(struct ImageSource *source,float radius,float amount,float threshold)
183 : ImageSource(source), source(source), kernel(NULL), amount(amount), threshold(threshold*threshold)
184 {
185 kernel=new ConvKernel_Gaussian_1D(radius);
186 kernel->Normalize();
187 hextra=kernel->GetWidth()/2;
188 vextra=hextra;
189 cache=new ISUnsharpMask_RowCache(this);
190 tmprows=(float **)malloc(sizeof(float *)*kernel->GetWidth());
191 MakeRowBuffer();
192 randomaccess=false;
193 }
194