Hardware Software Codesign Assignment
Challa Sai Ranga
21ECB0A10
Section A
Step-1: Convert image into hex
In this step, we convert an image into a hex format to store pixel values in a format
compatible with Verilog memory. This step is necessary because Verilog operates
with text-based memory files rather than direct image formats like PNG or JPEG.
The Python script reads an image, converts it into grayscale, resizes it for
uniformity, and writes pixel values in hex format to image.mem.
Python code to convert image into hex:
from PIL import Image
import numpy as np
# Load Image
img = Image.open("example.jpg").convert("L") # Convert to grayscale
img = img.resize((256, 256)) # Resize for simplicity
img_array = np.array(img)
# Save as memory file
with open("image.mem", "w") as f:
for row in img_array:
for pixel in row:
f.write(f"{pixel:02x}\n") # Convert to hex
print(" image.mem file created!")
Fig-1: example.jpg
Step-2: Verilog code for each step (Loading image to memory,
Applying median filter 3*3, Histogram equalization, testbench)
This step includes the hardware implementation of the image processing pipeline,
consisting of three modules:
1. Image Memory Module (image_memory.v)
• Loads the pixel values stored in image.mem.
• Outputs the corresponding pixel data when given an address.
• Works as a read-only memory for image processing.
2. Median Filter Module (median_filter.v)
• Extracts a 3×3 pixel window and sorts the pixel values.
• Selects the median value as the new pixel value, effectively removing
noise.
3. Histogram Equalization Module (hist_eq.v)
• Computes the histogram of pixel values.
• Generates a cumulative distribution function (CDF) for contrast
adjustment.
• Maps original pixel values to new intensity levels.
4. Testbench (testbench.v)
• Simulates the entire system.
• Reads pixel values from image_memory.v, applies median filtering, and
then performs histogram equalization.
• Saves processed pixel values to output.mem.
image_memory.v (Loads Image into Memory)
module image_memory (
input clk,
input [15:0] addr, // Image address
output reg [7:0] dout // Pixel output
);
reg [7:0] memory [0:65535]; // 256x256 image
initial begin
$readmemh("image.mem", memory); // Load image from file
end
always @(posedge clk) begin
dout <= memory[addr];
end
endmodule
median_filter.v (Applies 3x3 Median Filter)
module median_filter (
input clk,
input [71:0] pixel_data, // Flattened 3x3 pixel window
output reg [7:0] median_pixel
);
reg [7:0] sorted [8:0];
integer i, j;
reg [7:0] temp;
reg [7:0] pixels [8:0]; // Local storage for 3x3 pixels
always @(*) begin
// Extract 3x3 pixels
pixels[0] = pixel_data[7:0];
pixels[1] = pixel_data[15:8];
pixels[2] = pixel_data[23:16];
pixels[3] = pixel_data[31:24];
pixels[4] = pixel_data[39:32];
pixels[5] = pixel_data[47:40];
pixels[6] = pixel_data[55:48];
pixels[7] = pixel_data[63:56];
pixels[8] = pixel_data[71:64];
// Copy to sorted array
for (i = 0; i < 9; i = i + 1)
sorted[i] = pixels[i];
// Bubble sort to find the median
for (i = 0; i < 9; i = i + 1) begin
for (j = 0; j < 8 - i; j = j + 1) begin
if (sorted[j] > sorted[j + 1]) begin
temp = sorted[j];
sorted[j] = sorted[j + 1];
sorted[j + 1] = temp;
end
end
end
median_pixel = sorted[4]; // Middle value
end
endmodule
hist_eq.v (Histogram Equalization)
module hist_eq (
input clk,
input [7:0] pixel_in,
output reg [7:0] pixel_out = 0
);
reg [15:0] hist [0:255];
reg [15:0] cdf [0:255];
integer i;
initial begin
// Initialize histogram and CDF
for (i = 0; i < 256; i = i + 1) begin
hist[i] = 0;
cdf[i] = 0;
end
end
always @(posedge clk) begin
hist[pixel_in] = hist[pixel_in] + 1;
// Compute CDF
cdf[0] = hist[0];
for (i = 1; i < 256; i = i + 1)
cdf[i] = cdf[i-1] + hist[i];
// Apply Histogram Equalization
pixel_out = (cdf[pixel_in] * 255) / (256*256);
end
endmodule
Fig-2: Histogram of original image and equalized image
testbench.v (Simulates Everything)
module testbench;
reg clk;
reg [15:0] addr;
wire [7:0] pixel;
wire [7:0] median_pixel;
wire [7:0] hist_pixel;
integer file;
image_memory mem(.clk(clk), .addr(addr), .dout(pixel));
median_filter med_filter(.clk(clk), .pixel_data({pixel, pixel, pixel, pixel, pixel, pixel, pixel,
pixel, pixel}), .median_pixel(median_pixel));
hist_eq hist(.clk(clk), .pixel_in(median_pixel), .pixel_out(hist_pixel));
initial begin
clk = 0;
addr = 0;
file = $fopen("output.mem", "w"); // Open file to write processed pixels
#5; // Wait for memory to load
repeat (65536) begin // Process all pixels (256x256 = 65536)
#10;
$fdisplay(file, "%02x", hist_pixel); // Ensure 2-digit hex values
addr = addr + 1;
end
$fclose(file); // Close file after writing
$finish;
end
always #5 clk = ~clk; // Clock toggle every 5 time units
endmodule
Step 3: Convert Processed Data Back to Image
After processing in Verilog, the output pixels are saved in output.mem. A Python
script reads this file, converts hex values back into integer pixel values, and
reconstructs the image using the PIL library. The final image, output.jpg,
represents the filtered and histogram-equalized version of the original input.
Python code to convert hex to jpg:
import numpy as np
from PIL import Image
with open("output.mem", "r") as f:
hex_values = f.read().splitlines()
data = np.array([int(x, 16) for x in hex_values], dtype=np.uint8)
image = Image.fromarray(data.reshape((256,256)), "L")
image.save("output.jpg")
Fig-2: output.jpg (After applying median filter)