Permalink
Browse files

o Prepare cone-mirror code.

  • Loading branch information...
hzeller committed Sep 17, 2017
1 parent cc71454 commit ea57624c54bb77b1d73e07417f172cb9d2bfaa68
Showing with 103 additions and 14 deletions.
  1. +101 −13 src/ldgraphy-scanner.cc
  2. +2 −1 src/main.cc
View
@@ -32,6 +32,9 @@
# define LDGRAPHY_DEBUG_OUTPUTS 0
#endif
// Experimental.
#define LDGRAPHY_CONE_MIRROR 0
// Output images to TMP to observe the image processing progress.
constexpr bool debug_images = false;
@@ -88,10 +91,18 @@ constexpr float bed_length = 162.0; // Sled length.
// Width of the laser to throw. Comes from the case calculation.
// Fudge value from real life :)
#if !LDGRAPHY_CONE_MIRROR
// straight mirror
constexpr float bed_width_fudge_value = -1.88; // Measured :)
constexpr float bed_width = 102.0 + bed_width_fudge_value;
constexpr float kScanAngleRad = 40.0 * deg2rad;
constexpr float kRadiusMM = (bed_width/2) / tan(kScanAngleRad / 2);
#else
// Cone mirror. Experimental.
constexpr float kScanAngleRad = 90.0f * deg2rad;
constexpr float kRadiusMM = 53.0f;
constexpr float bed_width = 2 * sin(kScanAngleRad/2) * kRadiusMM;
#endif
LDGraphyScanner::LDGraphyScanner(float exposure_factor)
: exposure_factor_(roundf(exposure_factor)), // We only do integer for now.
@@ -111,6 +122,9 @@ LDGraphyScanner::LDGraphyScanner(float exposure_factor)
kSegmentAngleRad / deg2rad, kMirrorThrowAngleRad / deg2rad,
kScanAngleRad/deg2rad, 100 * kScanAngleRad / kSegmentAngleRad);
#endif
// Need to have enough data fraction to cover our angle. More mechanically
// used than our data fraction allows. See above output.
assert(kSegmentAngleRad >= kScanAngleRad);
}
LDGraphyScanner::~LDGraphyScanner() {
@@ -131,11 +145,58 @@ void LDGraphyScanner::SetLaserDotSize(float sled_dot_size_mm,
// Radius is given in pixels; output is a vector given a data position shows
// where to look up the pixel in the original image. Maximum of 'num' values, but
// will return less as we only cover a smaller segment.
static std::vector<int> PrepareTangensLookup(float radius_pixels,
float scan_range_pixels,
size_t num) {
#if LDGRAPHY_CONE_MIRROR
static std::vector<int> PrepareYLookup(float radius_pixels, float angle_rad,
float scan_range_pixels,
size_t num) {
std::vector<int> result;
const float scan_angle_range = 2 * atan(scan_range_pixels/2 / radius_pixels);
const float scan_angle_range = angle_rad;
const float scan_angle_start = -scan_angle_range/2;
const float angle_step = kSegmentAngleRad / num; // Overall arc mapped to full
const float scan_center = scan_range_pixels / 2;
// only the values between -angle_range/2 .. angle_range/2
for (size_t i = 0; i < num; ++i) {
const float y_pixel = roundf(sin(scan_angle_start + i * angle_step)
* radius_pixels + scan_center);
assert(y_pixel >= 0); // Otherwise radius/size is wrongly calculated.
if (y_pixel > scan_range_pixels)
break;
result.push_back(y_pixel);
}
#if LDGRAPHY_DEBUG_OUTPUTS
fprintf(stderr, "Last Y coordinate full width = %d; "
"mapped to %d + %d shoulder = %d\n",
result.back(), (int)result.size(),
kHSyncShoulder, (int)result.size() + kHSyncShoulder);
#endif
return result;
}
// Given data position, what is the x-offset there. For a arc-cone mirror, this
// is obviously circular shaped, but there can be additional factors such
// inaccuracies in mirrors that we can calibrate here.
static std::vector<int> PrepareXOffset(float radius_pixels, float angle_rad,
size_t num, int *max_offset) {
std::vector<int> result;
const float scan_angle_range = angle_rad;
const float scan_angle_start = -scan_angle_range/2;
const float angle_step = kSegmentAngleRad / num;
// only the values between -angle_range/2 .. angle_range/2
for (size_t i = 0; i < num; ++i) {
const float a = scan_angle_start + i * angle_step;
const float x_pixel = roundf(cos(a) * radius_pixels);
if (a > angle_rad/2) break;
result.push_back(roundf(radius_pixels - x_pixel));
}
*max_offset = result[0];
return result;
}
#else
static std::vector<int> PrepareYLookup(float radius_pixels, float angle_rad,
float scan_range_pixels,
size_t num) {
std::vector<int> result;
const float scan_angle_range = angle_rad;
const float scan_angle_start = -scan_angle_range/2;
const float angle_step = kSegmentAngleRad / num; // Overall arc mapped to full
const float scan_center = scan_range_pixels / 2;
@@ -145,7 +206,7 @@ static std::vector<int> PrepareTangensLookup(float radius_pixels,
* radius_pixels + scan_center);
if (y_pixel > scan_range_pixels)
break;
result.push_back(roundf(y_pixel));
result.push_back(y_pixel);
}
#if LDGRAPHY_DEBUG_OUTPUTS
fprintf(stderr, "Last Y coordinate full width = %d; "
@@ -156,14 +217,30 @@ static std::vector<int> PrepareTangensLookup(float radius_pixels,
return result;
}
// Given data position, what is the x-offset there. For a arc-cone mirror, this
// is obviously circular shaped, but there can be additional factors such
// inaccuracies in mirrors that we can calibrate here.
static std::vector<int> PrepareXOffset(float, float,
size_t num, int *max_offset) {
// With a straight mirror, we never offset in X axis.
std::vector<int> result(num, 0);
*max_offset = 0;
return result;
}
#endif
uint8_t flip_bits(uint8_t b) {
b = (b & 0xF0) >> 4 | (b & 0x0F) << 4;
b = (b & 0xCC) >> 2 | (b & 0x33) << 2;
b = (b & 0xAA) >> 1 | (b & 0x55) << 1;
return b;
}
// Copy and mirror line. Essentially memcpy() but backwards and bits reversed
static void mirror_copy(uint8_t *to, const uint8_t *const src, size_t n) {
static void mirror_copy(uint8_t *to, size_t offset,
const uint8_t *const src, size_t n) {
// TODO: we should allow bit-wise offset. For now, we're a bit more
// coarse-grained.
to += offset / 8;
const uint8_t *from = src + n - 1;
while (from >= src)
*to++ = flip_bits(*from--);
@@ -202,13 +279,20 @@ bool LDGraphyScanner::SetImage(BitmapImage *img,
sled_step_per_image_pixel_ = (image_resolution_mm_per_pixel
/ SledControl::kSledMMperStep);
scanlines_ = img->width() * sled_step_per_image_pixel_;
// Create lookup-table: data pixel position to actual position in image.
// This is dependend on the resolution of the incoming image.
std::vector<int> y_lookup
= PrepareTangensLookup(kRadiusMM / image_resolution_mm_per_pixel,
bed_width / image_resolution_mm_per_pixel,
SCAN_PIXELS);
= PrepareYLookup(kRadiusMM / image_resolution_mm_per_pixel,
kScanAngleRad,
bed_width / image_resolution_mm_per_pixel,
SCAN_PIXELS);
int max_offset;
std::vector<int> x_offset
= PrepareXOffset(kRadiusMM / image_resolution_mm_per_pixel,
kScanAngleRad,
SCAN_PIXELS, &max_offset);
#if 1
// Due to the tangens, we have worse resolution at the edges. So look at the
// beginning to report a worst-case resolution.
@@ -241,19 +325,23 @@ bool LDGraphyScanner::SetImage(BitmapImage *img,
// Convert this into the image, tangens-corrected and rotated by
// 90 degrees, so that we can send it line-by-line.
fprintf(stderr, " Geometry preprocess...\n");
scan_image_.reset(new BitmapImage(img->width(), SCAN_PIXELS));
scan_image_.reset(new BitmapImage(img->width() + max_offset, SCAN_PIXELS));
scanlines_ = scan_image_->width() * sled_step_per_image_pixel_;
fprintf(stderr, " Geometry preprocess to output image %dx%d\n",
scan_image_->width(), scan_image_->height());
for (size_t i = 0; i < y_lookup.size(); ++i) {
const int from_y_pixel = img->height() - 1 - y_lookup[i];
if (from_y_pixel < 0) break; // done.
const int to_y_pixel = i + kHSyncShoulder;
// TODO: the x offset should actually happen after thinning
mirror_copy(scan_image_->GetMutableRow(to_y_pixel),
x_offset[i],
img->GetRow(from_y_pixel), img->width() / 8);
}
delete img;
scan_image_.reset(CreateRotatedImage(*scan_image_));
if (debug_images) scan_image_->ToPBM(fopen("/tmp/ld_1_tangens.pbm", "w"));
if (debug_images) scan_image_->ToPBM(fopen("/tmp/ld_1_geometry.pbm", "w"));
const float laser_resolution_in_mm_per_pixel = bed_width / y_lookup.size();
fprintf(stderr, " Thinning structures for (%.2f, %.2f) laser dot size...\n",
laser_sled_dot_size_, laser_scan_dot_size_);
View
@@ -39,6 +39,7 @@
#include "sled-control.h"
constexpr float kThinningChartResolution = 0.005; // mm per pixel
constexpr float kInitialSledOffsetMM = 3; // sled skip initial markings.
// Interrupt handling. Provide a is_interrupted() function that reports
// if Ctrl-C has been pressed. Requires ArmInterruptHandler() called before use.
@@ -270,7 +271,7 @@ int main(int argc, char *argv[]) {
sled.Move(-180); // Back to base.
float forward_move = 3; // Forward until we reach begin.
float forward_move = kInitialSledOffsetMM; // Forward until we reach begin.
if (mirror_adjust_exposure) forward_move += 5;
forward_move += offset_x;
sled.Move(forward_move);

0 comments on commit ea57624

Please sign in to comment.