partial test complete

This commit is contained in:
Henry Schimke
2023-04-24 17:42:40 -05:00
parent a811b56fbb
commit d08f67feed
8 changed files with 115 additions and 47 deletions

View File

@@ -147,7 +147,7 @@ impl BitSource {
// Next read whole bytes // Next read whole bytes
if num_bits > 0 { if num_bits > 0 {
while num_bits >= 8 { while num_bits >= 8 {
result = (result << 8) | self.bytes[self.byte_offset] as u32; result = (result << 8) | self.bytes[byte_offset] as u32;
// result = ((result as u16) << 8) as u8 | (self.bytes[self.byte_offset]); // result = ((result as u16) << 8) as u8 | (self.bytes[self.byte_offset]);
byte_offset += 1; byte_offset += 1;
num_bits -= 8; num_bits -= 8;
@@ -158,7 +158,7 @@ impl BitSource {
let bits_to_not_read = 8 - num_bits; let bits_to_not_read = 8 - num_bits;
let mask = (0xFF >> bits_to_not_read) << bits_to_not_read; let mask = (0xFF >> bits_to_not_read) << bits_to_not_read;
result = (result << num_bits) result = (result << num_bits)
| ((self.bytes[self.byte_offset] & mask) as u32 >> bits_to_not_read); | ((self.bytes[byte_offset] & mask) as u32 >> bits_to_not_read);
bit_offset += num_bits; bit_offset += num_bits;
} }
} }

View File

@@ -305,7 +305,7 @@ pub fn CollectRingPoints(
edgeIndex: i32, edgeIndex: i32,
backup: bool, backup: bool,
) -> Vec<Point> { ) -> Vec<Point> {
let centerI = center.round(); let centerI = center.floor();
let radius = range; let radius = range;
let mut cur = EdgeTracer::new(image, centerI, point(0.0, 1.0)); let mut cur = EdgeTracer::new(image, centerI, point(0.0, 1.0));
if cur.stepToEdge(Some(edgeIndex), Some(radius), Some(backup)) == 0 { if cur.stepToEdge(Some(edgeIndex), Some(radius), Some(backup)) == 0 {
@@ -362,17 +362,20 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
let dist2Center = |a, b| Point::distance(a, center) < Point::distance(b, center); let dist2Center = |a, b| Point::distance(a, center) < Point::distance(b, center);
// rotate points such that the first one is the furthest away from the center (hence, a corner) // rotate points such that the first one is the furthest away from the center (hence, a corner)
let max_by_pred = |a: &Point, b: &Point| { let max_by_pred = |a: &&Point, b: &&Point| {
if dist2Center(*a, *b) { let da = Point::distance(**a, center);
std::cmp::Ordering::Greater let db = Point::distance(**b, center);
} else { da.partial_cmp(&db).unwrap()
std::cmp::Ordering::Less // if dist2Center(**a, **b) {
} // std::cmp::Ordering::Greater
// } else {
// std::cmp::Ordering::Less
// }
}; };
let max = points.iter().copied().max_by(max_by_pred)?; let max = points.iter().max_by(max_by_pred)?;
let pos = points.iter().position(|e| *e == max)?; let pos = points.iter().position(|e| e == max)?;
points.rotate_left(pos); points.rotate_left(pos);
// std::rotate(points.begin(), std::max_element(points.begin(), points.end(), dist2Center), points.end()); // std::rotate(points.begin(), std::max_element(points.begin(), points.end(), dist2Center), points.end());
@@ -380,9 +383,8 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
let mut corners = [Point::default(); 4]; let mut corners = [Point::default(); 4];
corners[0] = points[0]; corners[0] = points[0];
// find the oposite corner by looking for the farthest point near the oposite point // find the oposite corner by looking for the farthest point near the oposite point
points[(points.len() * 3 / 8)..=(points.len() * 5 / 8)] corners[2] = *points[(points.len() * 3 / 8)..=(points.len() * 5 / 8)]
.iter() .iter()
.copied()
.max_by(max_by_pred)?; .max_by(max_by_pred)?;
// corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center); // corners[2] = std::max_element(&points[Size(points) * 3 / 8], &points[Size(points) * 5 / 8], dist2Center);
// find the two in between corners by looking for the points farthest from the long diagonal // find the two in between corners by looking for the points farthest from the long diagonal
@@ -390,11 +392,14 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
let dist2Diagonal = /*[l = RegressionLine(*corners[0], *corners[2])]*/| a, b| { l.distance_single(a) < l.distance_single(b) }; let dist2Diagonal = /*[l = RegressionLine(*corners[0], *corners[2])]*/| a, b| { l.distance_single(a) < l.distance_single(b) };
let diagonal_max_by_pred = |p1: &Point, p2: &Point| { let diagonal_max_by_pred = |p1: &Point, p2: &Point| {
if dist2Diagonal(*p1, *p2) { let d1 = l.distance_single(*p1);
std::cmp::Ordering::Greater let d2 = l.distance_single(*p2);
} else { d1.partial_cmp(&d2).unwrap()
std::cmp::Ordering::Less // if dist2Diagonal(*p1, *p2) {
} // std::cmp::Ordering::Greater
// } else {
// std::cmp::Ordering::Less
// }
}; };
corners[1] = points[(points.len() / 8)..=(points.len() * 3 / 8)] corners[1] = points[(points.len() / 8)..=(points.len() * 3 / 8)]
.iter() .iter()
@@ -407,11 +412,19 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
.max_by(diagonal_max_by_pred)?; .max_by(diagonal_max_by_pred)?;
// corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal); // corners[3] = std::max_element(&points[Size(points) * 5 / 8], &points[Size(points) * 7 / 8], dist2Diagonal);
let corner_positions = [0, points.iter().position(|p| *p == corners[1])?, points.iter().position(|p| *p == corners[2])?, points.iter().position(|p| *p == corners[3])?];
// let lines = [
// RegressionLine::with_two_points(corners[0] + 1.0, corners[1]),
// RegressionLine::with_two_points(corners[1] + 1.0, corners[2]),
// RegressionLine::with_two_points(corners[2] + 1.0, corners[3]),
// RegressionLine::with_two_points(corners[3] + 1.0, *points.last()? + 1.0),
// ];
let lines = [ let lines = [
RegressionLine::with_two_points(corners[0] + 1.0, corners[1]), RegressionLine::with_point_slice(&points[corner_positions[0] + 1.. corner_positions[1]]),
RegressionLine::with_two_points(corners[1] + 1.0, corners[2]), RegressionLine::with_point_slice(&points[corner_positions[1] + 1.. corner_positions[2]]),
RegressionLine::with_two_points(corners[2] + 1.0, corners[3]), RegressionLine::with_point_slice(&points[corner_positions[2] + 1.. corner_positions[3]]),
RegressionLine::with_two_points(corners[3] + 1.0, *points.last()? + 1.0), RegressionLine::with_point_slice(&points[corner_positions[3] + 1.. points.len()]),
]; ];
// std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]}, // std::array lines{RegressionLine{corners[0] + 1, corners[1]}, RegressionLine{corners[1] + 1, corners[2]},
// RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}}; // RegressionLine{corners[2] + 1, corners[3]}, RegressionLine{corners[3] + 1, &points.back() + 1}};
@@ -428,6 +441,44 @@ pub fn FitQadrilateralToPoints(center: Point, points: &mut [Point]) -> Option<Qu
Some(res) Some(res)
} }
// fn fit_quadralateral_to_points(center: Point, points: Vec<Point>) -> Option<Quadrilateral> {
// let dist2center = |a, b| Point::distance(a, center) < Point::distance(b, center);
// // Rotate points such that the first one is the furthest away from the center (hence, a corner)
// let mut points = points;
// points.rotate_left(points.iter().position(|p| dist2center(p, center)).unwrap());
// let mut corners = [&points[0]; 4];
// corners[0] = &points[0];
// // Find the opposite corner by looking for the farthest point near the opposite point
// let opposite_corner = points.iter().take(points.len() / 2).max_by(|p| dist2center(p, corners[0]));
// corners[2] = opposite_corner;
// // Find the two in between corners by looking for the points farthest from the long diagonal
// let dist2diagonal = |l: &RegressionLine, a| l.distance(a);
// corners[1] = points.iter().take(points.len() / 2).max_by(|p| dist2diagonal(&RegressionLine(*corners[0], *corners[2]), *p));
// corners[3] = points.iter().skip(points.len() / 2).max_by(|p| dist2diagonal(&RegressionLine(*corners[0], *corners[2]), *p));
// let lines = [
// RegressionLine::new(corners[0], corners[1]),
// RegressionLine::new(corners[1], corners[2]),
// RegressionLine::new(corners[2], corners[3]),
// RegressionLine::new(corners[3], corners[0]),
// ];
// if lines.iter().any(|l| !l.is_valid()) {
// return None;
// }
// let mut res = QuadrilateralF::new();
// for i in 0..4 {
// res[i] = intersect(lines[i], lines[(i + 1) % 4]);
// }
// Some(res)
// }
pub fn QuadrilateralIsPlausibleSquare(q: &Quadrilateral, lineIndex: usize) -> bool { pub fn QuadrilateralIsPlausibleSquare(q: &Quadrilateral, lineIndex: usize) -> bool {
let mut m; let mut m;

View File

@@ -56,7 +56,7 @@ impl<'a> FastEdgeToEdgeCounter<'a> {
steps += 1; steps += 1;
if steps > maxSteps { if steps > maxSteps {
if maxSteps == self.stepsToBorder { if maxSteps == self.stepsToBorder {
break false; break;
} else { } else {
return 0; return 0;
} }
@@ -67,7 +67,7 @@ impl<'a> FastEdgeToEdgeCounter<'a> {
// if !(self.under_arry[idx_pt] // if !(self.under_arry[idx_pt]
// == self.under_arry[self.p as usize]) // == self.under_arry[self.p as usize])
if !(self.under_arry.get_index(idx_pt) == self.under_arry.get_index(self.p as usize)) { if !(self.under_arry.get_index(idx_pt) == self.under_arry.get_index(self.p as usize)) {
break true; break;
} }
} // while (p[steps * stride] == p[0]); } // while (p[steps * stride] == p[0]);

View File

@@ -5,12 +5,12 @@ use crate::{Exceptions, Point};
pub struct Matrix<T: Default + Clone + Copy> { pub struct Matrix<T: Default + Clone + Copy> {
width: usize, width: usize,
height: usize, height: usize,
data: Vec<T>, data: Vec<Option<T>>,
} }
impl<T: Default + Clone + Copy> Matrix<T> { impl<T: Default + Clone + Copy> Matrix<T> {
pub fn with_data(width: usize, height: usize, data: Vec<T>) -> Result<Matrix<T>> { pub fn with_data(width: usize, height: usize, data: Vec<Option<T>>) -> Result<Matrix<T>> {
if (width != 0 && data.len() / width as usize != height as usize) { if width != 0 && data.len() / width as usize != height as usize {
return Err(Exceptions::illegal_argument_with( return Err(Exceptions::illegal_argument_with(
"invalid size: width * height is too big", "invalid size: width * height is too big",
)); ));
@@ -31,7 +31,7 @@ impl<T: Default + Clone + Copy> Matrix<T> {
Ok(Self { Ok(Self {
width, width,
height, height,
data: vec![T::default(); width * height], data: vec![None; width * height],
}) })
} }
@@ -66,13 +66,15 @@ impl<T: Default + Clone + Copy> Matrix<T> {
pub fn get(&self, x: usize, y: usize) -> Option<T> { pub fn get(&self, x: usize, y: usize) -> Option<T> {
if x >= self.width || y >= self.height { if x >= self.width || y >= self.height {
None None
} else { } else if let Some(Some(d)) = self.data.get(Self::get_offset(x, y, self.width)){
Some(self.data[Self::get_offset(x, y, self.width)]) Some(*d)
}else {
None
} }
} }
pub fn set(&mut self, x: usize, y: usize, value: T) -> T { pub fn set(&mut self, x: usize, y: usize, value: T) -> T {
self.data[Self::get_offset(x, y, self.width)] = value; self.data[Self::get_offset(x, y, self.width)] = Some(value);
self.get(x, y).unwrap() self.get(x, y).unwrap()
} }
@@ -84,7 +86,7 @@ impl<T: Default + Clone + Copy> Matrix<T> {
self.set(p.x as usize, p.y as usize, value) self.set(p.x as usize, p.y as usize, value)
} }
pub fn data(&self) -> &[T] { pub fn data(&self) -> &[Option<T>] {
&self.data &self.data
} }
@@ -97,10 +99,10 @@ impl<T: Default + Clone + Copy> Matrix<T> {
// } // }
pub fn clear_with(&mut self, value: T) { pub fn clear_with(&mut self, value: T) {
self.data.fill(value) self.data.fill(Some(value))
} }
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.data.fill(T::default()) self.data.fill(None)
} }
} }

View File

@@ -228,4 +228,9 @@ impl RegressionLine {
new_rl.evaluate(&[point1, point2]); new_rl.evaluate(&[point1, point2]);
new_rl new_rl
} }
pub fn with_point_slice(points: &[Point]) -> Self {
let mut new_rl = RegressionLine::default();
new_rl.evaluate(points);
new_rl
}
} }

View File

@@ -35,11 +35,14 @@ pub fn CorrectErrors(codewordBytes: &mut [u8], numDataCodewords: u32) -> Result<
let rs = ReedSolomonDecoder::new(get_predefined_genericgf( let rs = ReedSolomonDecoder::new(get_predefined_genericgf(
PredefinedGenericGF::QrCodeField256, PredefinedGenericGF::QrCodeField256,
)); ));
if rs.decode(&mut codewordsInts, numECCodewords)? != 0
// if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords)) rs.decode(&mut codewordsInts, numECCodewords)?;
{
return Ok(false); // if rs.decode(&mut codewordsInts, numECCodewords)? != 0
} // // if (!ReedSolomonDecode(GenericGF::QRCodeField256(), codewordsInts, numECCodewords))
// {
// return Ok(false);
// }
// Copy back into array of bytes -- only need to worry about the bytes that were data // Copy back into array of bytes -- only need to worry about the bytes that were data
// We don't care about errors in the error-correction codewords // We don't care about errors in the error-correction codewords
@@ -298,9 +301,9 @@ pub fn DecodeBitStream(
let modeBitLength = Mode::get_codec_mode_bits_length(version); let modeBitLength = Mode::get_codec_mode_bits_length(version);
let res = (|| { let res = (|| {
while (!IsEndOfStream(&mut bits, version)?) { while !IsEndOfStream(&mut bits, version)? {
let mode: Mode; let mode: Mode;
if (modeBitLength == 0) { if modeBitLength == 0 {
mode = Mode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0 mode = Mode::NUMERIC; // MicroQRCode version 1 is always NUMERIC and modeBitLength is 0
} else { } else {
mode = Mode::CodecModeForBits( mode = Mode::CodecModeForBits(
@@ -309,7 +312,7 @@ pub fn DecodeBitStream(
)?; )?;
} }
match (mode) { match mode {
Mode::FNC1_FIRST_POSITION => { Mode::FNC1_FIRST_POSITION => {
// if (!result.empty()) // uncomment to enforce specification // if (!result.empty()) // uncomment to enforce specification
// throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position"); // throw FormatError("GS1 Indicator (FNC1 in first position) at illegal position");
@@ -317,14 +320,14 @@ pub fn DecodeBitStream(
result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as <GS> result.symbology.aiFlag = AIFlag::GS1; // In Alphanumeric mode undouble doubled '%' and treat single '%' as <GS>
} }
Mode::FNC1_SECOND_POSITION => { Mode::FNC1_SECOND_POSITION => {
if (!result.is_empty()) { if !result.is_empty() {
return Err(Exceptions::format_with("AIM Application Indicator (FNC1 in second position) at illegal position")); return Err(Exceptions::format_with("AIM Application Indicator (FNC1 in second position) at illegal position"));
// throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position"); // throw FormatError("AIM Application Indicator (FNC1 in second position) at illegal position");
} }
result.symbology.modifier = b'5'; // As above result.symbology.modifier = b'5'; // As above
// ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z" // ISO/IEC 18004:2015 7.4.8.3 AIM Application Indicator (FNC1 in second position), "00-99" or "A-Za-z"
let appInd = bits.readBits(8)?; let appInd = bits.readBits(8)?;
if (appInd < 100) if appInd < 100
// "00-09" // "00-09"
{ {
result += result +=
@@ -367,7 +370,7 @@ pub fn DecodeBitStream(
// "Normal" QR code modes: // "Normal" QR code modes:
// How many characters will follow, encoded in this mode? // How many characters will follow, encoded in this mode?
let count = bits.readBits(mode.CharacterCountBits(version) as usize)?; let count = bits.readBits(mode.CharacterCountBits(version) as usize)?;
match (mode) { match mode {
Mode::NUMERIC => DecodeNumericSegment(&mut bits, count, &mut result)?, Mode::NUMERIC => DecodeNumericSegment(&mut bits, count, &mut result)?,
Mode::ALPHANUMERIC => { Mode::ALPHANUMERIC => {
DecodeAlphanumericSegment(&mut bits, count, &mut result)? DecodeAlphanumericSegment(&mut bits, count, &mut result)?

View File

@@ -714,8 +714,8 @@ pub fn SampleQR(image: &BitMatrix, fp: &FinderPatternSet) -> Result<QRCodeDetect
let y0 = apM[y]; let y0 = apM[y];
let y1 = apM[y + 1]; let y1 = apM[y + 1];
rois.push(SamplerControl { rois.push(SamplerControl {
p0: point_i(x0 - u32::from(x == 0) * 6, x1 + u32::from(x == N - 1) * 7), p0: point_i(x0 - u32::from(x == 0) * 6, y0 - u32::from(y == 0) * 6),
p1: point_i(y0 - u32::from(y == 0) * 6, y1 + u32::from(y == N - 1) * 7), p1: point_i(x1 + u32::from(x == N - 1) * 7, y1 + u32::from(y == N - 1) * 7),
transform: PerspectiveTransform::quadrilateralToQuadrilateral( transform: PerspectiveTransform::quadrilateralToQuadrilateral(
Quadrilateral::rectangle_from_xy( Quadrilateral::rectangle_from_xy(
x0 as f32, x1 as f32, y0 as f32, y1 as f32, None, x0 as f32, x1 as f32, y0 as f32, y1 as f32, None,

View File

@@ -280,6 +280,13 @@ impl Point {
y: self.y.round(), y: self.y.round(),
} }
} }
pub fn floor(self) -> Self {
Self {
x: self.x.floor(),
y: self.y.floor()
}
}
} }
impl From<&(f32, f32)> for Point { impl From<&(f32, f32)> for Point {