MCPcopy Create free account
hub / github.com/ImageOptim/libimagequant / contrast_maps

Method contrast_maps

src/image.rs:238–304  ·  view source on GitHub ↗

Builds two maps: `importance_map` - approximation of areas with high-frequency noise, except straight edges. 1=flat, 0=noisy. edges - noise map including all edges

(&mut self)

Source from the content-addressed store, hash-verified

236 /// `importance_map` - approximation of areas with high-frequency noise, except straight edges. 1=flat, 0=noisy.
237 /// edges - noise map including all edges
238 pub(crate) fn contrast_maps(&mut self) -> Result<(), Error> {
239 let width = self.width();
240 let height = self.height();
241 if width < 4 || height < 4 || (3 * width * height) > LIQ_HIGH_MEMORY_LIMIT {
242 return Ok(()); // shrug
243 }
244
245 let noise = if let Some(n) = self.importance_map.as_deref_mut() { n } else {
246 let vec = try_zero_vec(width * height)?;
247 self.importance_map.get_or_insert_with(move || vec.into_boxed_slice())
248 };
249
250 let edges = if let Some(e) = self.edges.as_mut() { e } else {
251 let vec = try_zero_vec(width * height)?;
252 self.edges.get_or_insert_with(move || vec.into_boxed_slice())
253 };
254
255 let mut rows_iter = self.px.all_rows_f()?.chunks_exact(width);
256
257 let mut next_row = rows_iter.next().ok_or(Error::InvalidPointer)?;
258 let mut curr_row = next_row;
259 let mut prev_row;
260
261 for (noise_row, edges_row) in noise[..width * height].chunks_exact_mut(width).zip(edges[..width * height].chunks_exact_mut(width)) {
262 prev_row = curr_row;
263 curr_row = next_row;
264 next_row = rows_iter.next().unwrap_or(next_row);
265 let mut prev;
266 let mut curr = curr_row[0].0;
267 let mut next = curr;
268 for i in 0..width {
269 prev = curr;
270 curr = next;
271 next = curr_row[(i + 1).min(width - 1)].0;
272 // contrast is difference between pixels neighbouring horizontally and vertically
273 let horiz = (prev + next - curr * 2.).map(f32::abs); // noise is amplified
274 let prevl = prev_row[i].0;
275 let nextl = next_row[i].0;
276 let vert = (prevl + nextl - curr * 2.).map(f32::abs);
277 let horiz = horiz.a.max(horiz.r).max(horiz.g.max(horiz.b));
278 let vert = vert.a.max(vert.r).max(vert.g.max(vert.b));
279 let edge = horiz.max(vert);
280 let mut z = (horiz - vert).abs().mul_add(-0.5, edge);
281 z = 1. - z.max(horiz.min(vert));
282 z *= z;
283 z *= z;
284 // 85 is about 1/3rd of weight (not 0, because noisy pixels still need to be included, just not as precisely).
285 noise_row[i] = z.mul_add(176., 80.) as u8;
286 edges_row[i] = ((1. - edge) * 256.) as u8;
287 }
288 }
289 // noise areas are shrunk and then expanded to remove thin edges from the map
290 let mut tmp = try_zero_vec(width * height)?;
291 liq_max3(noise, &mut tmp, width, height);
292 liq_max3(&tmp, noise, width, height);
293 liq_blur(noise, &mut tmp, width, height, 3);
294 liq_max3(noise, &mut tmp, width, height);
295 liq_min3(&tmp, noise, width, height);

Callers 2

add_imageMethod · 0.80
update_dither_mapMethod · 0.80

Calls 8

try_zero_vecFunction · 0.85
liq_max3Function · 0.85
liq_blurFunction · 0.85
liq_min3Function · 0.85
all_rows_fMethod · 0.80
iter_mutMethod · 0.80
widthMethod · 0.45
heightMethod · 0.45

Tested by

no test coverage detected